Perfecting NixGL Wrappers for Home-Manager Use
RicSanOP opened this issue · 4 comments
Hello Everyone,
I am new to using nix and home-manager and have been enjoying tinkering around with functional package management. I have reached the point where I can more or less read nix code (like the wrappers in #114). I have gone through the process of setting up home-manager on a non-nixOS distro (Pop_OS! in my case) and have rewritten @ntsanov wrapper code from #114 into the following functions
{ pkgs }:
{
# This nix file contains helper functions for wrapping other
# graphical nix packages with nixGL calls depending on the
# graphics library and graphics chip of choice
nixIntelWrap = glib: pkg:
let
gpkg = "nix${glib}Intel";
bins = "${pkg}/bin";
in
pkgs.buildEnv {
name = "${gpkg}-${pkg.name}";
paths =
[ pkg ] ++
(map
(bin: pkgs.hiPrio (
pkgs.writeShellScriptBin bin ''
exec "${pkgs.nixgl.${gpkg}}/bin/${gpkg}" "${bins}/${bin}" "$@"
''
))
(builtins.attrNames (builtins.readDir bins))
);
};
nixNvidiaWrap = glib: ndv: pkg:
let
gpkg = "nix${glib}Nvidia";
gcmd = "${gpkg}-${ndv}";
bins = "${pkg}/bin";
in
pkgs.buildEnv {
name = "${gpkg}-${pkg.name}";
paths =
[ pkg ] ++
(map
(bin: pkgs.hiPrio (
pkgs.writeShellScriptBin bin ''
export __NV_PRIME_RENDER_OFFLOAD=1
export __GLX_VENDOR_LIBRARY_NAME=nvidia
exec "${pkgs.nixgl.auto.${gpkg}}/bin/${gcmd}" "${bins}/${bin}" "$@"
''
))
(builtins.attrNames (builtins.readDir bins))
);
};
}
Here are some successful usages of these wrapper functions in my home.nix
file:
home.packages = [
# # Adds the 'hello' command to your environment. It prints a friendly
# # "Hello, world!" when run.
# pkgs.hello
# # It is sometimes useful to fine-tune packages, for example, by applying
# # overrides. You can do that directly here, just don't forget the
# # parentheses. Maybe you want to install Nerd Fonts with a limited number of
# # fonts?
# (pkgs.nerdfonts.override { fonts = [ "FantasqueSansMono" ]; })
# # You can also create simple shell scripts directly inside your
# # configuration. For example, this adds a command 'my-hello' to your
# # environment:
# (pkgs.writeShellScriptBin "my-hello" ''
# echo "Hello, ${config.home.username}!"
# '')
# nixGL packages for running OpenGL and Vulkan nix packages
# using either an Nvidia driver or Intel driver (Nvidia is default)
pkgs.nixgl.auto.nixGLNvidia
pkgs.nixgl.auto.nixVulkanNvidia
pkgs.nixgl.nixGLIntel
pkgs.nixgl.nixVulkanIntel
# terminal-shell-editor environment
#(nixgl.nixNvidiaWrap "GL" nvidiaDriverVersion pkgs.wezterm)
#(nixgl.nixNvidiaWrap "GL" nvidiaDriverVersion pkgs.alacritty)
pkgs.zsh
pkgs.neovim
# setup podman for container workloads
pkgs.podman
pkgs.docker-compose
pkgs.podman-compose
# productivity and workflow packages
(nixgl.nixNvidiaWrap "GL" nvidiaDriverVersion pkgs.vivaldi)
(nixgl.nixNvidiaWrap "GL" nvidiaDriverVersion pkgs.obsidian)
];
programs.wezterm = {
enable = true;
package = nixgl.nixNvidiaWrap "GL" nvidiaDriverVersion pkgs.wezterm;
};
programs.alacritty = {
enable = true;
package = nixgl.nixNvidiaWrap "GL" nvidiaDriverVersion pkgs.alacritty;
settings = {
};
};
Now as you may have noticed, the nixgl wrap around pkgs.alacritty
and pkgs.wezterm
have been commented out from home.packages
as they are required in their respective programs.__TERMINAL__.package
attributes. Providing the nixgl wrapped derivation has worked fine for wezterm
, but unfortunately hasn't worked for alacritty
due to the following error:
error:
… while evaluating a branch condition
at /nix/store/adkf0aw7d4yfxspb1h23zkhxlskidfpi-source/lib/lists.nix:56:9:
55| fold' = n:
56| if n == len
| ^
57| then nul
… while calling the 'length' builtin
at /nix/store/adkf0aw7d4yfxspb1h23zkhxlskidfpi-source/lib/lists.nix:54:13:
53| let
54| len = length list;
| ^
55| fold' = n:
(stack trace truncated; use '--show-trace' to show the full trace)
error: attribute 'version' missing
at /nix/store/ddnh5dcx1z38990c7h3fzzcy1vq7g9la-source/modules/programs/alacritty.nix:7:32:
6| cfg = config.programs.alacritty;
7| useToml = lib.versionAtLeast cfg.package.version "0.13";
| ^
8| tomlFormat = pkgs.formats.toml { };
It is clear that the wrapper function I use somehow loses the version
attribute which is set in nixpkgs at https://github.com/NixOS/nixpkgs/blob/master/pkgs/applications/terminal-emulators/alacritty/default.nix and referenced in home-manager at https://github.com/nix-community/home-manager/blob/master/modules/programs/alacritty.nix as can be seen in the error line useToml = lib.versionAtLeast cfg.package.version "0.13";
. I came to understand this after playing around with the builtins.trace
function and seeing that the wrapped output did not have a version
parameter.
Even though I understand the issue, I am very much at a loss on how to re-add the version
attribute to the nixgl wrapped derivation. Even if a hacky, I would deeply appreciate any help on navigating this issue and getting the nixgl wrapped version of alacritty
to work with home-manager. Thank you to any and all in advance.
Hey @RicSanOP, my wrapper handles this by returning the original package modified via overrideAttrs. This way, only the necessary attributes are overridden, and any other attributes required by home manager are preserved.
The problem with your wrapper is that buildEnv
does not inherit any of the attributes from the wrapped derivation. I'm not very familiar with buildEnv
, but you could try using overrideAttrs
on it if it supports overriding, or explore merging in attributes from the original derivation with the attrset merge operator (//
).
Hey @Smona ,
Thank you for your kind and prompt response.
I had taken a look at your wrapper previously. What had initially caused me to hesitate from using it was that the wrapped package is named differently from the original (with the nixGL- prefix). But I guess that was just a matter of not overriding the old name. I will also admit that the setup was a bit more daunting. Does your wrapper also handle .desktop files well? I am sure you see my lack of experience on this matter, so any points of comparison between your and @ntsanov wrapper on the same thread would be appreciated.
The merge operator //
was exactly what I needed. I set my programs.alacritty.package
to package = pkgs.alacritty // (nixgl.nixNvidiaWrap "GL" nvidiaDriverVersion pkgs.alacritty);
which in essence only modifies elements that the wrapped version of the package changes and allows the home-manager module to run smoothly. I imagine this technique would work well for other packages out there with their own home-manager modules. Thank you for the tip @Smona
thanks for that feedback!
the nixGL-
name prefix only really affects the nix store path in this case, and I thought it could be helpful if inspecting the store to see the wrapper packages differentiated from the original ones, rather than just having the same name and a different hash. the output binaries still have the original names.
.desktop files appear to work perfectly with my wrapper, because all files from the original package are symlinked into the wrapper package so they exist where they need to once installed. The setup may be a little more complicated, but you can skip defining the nixgl prefix setting if you only need to use one GPU variant. I designed it that way so that the config module which includes nixgl-wrapped packages can be shared between multiple system definitions, with the nixGL variant passed in via the per-system top-level config. This also prevents unnecessary nixGL variants from being installed on each system like your example code does. you can see a full example of integrating the wrapper this way in my config repo.
As far as buildEnv vs overriding the package, IIRC the main downside with buildEnv was that supporting files (like .desktop or files in /share) were not linked into the user profile if the returned value from the wrapper wasn't a derivation, so app installations would be a bit broken.
For cross reference, there is also nix-community/home-manager#3968
May we continue there, and close this issue?