Open thanhnguyen2187 opened 1 year ago
# TODO: find a way to make the config more declarative
flakes is what you are searching for.
package = pkgs.writeShellScriptBin "alacritty" '' #!/bin/sh ${nixGL.auto.nixGLNvidia}/bin/nixGLNvidia ${pkgs.alacritty}/bin/alacritty "$@" '';
I don't think you can do this. I would try to add this to home.package instead and hope it gets in $PATH before alacrity. If that does not work I would try to make the wrapper you created a full package, without writeShellScript*.
Thanks a lot for the comment @SuperSandro2000. Forgot to mention that I did add nixGL.auto.nixGLNvidia
to my home.packages
, but the error was the same. Do you have any other idea?
For some inspiration, I wrapped my google-chrome with nixGL like this: https://github.com/Gerschtli/nix-config/commit/ab51b30fae25b72482c97f7b6d94bbef82e38f40
Maybe there could be a library function exported by this flake to get the needed lines directly without reading the package. @guibou What do you think? If we can agree on an API, I could create a PR :)
https://github.com/dali99/nix-dotfiles/blob/9639108c536fd7f1ab4d5606d5f5ee1b51bd0a4c/profiles/xsession/default.nix#L121 I have this for my non-nixos machines.
This successfully starts my window manager and lets me run gui programs without wrapping those induvidually
If anyone lands here also see my answer in Issue #114. You can wrap packages with nixGL
such that they will start without any issues.
This way you don't necessarily need to install another window manager. My use case is to use Home Manager as a replacement for brew
.
@thanhnguyen2187 Here is my wrapper example musing both GL and Vulkan drivers
nixGLVulkanMesaWrap = pkg:
pkgs.runCommand "${pkg.name}-nixgl-wrapper" { } ''
mkdir $out
ln -s ${pkg}/* $out
rm $out/bin
mkdir $out/bin
for bin in ${pkg}/bin/*; do
wrapped_bin=$out/bin/$(basename $bin)
echo "${lib.getExe pkgs.nixgl.nixGLIntel} ${
lib.getExe pkgs.nixgl.nixVulkanIntel
} $bin \$@" > $wrapped_bin
chmod +x $wrapped_bin
done
'';
here you can see what I have done so you can easly set it up rengare/dotfiles
I have been following a similar approach, but combining pkgs.symlinkJoin
with pkgs.makeWrapper
. Unfortunately, this fails for firefox in home manager, since home manager tries to override the package :cry:.
error: attribute 'override' missing
at /nix/store/xadqf1cap0pvxsx2yyyk57wm13fyir6x-source/modules/programs/firefox.nix:502:9:
501| else if versionAtLeast config.home.stateVersion "19.09" then
502| cfg.package.override (old: { cfg = old.cfg or { } // fcfg; })
| ^
503| else
Okay! After lots of hacking around I think I've found a way to solve this problem in the general case.
In one of my home-manager modules, I define this option, which i set per-machine to something like ${nixGL.packages.x86_64-linux.nixGLIntel}/bin/nixGLIntel
:
{
options.nixGLPrefix = lib.mkOption {
type = lib.types.str;
default = "";
description = ''
Will be prepended to commands which require working OpenGL.
This needs to be set to the right nixGL package on non-NixOS systems.
'';
};
}
Then, I use this function to wrap any packages that I want to be run in the nixGL context:
# Call once on import to load global context
{ pkgs, config }:
# Wrap a single package
pkg:
if config.nixGLPrefix == "" then
pkg
else
# Wrap the package's binaries with nixGL, while preserving the rest of
# the outputs and derivation attributes.
(pkg.overrideAttrs (old: {
name = "nixGL-${pkg.name}";
buildCommand = ''
set -eo pipefail
${
# Heavily inspired by https://stackoverflow.com/a/68523368/6259505
pkgs.lib.concatStringsSep "\n" (map (outputName: ''
echo "Copying output ${outputName}"
set -x
cp -rs --no-preserve=mode "${pkg.${outputName}}" "''$${outputName}"
set +x
'') (old.outputs or [ "out" ]))}
rm -rf $out/bin/*
shopt -s nullglob # Prevent loop from running if no files
for file in ${pkg.out}/bin/*; do
echo "#!${pkgs.bash}/bin/bash" > "$out/bin/$(basename $file)"
echo "exec -a \"\$0\" ${config.nixGLPrefix} $file \"\$@\"" >> "$out/bin/$(basename $file)"
chmod +x "$out/bin/$(basename $file)"
done
shopt -u nullglob # Revert nullglob back to its normal default state
'';
}))
And here is an example of how I would use it:
let nixGL = import ./nixGL.nix { inherit pkgs config; };
in {
home.packages = with pkgs;
[
(nixGL keybase-gui)
(nixGL spotify)
(nixGL vlc)
# ...
];
programs.chromium = {
enable = true;
package = (nixGL pkgs.chromium);
};
}
This has worked for me for every GUI app I install with nix on Ubuntu 22. I'm using it for firefox, kitty, and the emacs-community-overlay with doom emacs, all of which are passed into their respective home manager modules and decorated with additional options after they're wrapped.
Basically, it wraps the passed derivation, preserving every attribute except the build command, which symlinks all the derivation output files to $out
, and overrides solely the binary outputs to be wrapper scripts which pass the original binary file into the configured nixGL
prefix.
I think with a little more work, this function could be modified to directly take a nixGLPrefix
option, rather than config
, and could be packaged with nixGL for user consumption.
Pros:
Cons / TODO:
Do you mind explaning this:
{
options.nixGLPrefix = lib.mkOption {
type = lib.types.str;
default = "";
description = ''
Will be prepended to commands which require working OpenGL.
This needs to be set to the right nixGL package on non-NixOS systems.
'';
};
}
Where this should go ?
I'm using flakes so i can handle multiple users/machines. Does it go into the modules array for each homeManagerConfiguration ?
Edit: I've managed to make it work without this option by using nixGLDefault in the nixGL wrapper as below:
# Call once on import to load global context
{ pkgs, config }:
# Wrap a single package
pkg:
#if config.nixGLPrefix == "" then
# pkg
#else
# Wrap the package's binaries with nixGL, while preserving the rest of
# the outputs and derivation attributes.
(pkg.overrideAttrs (old: {
name = "nixGL-${pkg.name}";
buildCommand = ''
set -eo pipefail
${
# Heavily inspired by https://stackoverflow.com/a/68523368/6259505
pkgs.lib.concatStringsSep "\n" (map (outputName: ''
echo "Copying output ${outputName}"
set -x
cp -rs --no-preserve=mode "${pkg.${outputName}}" "''$${outputName}"
set +x
'') (old.outputs or [ "out" ]))}
rm -rf $out/bin/*
shopt -s nullglob # Prevent loop from running if no files
for file in ${pkg.out}/bin/*; do
echo "#!${pkgs.bash}/bin/bash" > "$out/bin/$(basename $file)"
echo "exec -a \"\$0\" ${pkgs.nixgl.auto.nixGLDefault}/bin/nixGL $file \"\$@\"" >> "$out/bin/$(basename $file)"
chmod +x "$out/bin/$(basename $file)"
done
shopt -u nullglob # Revert nullglob back to its normal default state
'';
}))
@lamarios I would recommend reading the docs about writing home manager modules, or more specifically NixOS modules. You can either add the option to your home.nix
file and wrap the rest of your config in a config
attribute, or better yet create a new file (module), and import it into your home.nix:
# options.nix
{ lib, ... }:
{
options.nixGLPrefix = lib.mkOption {
type = lib.types.str;
default = "";
description = ''
Will be prepended to commands which require working OpenGL.
This needs to be set to the right nixGL package on non-NixOS systems.
'';
};
}
# home.nix
{
imports = [ ./options.nix ];
# ...
}
@lamarios take a look at my dotfiles https://github.com/rengare/dotfiles/tree/main/nix
Thanks both, managed to get it working. I have a bit of a hard time with how things come together in nix. I didn't know that just importing the options.nix file would actually make the option available just like that.
So, how is the home-manager support going to look like? For example, aagl-on-nix and nix-gaming both works on a simple import, then the modules and pkgs are available to access with a prefix, and that's honestly a lot easier than learning how flakes works (I still don't really get it -- anyone have an article with good examples?).
Here's a simple version to wrap all of the executables in an underlying package that works quite nicely, inspired by @Smona's work.
let
nixGL = import <nixgl> { };
nixGLWrap = pkg:
let
bin = "${pkg}/bin";
executables = builtins.attrNames (builtins.readDir bin);
in
pkgs.buildEnv {
name = "nixGL-${pkg.name}";
paths = map
(name: pkgs.writeShellScriptBin name ''
exec -a "$0" ${nixGL.auto.nixGLDefault}/bin/nixGL ${bin}/${name} "$@"
'')
executables;
};
in
programs.kitty.package = nixGLWrap pkgs.kitty;
A small downside to this approach is that the composed package does not contain all of the contents of the original package, so if you need to refer to other files from the original package, you'll need to use the unwrapped version.
Here's an improvement to the above that uses pkgs.buildEnv
+ pkgs.hiPrio
to construct a new package that behaves like the underlying package.
{ pkgs }:
pkg:
let
nixGL = import <nixgl> { };
bins = "${pkg}/bin";
in
pkgs.buildEnv {
name = "nixGL-${pkg.name}";
paths =
[ pkg ] ++
(map
(bin: pkgs.hiPrio (
pkgs.writeShellScriptBin bin ''
exec -a "$0" "${nixGL.auto.nixGLDefault}/bin/nixGL" "${bins}/${bin}" "$@"
''
))
(builtins.attrNames (builtins.readDir bins)));
}
Here's an improvement to the above that uses
pkgs.buildEnv
+pkgs.hiPrio
to construct a new package that behaves like the underlying package.{ pkgs }: pkg: let nixGL = import <nixgl> { }; bins = "${pkg}/bin"; in pkgs.buildEnv { name = "nixGL-${pkg.name}"; paths = [ pkg ] ++ (map (bin: pkgs.hiPrio ( pkgs.writeShellScriptBin bin '' exec -a "$0" "${nixGL.auto.nixGLDefault}/bin/nixGL" "${bins}/${bin}" "$@" '' )) (builtins.attrNames (builtins.readDir bins))); }
This works great, unfortunately I now understand what was meant by "does not contain all of the contents of the original package". It seems the .desktop files are no longer installed with the package making launchers like wofi not able to pick them up.
unfortunately I now understand what was meant by "does not contain all of the contents of the original package". It seems the .desktop files are no longer installed with the package making launchers like wofi not able to pick them up.
It should work if you add both the original package and the wrapper env to home.packages
-- because the wrapped binaries are hiPrio
, they'll be the ones linked into the profile bin
directory instead of whatever they wrap. Perhaps it's better to make the whole wrapper env hiPrio
instead of each individual wrapper, not sure.
I have been using a bunch of wrappers for a while now, and I've been quite happy with how they work. But I haven't had time to clean up this stuff and create a HM module that others could easily use. And I won't have time for months yet. So I'll just leave this stuff here, maybe someone will pick it up. Perhaps combining it with the hiPrio stuff, which I didn't know about.
My goals:
--impure
To use this, nixGL has to be an input of your HM flake, and you need to copy its default package to your flake outputs. Like so:
outputs.gpuWrappers = self.inputs.nixgl.defaultPackage;
Then, the config file quoted below will add the function for wrapping packages to pkgs.lib.gpuWrapPackage
, and include a package with scripts called nixgl
and nvidia-offload
. The latter simply injects environment variables needed to run things on the discrete GPU on an Optimus laptop. The nixgl
script will check if Nvidia-specific environment is present, and run the given command with either the Intel or Nvidia wrapper.
You can use the wrapper like so:
home.packages = with pkgs; [
(lib.gpuWrapCheck targetpackage)
];
This "check" wrapper will only wrap the package if you have enabled the genericLinux
HM option. I need this because I use the same config on both Nixos and non-Nixos systems.
Another pecularity is that the nixgl
script will cache the nixGL build by creating a symlink in ~/.config/nixgl/result
. This symlink is removed when HM config is switched, so expect the next run of the wrapper to take a bit longer.
Hopefully, someone will find this useful 😄
{ config, lib, pkgs, nixpkgs, self, ... }:
{
targets.genericLinux.enable = true;
nixpkgs.overlays = [
(final: prev: {
lib = (prev.lib or {}) // {
gpuWrapPackage = pkg: pkgs.runCommand "${pkg.name}-nixgl-pkg-wrapper" {} ''
# Create a new package that wraps the binaries with nixGL
mkdir $out
ln -s ${pkg}/* $out
rm $out/bin
mkdir $out/bin
for bin in ${pkg}/bin/*
do
wrapped_bin=$out/bin/$(basename $bin)
echo "#!/bin/sh" > $wrapped_bin
echo "exec nixgl $bin \"\$@\"" >> $wrapped_bin
chmod +x $wrapped_bin
done
# If .desktop files refer to the old derivation, replace the references
if [ -d "${pkg}/share/applications" ] && grep "${pkg}" ${pkg}/share/applications/*.desktop > /dev/null
then
rm $out/share
mkdir -p $out/share
cd $out/share
ln -s ${pkg}/share/* ./
rm applications
mkdir applications
cd applications
cp -a ${pkg}/share/applications/* ./
for dsk in *.desktop
do
sed -i "s|${pkg}|$out|g" "$dsk"
done
fi
'';
};
gpuWrapCheck = pkg:
if config.targets.genericLinux.enable
then final.lib.gpuWrapPackage pkg # Comes from non-nixos.nix
else pkg;
gpu-wrappers = let
system = prev.system;
nixglPkgs = "${self}#gpuWrappers.${system}";
wrapIntel = type: lib.getExe self.inputs.nixgl.packages.${system}."nix${type}Intel";
wrapNvidia = type: ''
nix shell --quiet --impure ${nixglPkgs}.nix${type}Nvidia -c nix${type}Nvidia-$()
'';
in pkgs.runCommand "gpu-wrappers" {} ''
bin=$out/bin
mkdir -p $bin
cat > $bin/nixgl-intel <<EOF
#!/bin/sh
exec ${wrapIntel "GL"} ${wrapIntel "Vulkan"} "\$@"
EOF
chmod +x $bin/nixgl-intel
cat > $bin/nixgl-nvidia <<EOF
#!/bin/sh
glbin=\$(nix eval --quiet --raw --impure "${nixglPkgs}.nixGLNvidia.meta.name")
vkbin=\$(echo \$glbin | sed s/GL/Vulkan/)
exec nix shell --quiet --impure ${nixglPkgs}.nixGLNvidia ${nixglPkgs}.nixVulkanNvidia -c \$glbin \$vkbin "\$@"
EOF
chmod +x $bin/nixgl-nvidia
cat > $bin/nvidia-offload <<EOF
#!/bin/sh
export __NV_PRIME_RENDER_OFFLOAD=1
export __NV_PRIME_RENDER_OFFLOAD_PROVIDER=NVIDIA-G0
export __GLX_VENDOR_LIBRARY_NAME=nvidia
export __VK_LAYER_NV_optimus=NVIDIA_only
exec "\$@"
EOF
chmod +x $bin/nvidia-offload
cat > $bin/nixgl <<EOF
#!/bin/sh
if [ ! -h "${config.xdg.cacheHome}/nixgl/result" ]
then
mkdir -p "${config.xdg.cacheHome}/nixgl"
nix build --quiet --impure \
--out-link "${config.xdg.cacheHome}/nixgl/result" \
${nixglPkgs}.nixGLNvidia \
${nixglPkgs}.nixVulkanNvidia
fi
if [ "\$__NV_PRIME_RENDER_OFFLOAD" = "1" ]
then
nixgl-nvidia "\$@"
else
nixgl-intel "\$@"
fi
EOF
chmod +x $bin/nixgl
'';
})
];
home.packages = [ pkgs.gpu-wrappers ];
home.activation = {
clearNixglCacle = lib.hm.dag.entryAfter [ "writeBoundary" ] ''
[ -v DRY_RUN ] || rm -f ${config.xdg.cacheHome}/nixgl/result*
'';
};
}
Hey everyone,
First of all, thanks a lot for providing these very useful wrapper functions and explanations. It has been key to helping me get home-manager working with graphical apps and giving me some deeper understanding as to how nix works in general. If any of you have a minute, kindly check out my new issue #163 which is trying to deal with a particular edge case when installing a nixgl wrapped version of alacritty.
In addition, if anyone has any advice on how to purify the installation and wrapping of packages using Nvidia NixGL then I am all ears.
@RicSanOP, the wrappers I posted achieve purity by not integrating the nixGL Nvidia wrappers into the home-manager configuration, but by calling nix run
on the nixGL flake, like you would do manually on command line. This way, the home-manager configuration remains pure.
@exzombie I am trying to run your wrappers. I have pretty much copy pasted your above code into my home.nix
file. The input parameters to the home.nix
are { config, lib, pkgs, nixpkgs, self, ... }:
.
Unfortunately, I am getting this error when trying to run home-manager switch
error: attribute 'gpuWrapCheck' missing
at /nix/store/0m54m27cnj2pzpsjhix92hn1k5l4knsv-source/home.nix:149:6:
148| home.packages = with pkgs; [
149| (lib.gpuWrapCheck brave)
| ^
150| ];
My current guess is that I am not setting things up correctly within the flake.nix
. Could you please take a quick look at my short hacky flake, or perhaps link me to a full working example that you use. Thank you to you or anyone else who knows what is going on.
{
description = "Home Manager configuration of ricsan";
inputs = {
# Specify the source of Home Manager and Nixpkgs.
nixpkgs.url = "github:nixos/nixpkgs/nixpkgs-unstable";
home-manager = {
url = "github:nix-community/home-manager";
inputs.nixpkgs.follows = "nixpkgs";
};
nixgl.url = "github:nix-community/nixGL";
};
outputs = { self, nixpkgs, home-manager, nixgl, ... }:
let
pkgs = import nixpkgs {
system = "x86_64-linux";
overlays = [ nixgl.overlay ];
};
in {
gpuWrappers = self.inputs.nixgl.defaultPackage;
homeConfigurations."ricsan" = home-manager.lib.homeManagerConfiguration {
inherit pkgs ;
# Specify your home configuration modules here, for example,
# the path to your home.nix.
modules = [ ./home.nix ];
# Optionally use extraSpecialArgs
# to pass through arguments to home.nix
};
};
}
@RicSanOP, there's lib
and there's pkgs.lib
; gpuWrapCheck
is in the latter. You are using with pkgs;
when defining home.packages
, and I'm not sure what the precedence for resolution is. Is it possible that lib
there does not refer to pkgs.lib
?
@exzombie I simply copy pasted from what you had above just to get it working. Right now my home.nix
word for word copies what you have above. I have these inputs to the file
{ config, lib, pkgs, nixpkgs, self, ... }:
and this is now the current home.packages
that I am using
home.packages = [
pkgs.gpu-wrappers
(lib.gpuWrapCheck pkgs.brave)
];
the rest is a copy paste of the large wrapper blob you provided in your initial comment. I still get the same error regarding the missing gpuWrapCheck
attribute. I have even tried changing the line to (pkgs.lib.gpuWrapCheck pkgs.brave)
and (nixpkgs.lib.gpuWrapCheck pkgs.brave)
and gotten the same problem. I feel the issue is with the parameters I am sending in from the flake to home-manager (please see the flake.nix
posted above.
I am providing my entire home.nix
file for your reference. Please share your working example if possible (or at least the important snippets needed to get this working).
{ config, lib, pkgs, nixpkgs, self, ... }:
let
...
in {
targets.genericLinux.enable = true;
nixpkgs.overlays = [
(final: prev: {
lib = (prev.lib or {}) // {
gpuWrapPackage = pkg: pkgs.runCommand "${pkg.name}-nixgl-pkg-wrapper" {} ''
# Create a new package that wraps the binaries with nixGL
mkdir $out
ln -s ${pkg}/* $out
rm $out/bin
mkdir $out/bin
for bin in ${pkg}/bin/*
do
wrapped_bin=$out/bin/$(basename $bin)
echo "#!/bin/sh" > $wrapped_bin
echo "exec nixgl $bin \"\$@\"" >> $wrapped_bin
chmod +x $wrapped_bin
done
# If .desktop files refer to the old derivation, replace the references
if [ -d "${pkg}/share/applications" ] && grep "${pkg}" ${pkg}/share/applications/*.desktop > /dev/null
then
rm $out/share
mkdir -p $out/share
cd $out/share
ln -s ${pkg}/share/* ./
rm applications
mkdir applications
cd applications
cp -a ${pkg}/share/applications/* ./
for dsk in *.desktop
do
sed -i "s|${pkg}|$out|g" "$dsk"
done
fi
'';
};
gpuWrapCheck = pkg:
if config.targets.genericLinux.enable
then final.lib.gpuWrapPackage pkg # Comes from non-nixos.nix
else pkg;
gpu-wrappers = let
system = prev.system;
nixglPkgs = "${self}#gpuWrappers.${system}";
wrapIntel = type: lib.getExe self.inputs.nixgl.packages.${system}."nix${type}Intel";
wrapNvidia = type: ''
nix shell --quiet --impure ${nixglPkgs}.nix${type}Nvidia -c nix${type}Nvidia-$()
'';
in pkgs.runCommand "gpu-wrappers" {} ''
bin=$out/bin
mkdir -p $bin
cat > $bin/nixgl-intel <<EOF
#!/bin/sh
exec ${wrapIntel "GL"} ${wrapIntel "Vulkan"} "\$@"
EOF
chmod +x $bin/nixgl-intel
cat > $bin/nixgl-nvidia <<EOF
#!/bin/sh
glbin=\$(nix eval --quiet --raw --impure "${nixglPkgs}.nixGLNvidia.meta.name")
vkbin=\$(echo \$glbin | sed s/GL/Vulkan/)
exec nix shell --quiet --impure ${nixglPkgs}.nixGLNvidia ${nixglPkgs}.nixVulkanNvidia -c \$glbin \$vkbin "\$@"
EOF
chmod +x $bin/nixgl-nvidia
cat > $bin/nvidia-offload <<EOF
#!/bin/sh
export __NV_PRIME_RENDER_OFFLOAD=1
export __NV_PRIME_RENDER_OFFLOAD_PROVIDER=NVIDIA-G0
export __GLX_VENDOR_LIBRARY_NAME=nvidia
export __VK_LAYER_NV_optimus=NVIDIA_only
exec "\$@"
EOF
chmod +x $bin/nvidia-offload
cat > $bin/nixgl <<EOF
#!/bin/sh
if [ ! -h "${config.xdg.cacheHome}/nixgl/result" ]
then
mkdir -p "${config.xdg.cacheHome}/nixgl"
nix build --quiet --impure \
--out-link "${config.xdg.cacheHome}/nixgl/result" \
${nixglPkgs}.nixGLNvidia \
${nixglPkgs}.nixVulkanNvidia
fi
if [ "\$__NV_PRIME_RENDER_OFFLOAD" = "1" ]
then
nixgl-nvidia "\$@"
else
nixgl-intel "\$@"
fi
EOF
chmod +x $bin/nixgl
'';
})
];
home.activation = {
clearNixglCacle = lib.hm.dag.entryAfter [ "writeBoundary" ] ''
[ -v DRY_RUN ] || rm -f ${config.xdg.cacheHome}/nixgl/result*
'';
};
# Home Manager needs a bit of information about you and the paths it should
# manage.
home.username = "ricsan";
home.homeDirectory = "/home/ricsan";
# This value determines the Home Manager release that your configuration is
# compatible with. This helps avoid breakage when a new Home Manager release
# introduces backwards incompatible changes.
#
# You should not change this value, even if you update Home Manager. If you do
# want to update the value, then make sure to first check the Home Manager
# release notes.
home.stateVersion = "23.11"; # Please read the comment before changing.
# nixpkgs configuration
nixpkgs.config = {
allowUnfree = true;
};
home.packages = [
pkgs.gpu-wrappers
(lib.gpuWrapCheck pkgs.brave)
];
# Let Home Manager install and manage itself.
programs.home-manager.enable = true;
}
@RicSanOP That's the thing, I can't paste my whole config because it is huge, handling multiple machines. But I think I have solved your problem. There's actually three issues:
self
to the modules, which has to be done through extraSpecialArgs
.gpuWrapCheck
in the wrong place, it should be in the lib
part of the overlay. There's even a stray comment there about the definition being in another file 🤦lib
part of the overlay is not applied, even when the gpu-wrappers
package in the same overlay is added correctly. I don't understand why and I don't see a difference to my config. Perhaps someone more familiar with how overlays work could shed some light on that. But it's not really needed for your setup. The reason why I wanted gpuWrapPackage
and gpuWrapCheck
in pkgs.lib
is because I use it in several places in my own config. But there could be better ways to achieve that.I made adjustments to your flake and moved the wrapper functions out of pkgs.lib
. The result is below. It evaluates correctly for me, but I haven't tried applying the configuration to my system, so I can only hope it runs well for you. Don't forget, after applying the configuration, the "cache" (a result
symlink, really) for the nixgl package is removed, and the package is rebuilt the next time you run a wrapped application.
flake.nix
:
{
description = "Home Manager configuration of ricsan";
inputs = {
# Specify the source of Home Manager and Nixpkgs.
nixpkgs.url = "github:nixos/nixpkgs/nixpkgs-unstable";
home-manager = {
url = "github:nix-community/home-manager";
inputs.nixpkgs.follows = "nixpkgs";
};
nixgl = {
url = "github:nix-community/nixGL";
inputs.nixpkgs.follows = "nixpkgs";
};
};
outputs = { self, nixpkgs, home-manager, nixgl, ... }:
let
pkgs = nixpkgs.legacyPackages."x86_64-linux";
extraSpecialArgs = {
inherit self;
inherit nixpkgs;
};
in {
gpuWrappers = nixgl.defaultPackage;
homeConfigurations."ricsan" = home-manager.lib.homeManagerConfiguration {
inherit pkgs;
inherit extraSpecialArgs;
# Specify your home configuration modules here, for example,
# the path to your home.nix.
modules = [ ./home.nix ];
};
};
}
home.nix
:
{ config, lib, pkgs, nixpkgs, self, ... }:
let
gpuWrapPackage = pkg: pkgs.runCommand "${pkg.name}-nixgl-pkg-wrapper" {} ''
# Create a new package that wraps the binaries with nixGL
mkdir $out
ln -s ${pkg}/* $out
rm $out/bin
mkdir $out/bin
for bin in ${pkg}/bin/*
do
wrapped_bin=$out/bin/$(basename $bin)
echo "#!/bin/sh" > $wrapped_bin
echo "exec nixgl $bin \"\$@\"" >> $wrapped_bin
chmod +x $wrapped_bin
done
# If .desktop files refer to the old derivation, replace the references
if [ -d "${pkg}/share/applications" ] && grep "${pkg}" ${pkg}/share/applications/*.desktop > /dev/null
then
rm $out/share
mkdir -p $out/share
cd $out/share
ln -s ${pkg}/share/* ./
rm applications
mkdir applications
cd applications
cp -a ${pkg}/share/applications/* ./
for dsk in *.desktop
do
sed -i "s|${pkg}|$out|g" "$dsk"
done
fi
'';
gpuWrapCheck = pkg:
if config.targets.genericLinux.enable
then gpuWrapPackage pkg
else pkg;
in {
targets.genericLinux.enable = true;
nixpkgs.overlays = [
(final: prev: {
gpu-wrappers = let
system = prev.system;
nixglPkgs = "${self}#gpuWrappers.${system}";
wrapIntel = type: lib.getExe self.inputs.nixgl.packages.${system}."nix${type}Intel";
wrapNvidia = type: ''
nix shell --quiet --impure ${nixglPkgs}.nix${type}Nvidia -c nix${type}Nvidia-$()
'';
in pkgs.runCommand "gpu-wrappers" {} ''
bin=$out/bin
mkdir -p $bin
cat > $bin/nixgl-intel <<EOF
#!/bin/sh
exec ${wrapIntel "GL"} ${wrapIntel "Vulkan"} "\$@"
EOF
chmod +x $bin/nixgl-intel
cat > $bin/nixgl-nvidia <<EOF
#!/bin/sh
glbin=\$(nix eval --quiet --raw --impure "${nixglPkgs}.nixGLNvidia.meta.name")
vkbin=\$(echo \$glbin | sed s/GL/Vulkan/)
exec nix shell --quiet --impure ${nixglPkgs}.nixGLNvidia ${nixglPkgs}.nixVulkanNvidia -c \$glbin \$vkbin "\$@"
EOF
chmod +x $bin/nixgl-nvidia
cat > $bin/nvidia-offload <<EOF
#!/bin/sh
export __NV_PRIME_RENDER_OFFLOAD=1
export __NV_PRIME_RENDER_OFFLOAD_PROVIDER=NVIDIA-G0
export __GLX_VENDOR_LIBRARY_NAME=nvidia
export __VK_LAYER_NV_optimus=NVIDIA_only
exec "\$@"
EOF
chmod +x $bin/nvidia-offload
cat > $bin/nixgl <<EOF
#!/bin/sh
if [ ! -h "${config.xdg.cacheHome}/nixgl/result" ]
then
mkdir -p "${config.xdg.cacheHome}/nixgl"
nix build --quiet --impure \
--out-link "${config.xdg.cacheHome}/nixgl/result" \
${nixglPkgs}.nixGLNvidia \
${nixglPkgs}.nixVulkanNvidia
fi
if [ "\$__NV_PRIME_RENDER_OFFLOAD" = "1" ]
then
nixgl-nvidia "\$@"
else
nixgl-intel "\$@"
fi
EOF
chmod +x $bin/nixgl
'';
})
];
home.activation = {
clearNixglCache = lib.hm.dag.entryAfter [ "writeBoundary" ] ''
[ -v DRY_RUN ] || rm -f ${config.xdg.cacheHome}/nixgl/result*
'';
};
# Home Manager needs a bit of information about you and the paths it should
# manage.
home.username = "ricsan";
home.homeDirectory = "/home/ricsan";
# This value determines the Home Manager release that your configuration is
# compatible with. This helps avoid breakage when a new Home Manager release
# introduces backwards incompatible changes.
#
# You should not change this value, even if you update Home Manager. If you do
# want to update the value, then make sure to first check the Home Manager
# release notes.
home.stateVersion = "23.11"; # Please read the comment before changing.
# nixpkgs configuration
nixpkgs.config = {
allowUnfree = true;
};
home.packages = [
pkgs.gpu-wrappers
(gpuWrapCheck pkgs.brave)
];
# Let Home Manager install and manage itself.
programs.home-manager.enable = true;
}
Hey @exzombie , thanks a lot for the help. The above modifications have surely gotten home-manager to successfully switch. Now the gpu-wrappers.nixgl
binary does work with intel graphics settings. However, it simply does not work with nvidia graphics settings. I get an error for missing buildInputs
which I believe occurs at the following lines within `gpu-wrappers:
nix build --quiet --impure \
--out-link "${config.xdg.cacheHome}/nixgl/result" \
${nixglPkgs}.nixGLNvidia \
${nixglPkgs}.nixVulkanNvidia
and
exec nix shell --quiet --impure ${nixglPkgs}.nixGLNvidia ${nixglPkgs}.nixVulkanNvidia -c \$glbin \$vkbin "\$@"
which only happens when the following line (marked with **) from the gpu-wrappers.nixgl
binary is run
if [ "\$__NV_PRIME_RENDER_OFFLOAD" = "1" ]
then
**nixgl-nvidia "\$@"**
else
nixgl-intel "\$@"
fi
EOF
Here is the output when a graphical program successfully runs using the intel option
error:
… while calling the 'derivationStrict' builtin
at <nix/derivation-internal.nix>:9:12:
8|
9| strict = derivationStrict drvAttrs;
| ^
10|
… while evaluating derivation 'nixGLNvidia-550.54.14'
whose name attribute is located at /nix/store/371rdljjbpd9njxnrzns793apg1x1x2k-source/pkgs/stdenv/generic/make-derivation.nix:331:7
… while evaluating attribute 'text' of derivation 'nixGLNvidia-550.54.14'
at /nix/store/371rdljjbpd9njxnrzns793apg1x1x2k-source/pkgs/build-support/trivial-builders/default.nix:103:17:
102| ({
103| inherit text executable checkPhase allowSubstitutes preferLocalBuild;
| ^
104| passAsFile = [ "text" ]
(stack trace truncated; use '--show-trace' to show the full trace)
error: attribute 'buildInputs' missing
at /nix/store/aqf7gfx4d7fz9gqnxsgmg1shs0bfvcc9-source/nixGL.nix:84:31:
83| useGLVND = true;
84| nativeBuildInputs = oldAttrs.buildInputs ++ [zstd];
| ^
85| });
Warning, nixVulkanIntel overwriting existing LD_LIBRARY_PATH
Gtk-Message: 05:38:52.334: Failed to load module "canberra-gtk-module"
Gtk-Message: 05:38:52.335: Failed to load module "canberra-gtk-module"
DRM kernel driver 'nvidia-drm' in use. NVK requires nouveau.
Fontconfig error: Cannot load default config file: No such file: (null)
[24624:24624:0329/053852.527379:ERROR:gl_surface_presentation_helper.cc(260)] GetVSyncParametersIfAvailable() failed for 1 times!
[24624:24624:0329/053852.659945:ERROR:gl_surface_presentation_helper.cc(260)] GetVSyncParametersIfAvailable() failed for 2 times!
[24624:24624:0329/053852.953710:ERROR:gl_surface_presentation_helper.cc(260)] GetVSyncParametersIfAvailable() failed for 3 times!
Here is the output when a graphical program unsuccessfully runs using the nvidia option
error:
… while calling the 'derivationStrict' builtin
at <nix/derivation-internal.nix>:9:12:
8|
9| strict = derivationStrict drvAttrs;
| ^
10|
… while evaluating derivation 'nixGLNvidia-550.54.14'
whose name attribute is located at /nix/store/371rdljjbpd9njxnrzns793apg1x1x2k-source/pkgs/stdenv/generic/make-derivation.nix:331:7
… while evaluating attribute 'text' of derivation 'nixGLNvidia-550.54.14'
at /nix/store/371rdljjbpd9njxnrzns793apg1x1x2k-source/pkgs/build-support/trivial-builders/default.nix:103:17:
102| ({
103| inherit text executable checkPhase allowSubstitutes preferLocalBuild;
| ^
104| passAsFile = [ "text" ]
(stack trace truncated; use '--show-trace' to show the full trace)
error: attribute 'buildInputs' missing
at /nix/store/aqf7gfx4d7fz9gqnxsgmg1shs0bfvcc9-source/nixGL.nix:84:31:
83| useGLVND = true;
84| nativeBuildInputs = oldAttrs.buildInputs ++ [zstd];
| ^
85| });
nvidia
error:
… while calling the 'derivationStrict' builtin
at <nix/derivation-internal.nix>:9:12:
8|
9| strict = derivationStrict drvAttrs;
| ^
10|
… while evaluating derivation 'nixGLNvidia-550.54.14'
whose name attribute is located at /nix/store/371rdljjbpd9njxnrzns793apg1x1x2k-source/pkgs/stdenv/generic/make-derivation.nix:331:7
… while evaluating attribute 'text' of derivation 'nixGLNvidia-550.54.14'
at /nix/store/371rdljjbpd9njxnrzns793apg1x1x2k-source/pkgs/build-support/trivial-builders/default.nix:103:17:
102| ({
103| inherit text executable checkPhase allowSubstitutes preferLocalBuild;
| ^
104| passAsFile = [ "text" ]
(stack trace truncated; use '--show-trace' to show the full trace)
error: attribute 'buildInputs' missing
at /nix/store/aqf7gfx4d7fz9gqnxsgmg1shs0bfvcc9-source/nixGL.nix:84:31:
83| useGLVND = true;
84| nativeBuildInputs = oldAttrs.buildInputs ++ [zstd];
| ^
85| });
Do you have any ideas as to what is going on or how to fix it? I am glad this is close to working. Perhaps after I get this working and properly organized, I will take a look into getting that home-manager module made. Really appreciate your help and patience so far.
It appears something changed in nixGL. Now I'm afraid to update my own flake 😅. I can start brave from your configuration by changing the flake inputs to
inputs = {
# Specify the source of Home Manager and Nixpkgs.
nixpkgs.url = "github:nixos/nixpkgs/nixos-23.11";
home-manager = {
url = "github:nix-community/home-manager/release-23.11";
inputs.nixpkgs.follows = "nixpkgs";
};
nixgl = {
url = "github:nix-community/nixGL/489d6b095ab9d289fe11af0219a9ff00fe87c7c5";
inputs.nixpkgs.follows = "nixpkgs";
};
};
I'm not sure whether it's necessary to revert to older nixpgks, it's there because I tried that first. But what really fixed it was using the specific nixGL commit that I had in my own flake.lock
. Curiously, just running
nix build --impure github:nix-community/nixGL#defaultPackage.x86_64-linux.{nixGLNvidia,nixVulkanNvidia}
works well enough. I don't know why it would fail when calling build on the flake cached in nix store 🤔
@rengare Your flake wrapper helper (Mesa at least) are working great for me, thank you!
For cross reference, there is also https://github.com/nix-community/home-manager/issues/3968
May we continue there, and close this issue?
Hi.
Thanks for the awesome work!
I am using Home Manager within Pop OS 22.04 and have got stuck trying to make nixGL works. My configuration basically looks like this:
Try running
home-manager switch
gives me this:While
nix-env -iA nixgl.auto.nixGLDefault
, and thennixGL alacritty
works as expected.I found a few issues on nixGL is not a proper package of nixpkgs, and guess that it probably is the issue. I wonder if there is a workaround? Using
nix
and Home Manager feels so good that I do not want to come back to good oldapt
...Thanks!