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.