nixGL
nixGL copied to clipboard
Easy automagic patching of applications
I want to patch all my applications that require OpenGL to automatically start within the NixGL wrapper. I think that applying it should be as easy as providing a list of packages to be patched. This could for example be done by simply injecting a hook into the nativeBuildInputs
via overrideAttrs
which then does all of the work.
This is similar to https://github.com/guibou/nixGL/issues/16#issuecomment-411698314, but deserves its own issue IMO.
I'm using the following overlay:
final: prev:
let
wrapGeneric = wrapper: pkg: { additionalBinaries ? [] }:
let
mainPrograms = if builtins.hasAttr "mainProgram" pkg.meta then [ pkg.meta.mainProgram ] else [];
bins = mainPrograms ++ additionalBinaries;
in if builtins.length bins == 0
then builtins.throw "No mainProgram defined and no additionalBinaries for ${pkg.name}"
else final.symlinkJoin {
name = "${pkg.name}-with-nixgl";
paths = let
wrapBin = bin: final.writeShellScriptBin bin ''
exec ${wrapper} ${pkg}/bin/${bin} "$@"
''; in (map wrapBin bins) ++ [ pkg ];
inherit (pkg) meta;
};
wrapNixGL = wrapGeneric "${final.nixgl.nixGLIntel}/bin/nixGLIntel";
wrapVulkanGL = wrapGeneric "${final.nixgl.nixVulkanIntel}/bin/nixVulkanIntel ${final.nixgl.nixGLIntel}/bin/nixGLIntel";
go = builtins.mapAttrs (n: wrapNixGL prev.${n});
goVulkan = builtins.mapAttrs (n: wrapVulkanGL prev.${n});
in go {
# things work fine when meta.mainProgram is specified
firefox = {};
# when it isn't, need to specify the binaries to wrap
anydesk.additionalBinaries = [ "anydesk" ];
} // goVulkan { # adds vulkan env in addition to gl (but I didn't test this extensively)
steam = {};
}
If you wanted always wrap all binaries, you could try getting the list of files in ${pkg}/bin/
, but it doesn't seem like the best idea necessarily.
You'll need to tweak wrapper paths if you're targeting Nvidia proprietary drivers.
In theory, you could overrideAttrs
to add wrapProgram
to postFixup
directly, but nixGL only exposes wrapper scripts, so I went with wrapper scripts and symlinkJoin
here. Also, cache is invalidated by overrideAttrs
, so you'd end up rebuilding things like firefox and chromium, which isn't fun.
I started with a similar approach to the above - trying to work out the main program and wrapping just that.
But I've found wrapping all executables is useful, because apps like Sway provide auxiliary binaries which are required.
My new approach involves symbolic linking all the package's directories, then wrapping all the files in bin/
{ pkgs, lib, ... }:
pkg:
pkgs.stdenv.mkDerivation {
name = "${pkg.name}-nixgl-wrapped";
unpackPhase = "true";
subject = pkg;
buildPhase = ''
mkdir $out
for d in $subject/*/; do
ln -s --target-directory=$out $d
done
rm $out/bin
mkdir $out/bin
for f in $subject/bin/*; do
wrapper="$out/bin/$(basename "$f")"
echo "#! @shell@ -e" > "$wrapper"
echo "${pkgs.nixgl.nixGLIntel}/bin/nixGLIntel $f \"\$@\"" >> "$wrapper"
chmod +x $wrapper
substituteAllInPlace $wrapper
done
'';
}
Import this function and call it with a package to wrap nixGLIntel
around it.
Issues:
- wrapping libexec executables?
- fix references to to binaries in the other directories, particularly
.desktop
files. Maybe copy everything from the original package?
In addition to the methods mentioned here and in #16, there is an easy, one-off way to patch executables by leveraging the meta.priority
attribute. This is definitely "easy", but not quite "automagic".
For example, I would like to patch the zed
executable by prepending nixVulkanIntel
; this can be achieved with:
writeShellApplication {
name = "zed";
runtimeInputs = [
zed-editor
nixgl.nixVulkanIntel
];
text = ''exec -a zed nixVulkanIntel zed "$@"'';
meta.priority = (zed-editor.meta.priority or 5) - 1;
# override the original zed binary
}
This can be installed along with the original zed-editor
package, so we still have access to the original xdg desktop files.