While the common use case for nixkpkgs.makeWrapper is to add arguments to the end of a command line string or change environment variables, I think it would make sense to also provide plumbing for creating "Bernstein chain"-style wrappers. These are the ones where you pass your program as an argument to a wrapping command, which then invokes it for you in some way. Like so:
Instead of
$ my-cool-command --foo bar
you run
$ wrapper my-cool-command --foo bar
Common examples are su, sudo and ssh, but more relevant for Nix and home-manager are programs like nixGL or boxxy. The first of which in particular seems to have a recurring demand within the community for a good way to create wrappers:
While the above threads have many proposed solutions, I think it would be nice to consolidate efforts, especially with the special care needed to ensure things like .desktop files point to the wrapper script (something which is overlooked in at least some of said solutions).
Findings and my suggested solution
It's not at all obvious at first, but Nix's nixpkgs.makeWrapper does allow you to write these sorts of prefix wrappers, if you use the makeWrapper/makeShellWrapper function rather than the more specialized wrapProgram/wrapShellProgram. These versions allow you to specify a different name of the wrapped program than the one you are wrapping.
Here is an example I wrote to wrap programs with boxxy on my machine (though it is really generic enough to factor the actual wrapper program out already):
# wrap the binary for pkg in a boxxy call
{ boxxy
, copyDesktopItems
, lib
, makeWrapper
, stdenv
, pkg
, binaryName ? lib.meta.getExe pkg
}:
let
binaryBaseName = builtins.baseNameOf binaryName;
in
stdenv.mkDerivation rec {
pname = "${pkg.pname}-boxxy";
inherit (pkg) version meta;
src = pkg;
nativeBuildInputs = [ makeWrapper copyDesktopItems ];
desktopItems = [ pkg ];
postBuild = ''
mkdir -p $out/bin
makeShellWrapper ${lib.meta.getExe boxxy} $out/bin/${binaryBaseName} --add-flags ${binaryName}
'';
preFixup = ''
shopt -s nullglob
for desktopFile in $out/share/applications/*
do
substituteInPlace "$desktopFile" --replace ${binaryName} $out/bin/${binaryBaseName}
done
shopt -u nullglob
'';
}
The important line is
makeShellWrapper ${lib.meta.getExe boxxy} $out/bin/${binaryBaseName} --add-flags ${binaryName}
Let's instantiate it with a concrete program to be wrapped, just to aid in the discussion:
makeShellWrapper ${lib.meta.getExe boxxy} $out/bin/chromium --add-flags /nix/store/.../bin/chromium
To summarize what it does, since it is a bit backwards compared to normal usage of the library, it creates a wrapper script ($out/bin/chromium) -- not with the original chromium binary as input, but with the wrapper one. Then, it adds the original chromium binary as the first argument to this command.
Since you already use the makeWrapper package in wrapper-manager, I think it would be elegant to reuse its functionality like this. For example by providing a version of the derivation with makeWrapper instead of wrapProgram, plus a convenience function that then pre-fills the output name, argv0 and the first argument like above.
While the common use case for
nixkpkgs.makeWrapperis to add arguments to the end of a command line string or change environment variables, I think it would make sense to also provide plumbing for creating "Bernstein chain"-style wrappers. These are the ones where you pass your program as an argument to a wrapping command, which then invokes it for you in some way. Like so:Instead of
you run
Common examples are
su,sudoandssh, but more relevant for Nix andhome-managerare programs likenixGLorboxxy. The first of which in particular seems to have a recurring demand within the community for a good way to create wrappers:While the above threads have many proposed solutions, I think it would be nice to consolidate efforts, especially with the special care needed to ensure things like
.desktopfiles point to the wrapper script (something which is overlooked in at least some of said solutions).Findings and my suggested solution
It's not at all obvious at first, but Nix's
nixpkgs.makeWrapperdoes allow you to write these sorts of prefix wrappers, if you use themakeWrapper/makeShellWrapperfunction rather than the more specializedwrapProgram/wrapShellProgram. These versions allow you to specify a different name of the wrapped program than the one you are wrapping.Here is an example I wrote to wrap programs with
boxxyon my machine (though it is really generic enough to factor the actual wrapper program out already):The important line is
Let's instantiate it with a concrete program to be wrapped, just to aid in the discussion:
To summarize what it does, since it is a bit backwards compared to normal usage of the library, it creates a wrapper script (
$out/bin/chromium) -- not with the originalchromiumbinary as input, but with the wrapper one. Then, it adds the originalchromiumbinary as the first argument to this command.Since you already use the
makeWrapperpackage inwrapper-manager, I think it would be elegant to reuse its functionality like this. For example by providing a version of the derivation withmakeWrapperinstead ofwrapProgram, plus a convenience function that then pre-fills the output name, argv0 and the first argument like above.