In Nixpkgs we typically create wrappers using the makeWrapper
setup hook. We add the hook to the build inputs, and then we run makeWrapper
or wrapProgram
to create a wrapper, passing arguments for setting/unsetting/prefixing/suffixing environment variables, but also for setting e.g. argv0
or flags to be passed to the executable.
For a while now I’ve been wanting to have declarative wrappers, mainly so it gets easier to perform the wrapping in a separate derivation and get rid of nested wrappers in case we compose wrappers.
The following is an idea of how I would see this work. In a derivation, one would typically write the values passed on for environment variables.
stdenv.mkDerivation {
wrappers = with lib.wrappers; {
# prefix $PATH with git
"bin/*".PATH.value = [ git ];
"bin/someprog".XDG_DATA_DIRS.value = [ "$out/share" ];
};
}
Each environment variable has as value an attribute set:
-
value
: Value of the environment variable. String or list of strings. -
valueModifier
: Function that is applied tovalue
. Function. -
action
: What to do when creating a wrapper. E.g. whether the value should be set, unset, appended or prepended. Function. -
onMerge
: What should happen in case of a wrapper merge. Function.
I’m not sure whether another level of nesting is needed in order to deal with argv0
and add-flags
wrappers = with lib.wrappers; {
# prefix $PATH with git
"bin/*".vars.PATH.value = [ git ];
"bin/*".add-flags = [ "--verbose" ];
"bin/someprog".vars.XDG_DATA_DIRS.value = [ "$out/share" ];
"bin/someprog".argv0 = "someprog";
};
The lib.wrappers
library contains helper functions for dealing with these wrappers, including a function for merging wrappers. We also specify defaults for environment variables, e.g.:
/* The following are default values used throughout Nixpkgs */
defaults = {
LD_LIBRARY_PATH = {
action = set;
onMerge = prepend;
valueModifier = makeLibraryPath;
};
JAVA_HOME = {
action = set;
};
LUA_PATH = {
action = set;
onMerge = prepend;
};
PERL5LIB = {
action = set;
onMerge = prepend;
valueModifier = makeFullPerlPath;
};
PYTHONHOME = {
action = set;
# onMerge = raise "Cannot change PYTHONHOME as it will result in breakage.";
};
PYTHONPATH = {
action = set;
onMerge = prepend;
# valueModifier needs to be defined in the package set as its interpreter dependent
};
PATH = {
action = prefix ":";
onMerge = prepend;
valueModifier = makeBinPath;
};
XDG_DATA_DIRS = {
action = prefix ":";
onMerge = prepend;
valueModifier = drvs: makeSearchPath "share" ":" drvs;
};
};
A library function is used to generate the arguments to makeWrapper
. The stdenv
will be extended to extend the the wrappers
argument with the defaults and call makeWrapper
. That means pulling makeWrapper
into stdenv
.
I’ve partially implemented this, and would like to know now what you think of this. Especially, regarding the following two points:
- an attribute set with defaults;
- including this in
stdenv
.