mirror of
https://github.com/NixOS/nixpkgs.git
synced 2024-11-26 17:03:01 +00:00
d8e1f6c976
providing easy setups of flags and configurations for derivations. configuration is passed via passthru automatically, additional names can be merged with final attrs in an automatic sensible way even supporting fix. fun and funMerge idea by Michael Raskin svn path=/nixpkgs/trunk/; revision=13546
1040 lines
40 KiB
Nix
1040 lines
40 KiB
Nix
# Utility functions.
|
|
|
|
let
|
|
|
|
inherit (builtins)
|
|
head tail isList stringLength substring lessThan sub
|
|
listToAttrs attrNames hasAttr;
|
|
|
|
in
|
|
|
|
rec {
|
|
listOfListsToAttrs = ll : builtins.listToAttrs (map (l : { name = (head l); value = (head (tail l)); }) ll);
|
|
|
|
|
|
# Identity function.
|
|
id = x: x;
|
|
|
|
|
|
# accumulates / merges all attr sets until null is fed.
|
|
# example: sumArgs id { a = 'a'; x = 'x'; } { y = 'y'; x = 'X'; } null
|
|
# result : { a = 'a'; x = 'X'; y = 'Y'; }
|
|
innerSumArgs = f : x : y : (if y == null then (f x)
|
|
else (innerSumArgs f (x // y)));
|
|
sumArgs = f : innerSumArgs f {};
|
|
|
|
# Advanced sumArgs version. Hm, twice as slow, I'm afraid.
|
|
# composedArgs id (x:x//{a="b";}) (x:x//{b=x.a + "c";}) null;
|
|
# {a="b" ; b="bc";};
|
|
innerComposedArgs = f : x : y : (if y==null then (f x)
|
|
else (if (builtins.isAttrs y) then
|
|
(innerComposedArgs f (x//y))
|
|
else (innerComposedArgs f (y x))));
|
|
composedArgs = f: innerComposedArgs f {};
|
|
|
|
defaultMergeArg = x : y: if builtins.isAttrs y then
|
|
y
|
|
else
|
|
(y x);
|
|
defaultMerge = x: y: x // (defaultMergeArg x y);
|
|
sumTwoArgs = f: x: y:
|
|
f (defaultMerge x y);
|
|
foldArgs = merger: f: init: x:
|
|
let arg=(merger init (defaultMergeArg init x)); in
|
|
# now add the function with composed args already applied to the final attrs
|
|
setAttrMerge "passthru" {} (f arg) ( x : x // { function = foldArgs merger f arg; } );
|
|
|
|
# returns f x // { passthru.fun = y : f (merge x y); } while preserving other passthru names.
|
|
# example: let ex = applyAndFun (x : removeAttrs x ["fixed"]) (mergeOrApply mergeAttr) {name = 6;};
|
|
# usage1 = ex.passthru.fun { name = 7; }; # result: { name = 7;}
|
|
# usage2 = ex.passthru.fun (a: a // {name = __add a.name 1; }); # result: { a = 7; }
|
|
# fix usage:
|
|
# usage3a = ex.passthru.fun (a: a // {name2 = a.fixed.toBePassed; }); # usage3a will fail because toBePassed is not yet given
|
|
# usage3b usage3a.passthru.fun { toBePassed = "foo";}; # result { name = 7; name2 = "foo"; toBePassed = "foo"; fixed = <this attrs>; }
|
|
applyAndFun = f : merge : x : assert (__isAttrs x || __isFunction x);
|
|
let takeFix = if (__isFunction x) then x else (attr: merge attr x); in
|
|
setAttrMerge "passthru" {} (fix (fixed : f (takeFix {inherit fixed;})))
|
|
( y : y //
|
|
{
|
|
fun = z : applyAndFun f merge (fixed: merge (takeFix fixed) z);
|
|
funMerge = z : applyAndFun f merge (fixed: let e = takeFix fixed; in e // merge e z);
|
|
} );
|
|
mergeOrApply = merge : x : y : if (__isFunction y) then y x else merge x y;
|
|
|
|
# rec { # an example of how composedArgsAndFun can be used
|
|
# a = composedArgsAndFun (x : x) { a = ["2"]; meta = { d = "bar";}; };
|
|
# # meta.d will be lost ! It's your task to preserve it (eg using a merge function)
|
|
# b = a.passthru.function { a = [ "3" ]; meta = { d2 = "bar2";}; };
|
|
# # instead of passing/ overriding values you can use a merge function:
|
|
# c = b.passthru.function ( x: { a = x.a ++ ["4"]; }); # consider using (maybeAttr "a" [] x)
|
|
# }
|
|
# result:
|
|
# {
|
|
# a = { a = ["2"]; meta = { d = "bar"; }; passthru = { function = .. }; };
|
|
# b = { a = ["3"]; meta = { d2 = "bar2"; }; passthru = { function = .. }; };
|
|
# c = { a = ["3" "4"]; meta = { d2 = "bar2"; }; passthru = { function = .. }; };
|
|
# # c2 is equal to c
|
|
# }
|
|
composedArgsAndFun = f: foldArgs defaultMerge f {};
|
|
|
|
# example a = pairMap (x : y : x + y) ["a" "b" "c" "d"];
|
|
# result: ["ab" "cd"]
|
|
innerPairMap = acc: f: l:
|
|
if l == [] then acc else
|
|
innerPairMap (acc ++ [(f (head l)(head (tail l)))])
|
|
f (tail (tail l));
|
|
pairMap = innerPairMap [];
|
|
|
|
|
|
|
|
# "Fold" a binary function `op' between successive elements of
|
|
# `list' with `nul' as the starting value, i.e., `fold op nul [x_1
|
|
# x_2 ... x_n] == op x_1 (op x_2 ... (op x_n nul))'. (This is
|
|
# Haskell's foldr).
|
|
fold = op: nul: list:
|
|
if list == []
|
|
then nul
|
|
else op (head list) (fold op nul (tail list));
|
|
|
|
|
|
# Concatenate a list of lists.
|
|
concatList = x : y : x ++ y;
|
|
concatLists = fold concatList [];
|
|
|
|
|
|
# Concatenate a list of strings.
|
|
concatStrings =
|
|
fold (x: y: x + y) "";
|
|
|
|
|
|
# Map and concatenate the result.
|
|
concatMap = f: list: concatLists (map f list);
|
|
|
|
concatMapStrings = f: list: concatStrings (map f list);
|
|
|
|
|
|
# Place an element between each element of a list, e.g.,
|
|
# `intersperse "," ["a" "b" "c"]' returns ["a" "," "b" "," "c"].
|
|
intersperse = separator: list:
|
|
if list == [] || tail list == []
|
|
then list
|
|
else [(head list) separator]
|
|
++ (intersperse separator (tail list));
|
|
|
|
toList = x : if (__isList x) then x else [x];
|
|
|
|
concatStringsSep = separator: list:
|
|
concatStrings (intersperse separator list);
|
|
|
|
makeLibraryPath = paths: concatStringsSep ":" (map (path: path + "/lib") paths);
|
|
|
|
|
|
# Flatten the argument into a single list; that is, nested lists are
|
|
# spliced into the top-level lists. E.g., `flatten [1 [2 [3] 4] 5]
|
|
# == [1 2 3 4 5]' and `flatten 1 == [1]'.
|
|
flatten = x:
|
|
if isList x
|
|
then fold (x: y: (flatten x) ++ y) [] x
|
|
else [x];
|
|
|
|
|
|
# Return an attribute from nested attribute sets. For instance ["x"
|
|
# "y"] applied to some set e returns e.x.y, if it exists. The
|
|
# default value is returned otherwise.
|
|
# comment: there is also builtins.getAttr ? (is there a better name for this function?)
|
|
getAttr = attrPath: default: e:
|
|
let attr = head attrPath;
|
|
in
|
|
if attrPath == [] then e
|
|
else if builtins ? hasAttr && builtins.hasAttr attr e
|
|
then getAttr (tail attrPath) default (builtins.getAttr attr e)
|
|
else default;
|
|
|
|
# shortcut for getAttr ["name"] default attrs
|
|
maybeAttr = name: default: attrs:
|
|
if (__hasAttr name attrs) then (__getAttr name attrs) else default;
|
|
|
|
|
|
# Filter a list using a predicate; that is, return a list containing
|
|
# every element from `list' for which `pred' returns true.
|
|
filter = pred: list:
|
|
fold (x: y: if pred x then [x] ++ y else y) [] list;
|
|
|
|
|
|
# Return true if `list' has an element `x':
|
|
elem = x: list: fold (a: bs: x == a || bs) false list;
|
|
|
|
|
|
# Find the sole element in the list matching the specified
|
|
# predicate, returns `default' if no such element exists, or
|
|
# `multiple' if there are multiple matching elements.
|
|
findSingle = pred: default: multiple: list:
|
|
let found = filter pred list;
|
|
in if found == [] then default
|
|
else if tail found != [] then multiple
|
|
else head found;
|
|
|
|
|
|
# Return true iff function `pred' returns true for at least element
|
|
# of `list'.
|
|
any = pred: list:
|
|
if list == [] then false
|
|
else if pred (head list) then true
|
|
else any pred (tail list);
|
|
|
|
|
|
# Return true iff function `pred' returns true for all elements of
|
|
# `list'.
|
|
all = pred: list:
|
|
if list == [] then true
|
|
else if pred (head list) then all pred (tail list)
|
|
else false;
|
|
|
|
# much shorter implementations using map and fold (are lazy as well)
|
|
# which ones are better?
|
|
# true if all/ at least one element(s) satisfy f
|
|
# all = f : l : fold logicalAND true (map f l);
|
|
# any = f : l : fold logicalOR false (map f l);
|
|
|
|
|
|
# Return true if each element of a list is equal, false otherwise.
|
|
eqLists = xs: ys:
|
|
if xs == [] && ys == [] then true
|
|
else if xs == [] || ys == [] then false
|
|
else head xs == head ys && eqLists (tail xs) (tail ys);
|
|
|
|
|
|
# Workaround, but works in stable Nix now.
|
|
eqStrings = a: b: (a+(substring 0 0 b)) == ((substring 0 0 a)+b);
|
|
|
|
|
|
# Determine whether a filename ends in the given suffix.
|
|
hasSuffix = ext: fileName:
|
|
let lenFileName = stringLength fileName;
|
|
lenExt = stringLength ext;
|
|
in !(lessThan lenFileName lenExt) &&
|
|
substring (sub lenFileName lenExt) lenFileName fileName == ext;
|
|
|
|
hasSuffixHack = a: b: hasSuffix (a+(substring 0 0 b)) ((substring 0 0 a)+b);
|
|
|
|
|
|
# Bring in a path as a source, filtering out all Subversion and CVS
|
|
# directories, as well as backup files (*~).
|
|
cleanSource =
|
|
let filter = name: type: let baseName = baseNameOf (toString name); in ! (
|
|
# Filter out Subversion and CVS directories.
|
|
(type == "directory" && (baseName == ".svn" || baseName == "CVS")) ||
|
|
# Filter out backup files.
|
|
(hasSuffix "~" baseName)
|
|
);
|
|
in src: builtins.filterSource filter src;
|
|
|
|
|
|
# Get all files ending with the specified suffices from the given
|
|
# directory. E.g. `sourceFilesBySuffices ./dir [".xml" ".c"]'.
|
|
sourceFilesBySuffices = path: exts:
|
|
let filter = name: type:
|
|
let base = baseNameOf (toString name);
|
|
in type != "directory" && any (ext: hasSuffix ext base) exts;
|
|
in builtins.filterSource filter path;
|
|
|
|
|
|
# Return a singleton list or an empty list, depending on a boolean
|
|
# value. Useful when building lists with optional elements
|
|
# (e.g. `++ optional (system == "i686-linux") flashplayer').
|
|
optional = cond: elem: if cond then [elem] else [];
|
|
|
|
|
|
# Return a list or an empty list, dependening on a boolean value.
|
|
optionals = cond: elems: if cond then elems else [];
|
|
|
|
optionalString = cond: string: if cond then string else "";
|
|
|
|
# Return the second argument if the first one is true or the empty version
|
|
# of the second argument.
|
|
ifEnable = cond: val:
|
|
if cond then val
|
|
else if builtins.isList val then []
|
|
else if builtins.isAttrs val then {}
|
|
# else if builtins.isString val then ""
|
|
else if (val == true || val == false) then false
|
|
else null;
|
|
|
|
# Return a list of integers from `first' up to and including `last'.
|
|
range = first: last:
|
|
if builtins.lessThan last first
|
|
then []
|
|
else [first] ++ range (builtins.add first 1) last;
|
|
|
|
|
|
# Return true only if there is an attribute and it is true.
|
|
checkFlag = attrSet: name:
|
|
if (name == "true") then true else
|
|
if (name == "false") then false else
|
|
if (isInList (getAttr ["flags"] [] attrSet) name) then true else
|
|
getAttr [name] false attrSet ;
|
|
|
|
|
|
logicalOR = x: y: x || y;
|
|
logicalAND = x: y: x && y;
|
|
|
|
|
|
# Input : attrSet, [ [name default] ... ], name
|
|
# Output : its value or default.
|
|
getValue = attrSet: argList: name:
|
|
( getAttr [name] (if checkFlag attrSet name then true else
|
|
if argList == [] then null else
|
|
let x = builtins.head argList; in
|
|
if (head x) == name then
|
|
(head (tail x))
|
|
else (getValue attrSet
|
|
(tail argList) name)) attrSet );
|
|
|
|
|
|
# Input : attrSet, [[name default] ...], [ [flagname reqs..] ... ]
|
|
# Output : are reqs satisfied? It's asserted.
|
|
checkReqs = attrSet : argList : condList :
|
|
(
|
|
fold logicalAND true
|
|
(map (x: let name = (head x) ; in
|
|
|
|
((checkFlag attrSet name) ->
|
|
(fold logicalAND true
|
|
(map (y: let val=(getValue attrSet argList y); in
|
|
(val!=null) && (val!=false))
|
|
(tail x))))) condList)) ;
|
|
|
|
|
|
isInList = list: x:
|
|
if (list == []) then false else
|
|
if (x == (head list)) then true else
|
|
isInList (tail list) x;
|
|
|
|
|
|
uniqList = {inputList, outputList ? []}:
|
|
if (inputList == []) then outputList else
|
|
let x=head inputList;
|
|
newOutputList = outputList ++
|
|
(if (isInList outputList x) then [] else [x]);
|
|
in uniqList {outputList=newOutputList;
|
|
inputList = (tail inputList);};
|
|
|
|
uniqListExt = {inputList, outputList ? [],
|
|
getter ? (x : x), compare ? (x: y: x==y)}:
|
|
if (inputList == []) then outputList else
|
|
let x=head inputList;
|
|
isX = y: (compare (getter y) (getter x));
|
|
newOutputList = outputList ++
|
|
(if any isX outputList then [] else [x]);
|
|
in uniqListExt {outputList=newOutputList;
|
|
inputList = (tail inputList);
|
|
inherit getter compare;
|
|
};
|
|
|
|
|
|
|
|
condConcat = name: list: checker:
|
|
if list == [] then name else
|
|
if checker (head list) then
|
|
condConcat
|
|
(name + (head (tail list)))
|
|
(tail (tail list))
|
|
checker
|
|
else condConcat
|
|
name (tail (tail list)) checker;
|
|
|
|
# Merge sets of attributes and use the function f to merge
|
|
# attributes values.
|
|
zip = f: sets:
|
|
builtins.listToAttrs (map (name: {
|
|
inherit name;
|
|
value =
|
|
f name
|
|
(map (__getAttr name)
|
|
(filter (__hasAttr name) sets));
|
|
}) (concatMap builtins.attrNames sets));
|
|
|
|
# divide a list in two depending on the evaluation of a predicate.
|
|
partition = pred:
|
|
fold (h: t:
|
|
if pred h
|
|
then { right = [h] ++ t.right; wrong = t.wrong; }
|
|
else { right = t.right; wrong = [h] ++ t.wrong; }
|
|
) { right = []; wrong = []; };
|
|
|
|
# Take a function and evaluate it with its own returned value.
|
|
fix = f:
|
|
(rec { result = f result; }).result;
|
|
|
|
# flatten a list of sets returned by 'f'.
|
|
# f : function to evaluate each set.
|
|
# attr : name of the attribute which contains more values.
|
|
# default: result if 'x' is empty.
|
|
# x : list of values that have to be processed.
|
|
uniqFlattenAttr = f: attr: default: x:
|
|
if x == []
|
|
then default
|
|
else
|
|
let h = f (head x); t = tail x;
|
|
v = removeAttrs h [attr]; n = getAttr [attr] [] h;
|
|
in
|
|
if elem v default
|
|
then uniqFlattenAttr f attr default t
|
|
else uniqFlattenAttr f attr (default ++ [v]) (toList n ++ t)
|
|
;
|
|
|
|
/* If. ThenElse. Always. */
|
|
|
|
# create "if" statement that can be dealyed on sets until a "then-else" or
|
|
# "always" set is reached. When an always set is reached by
|
|
|
|
isIf = attrs: (typeOf attrs) == "if";
|
|
mkIf = condition: thenelse:
|
|
if isIf thenelse then
|
|
mkIf (condition && thenelse.condition) thenelse.thenelse
|
|
else {
|
|
_type = "if";
|
|
inherit condition thenelse;
|
|
};
|
|
|
|
|
|
isThenElse = attrs: (typeOf attrs) == "then-else";
|
|
mkThenElse = attrs:
|
|
assert attrs ? thenPart && attrs ? elsePart;
|
|
attrs // { _type = "then-else"; };
|
|
|
|
|
|
isAlways = attrs: (typeOf attrs) == "always";
|
|
mkAlways = value: { inherit value; _type = "always"; };
|
|
|
|
pushIf = f: attrs:
|
|
if isIf attrs then pushIf f (
|
|
let val = attrs.thenelse; in
|
|
# evaluate the condition.
|
|
if isThenElse val then
|
|
if attrs.condition then
|
|
val.thenPart
|
|
else
|
|
val.elsePart
|
|
# ignore the condition.
|
|
else if isAlways val then
|
|
val.value
|
|
# otherwise
|
|
else
|
|
f attrs.condition val)
|
|
else
|
|
attrs;
|
|
|
|
# take care otherwise you will have to handle this by hand.
|
|
rmIf = pushIf (condition: val: val);
|
|
|
|
evalIf = pushIf (condition: val:
|
|
# guess: empty else part.
|
|
ifEnable condition val
|
|
);
|
|
|
|
delayIf = pushIf (condition: val:
|
|
# rewrite the condition on sub-attributes.
|
|
mapAttrs (name: mkIf condition) val
|
|
);
|
|
|
|
/* Options. */
|
|
|
|
mkOption = attrs: attrs // {_type = "option";};
|
|
|
|
typeOf = x: if (__isAttrs x && x ? _type) then x._type else "";
|
|
|
|
isOption = attrs: (typeOf attrs) == "option";
|
|
|
|
addDefaultOptionValues = defs: opts: opts //
|
|
builtins.listToAttrs (map (defName:
|
|
{ name = defName;
|
|
value =
|
|
let
|
|
defValue = builtins.getAttr defName defs;
|
|
optValue = builtins.getAttr defName opts;
|
|
in
|
|
if typeOf defValue == "option"
|
|
then
|
|
# `defValue' is an option.
|
|
if builtins.hasAttr defName opts
|
|
then builtins.getAttr defName opts
|
|
else defValue.default
|
|
else
|
|
# `defValue' is an attribute set containing options.
|
|
# So recurse.
|
|
if builtins.hasAttr defName opts && builtins.isAttrs optValue
|
|
then addDefaultOptionValues defValue optValue
|
|
else addDefaultOptionValues defValue {};
|
|
}
|
|
) (builtins.attrNames defs));
|
|
|
|
mergeDefaultOption = name: list:
|
|
if list != [] && tail list == [] then head list
|
|
else if all __isFunction list then x: mergeDefaultOption (map (f: f x) list)
|
|
else if all __isList list then concatLists list
|
|
else if all __isAttrs list then mergeAttrs list
|
|
else if all (x: true == x || false == x) list then fold logicalOR false list
|
|
else abort "${name}: Cannot merge values.";
|
|
|
|
mergeTypedOption = typeName: predicate: merge: name: list:
|
|
if all predicate list then merge list
|
|
else abort "${name}: Expect a ${typeName}.";
|
|
|
|
mergeEnableOption = mergeTypedOption "boolean"
|
|
(x: true == x || false == x) (fold logicalOR false);
|
|
|
|
mergeListOption = mergeTypedOption "list"
|
|
__isList concatLists;
|
|
|
|
mergeStringOption = mergeTypedOption "string"
|
|
(x: if builtins ? isString then builtins.isString x else x + "")
|
|
concatStrings;
|
|
|
|
# Merge sets of options and bindings.
|
|
# noOption: function to call if no option is declared.
|
|
mergeOptionSets = noOption: path: opts:
|
|
if all __isAttrs opts then
|
|
zip (attr: opts:
|
|
let
|
|
name = if path == "" then attr else path + "." + attr;
|
|
test = partition isOption opts;
|
|
opt = ({ merge = mergeDefaultOption; apply = id; } // head test.right);
|
|
in
|
|
if test.right == [] then mergeOptionSets noOption name (map delayIf test.wrong)
|
|
else if tail test.right != [] then throw "Multiple options for '${name}'."
|
|
else if test.wrong == [] then opt.apply opt.default
|
|
else opt.apply (opt.merge name (map evalIf test.wrong))
|
|
) opts
|
|
else noOption path opts;
|
|
|
|
# Keep all option declarations and add an attribute "name" inside
|
|
# each option which contains the path that has to be followed to
|
|
# access it.
|
|
filterOptionSets = path: opts:
|
|
if all __isAttrs opts then
|
|
zip (attr: opts:
|
|
let
|
|
name = if path == "" then attr else path + "." + attr;
|
|
test = partition isOption opts;
|
|
in
|
|
if test.right == [] then filterOptionSets name (map delayIf test.wrong)
|
|
else if tail test.right != [] then throw "Multiple options for '${name}'."
|
|
else { inherit name; } // (head test.right)
|
|
) opts
|
|
else {};
|
|
|
|
# Evaluate a list of option sets that would be merged with the
|
|
# function "merge" which expects two arguments. The attribute named
|
|
# "require" is used to imports option declarations and bindings.
|
|
fixOptionSetsFun = merge: pkgs: opts:
|
|
let
|
|
# ignore all conditions that are on require attributes.
|
|
rmRequireIf = conf:
|
|
let conf2 = delayIf conf; in
|
|
if conf2 ? require then
|
|
conf2 // { require = rmIf conf2.require; }
|
|
else
|
|
conf2;
|
|
|
|
# call configuration "files" with one of the existing convention.
|
|
optionSet = config: configFun:
|
|
if __isFunction configFun then
|
|
let result = configFun { inherit pkgs config; }; in
|
|
# {pkgs, config, ...}: {..}
|
|
if builtins.isAttrs result then result
|
|
# pkgs: config: {..}
|
|
else configFun pkgs config
|
|
# {..}
|
|
else configFun;
|
|
|
|
processConfig = config: configFun:
|
|
rmRequireIf (optionSet config configFun);
|
|
in
|
|
config: merge "" (
|
|
uniqFlattenAttr (processConfig config) "require" [] (toList opts)
|
|
);
|
|
|
|
fixOptionSets = merge: pkgs: opts:
|
|
fix (fixOptionSetsFun merge pkgs opts);
|
|
|
|
optionAttrSetToDocList = (l: attrs:
|
|
(if (getAttr ["_type"] "" attrs) == "option" then
|
|
[({
|
|
#inherit (attrs) description;
|
|
description = if attrs ? description then attrs.description else
|
|
throw ("No description ${toString l} : ${whatis attrs}");
|
|
}
|
|
//(if attrs ? example then {inherit(attrs) example;} else {} )
|
|
//(if attrs ? default then {inherit(attrs) default;} else {} )
|
|
//{name = l;}
|
|
)]
|
|
else (concatLists (map (s: (optionAttrSetToDocList
|
|
(l + (if l=="" then "" else ".") + s) (builtins.getAttr s attrs)))
|
|
(builtins.attrNames attrs)))));
|
|
|
|
innerModifySumArgs = f: x: a: b: if b == null then (f a b) // x else
|
|
innerModifySumArgs f x (a // b);
|
|
modifySumArgs = f: x: innerModifySumArgs f x {};
|
|
|
|
debugVal = if builtins ? trace then x: (builtins.trace x x) else x: x;
|
|
debugXMLVal = if builtins ? trace then x: (builtins.trace (builtins.toXML x) x) else x: x;
|
|
|
|
# this can help debug your code as well - designed to not produce thousands of lines
|
|
traceWhatis = x : __trace (whatis x) x;
|
|
traceMarked = str: x: __trace (str + (whatis x)) x;
|
|
attrNamesToStr = a : concatStringsSep "; " (map (x : "${x}=") (__attrNames a));
|
|
whatis = x :
|
|
if (__isAttrs x) then
|
|
if (x ? outPath) then "x is a derivation, name ${if x ? name then x.name else "<no name>"}, { ${attrNamesToStr x} }"
|
|
else "x is attr set { ${attrNamesToStr x} }"
|
|
else if (__isFunction x) then "x is a function"
|
|
else if (x == []) then "x is an empty list"
|
|
else if (__isList x) then "x is a list, first item is : ${whatis (__head x)}"
|
|
else if (x == true) then "x is boolean true"
|
|
else if (x == false) then "x is boolean false"
|
|
else if (x == null) then "x is null"
|
|
else "x is probably a string starting, starting characters: ${__substring 0 50 x}..";
|
|
# trace the arguments passed to function and its result
|
|
traceCall = n : f : a : let t = n2 : x : traceMarked "${n} ${n2}:" x; in t "result" (f (t "arg 1" a));
|
|
traceCall2 = n : f : a : b : let t = n2 : x : traceMarked "${n} ${n2}:" x; in t "result" (f (t "arg 1" a) (t "arg 2" b));
|
|
traceCall3 = n : f : a : b : c : let t = n2 : x : traceMarked "${n} ${n2}:" x; in t "result" (f (t "arg 1" a) (t "arg 2" b) (t "arg 3" c));
|
|
|
|
|
|
|
|
innerClosePropagation = ready: list: if list == [] then ready else
|
|
if (head list) ? propagatedBuildInputs then
|
|
innerClosePropagation (ready ++ [(head list)])
|
|
((head list).propagatedBuildInputs ++ (tail list)) else
|
|
innerClosePropagation (ready ++ [(head list)]) (tail list);
|
|
|
|
closePropagation = list: (uniqList {inputList = (innerClosePropagation [] list);});
|
|
|
|
stringToCharacters = s : let l = __stringLength s; in
|
|
if (__lessThan l 1) then [""] else [(__substring 0 1 s)] ++ stringToCharacters (__substring 1 (__sub l 1) s);
|
|
|
|
# should this be implemented as primop ? Yes it should..
|
|
escapeShellArg = s :
|
|
let escapeChar = x : if ( x == "'" ) then "'\"'\"'" else x;
|
|
in "'" + concatStrings (map escapeChar (stringToCharacters s) ) +"'";
|
|
|
|
defineShList = name : list : "\n${name}=(${concatStringsSep " " (map escapeShellArg list)})\n";
|
|
|
|
# this as well :-) arg: http://foo/bar/bz.ext returns bz.ext
|
|
dropPath = s :
|
|
if s == "" then "" else
|
|
let takeTillSlash = left : c : s :
|
|
if left == 0 then s
|
|
else if (__substring left 1 s == "/") then
|
|
(__substring (__add left 1) (__sub c 1) s)
|
|
else takeTillSlash (__sub left 1) (__add c 1) s; in
|
|
takeTillSlash (__sub (__stringLength s) 1) 1 s;
|
|
|
|
# calls a function (f attr value ) for each record item. returns a list
|
|
# should be renamed to mapAttrsFlatten
|
|
mapRecordFlatten = f : r : map (attr: f attr (builtins.getAttr attr r) ) (attrNames r);
|
|
|
|
# maps a function on each attr value
|
|
# f = attr : value : ..
|
|
mapAttrs = f : r : listToAttrs ( mapRecordFlatten (a : v : nv a ( f a v ) ) r);
|
|
|
|
# to be used with listToAttrs (_a_ttribute _v_alue)
|
|
nv = name : value : { inherit name value; };
|
|
# attribute set containing one attribute
|
|
nvs = name : value : listToAttrs [ (nv name value) ];
|
|
# adds / replaces an attribute of an attribute set
|
|
setAttr = set : name : v : set // (nvs name v);
|
|
|
|
# setAttrMerge (similar to mergeAttrsWithFunc but only merges the values of a particular name)
|
|
# setAttrMerge "a" [] { a = [2];} (x : x ++ [3]) -> { a = [2 3]; }
|
|
# setAttrMerge "a" [] { } (x : x ++ [3]) -> { a = [ 3]; }
|
|
setAttrMerge = name : default : attrs : f :
|
|
setAttr attrs name (f (maybeAttr name default attrs));
|
|
|
|
# iterates over a list of attributes collecting the attribute attr if it exists
|
|
catAttrs = attr : l : fold ( s : l : if (hasAttr attr s) then [(builtins.getAttr attr s)] ++ l else l) [] l;
|
|
|
|
mergeAttr = x : y : x // y;
|
|
mergeAttrs = fold mergeAttr {};
|
|
|
|
attrVals = nameList : attrSet :
|
|
map (x: builtins.getAttr x attrSet) nameList;
|
|
|
|
# Using f = a : b = b the result is similar to //
|
|
# merge attributes with custom function handling the case that the attribute
|
|
# exists in both sets
|
|
mergeAttrsWithFunc = f : set1 : set2 :
|
|
fold (n: set : if (__hasAttr n set)
|
|
then setAttr set n (f (__getAttr n set) (__getAttr n set2))
|
|
else set )
|
|
set1 (__attrNames set2);
|
|
|
|
# merging two attribute set concatenating the values of same attribute names
|
|
# eg { a = 7; } { a = [ 2 3 ]; } becomes { a = [ 7 2 3 ]; }
|
|
mergeAttrsConcatenateValues = mergeAttrsWithFunc ( a : b : (toList a) ++ (toList b) );
|
|
|
|
# merges attributes using //, if a name exisits in both attributes
|
|
# an error will be triggered unless its listed in mergeLists
|
|
# so you can mergeAttrsNoOverride { buildInputs = [a]; } { buildInputs = [a]; } {} to get
|
|
# { buildInputs = [a b]; }
|
|
# merging buildPhase does'nt really make sense. The cases will be rare where appending /prefixing will fit your needs?
|
|
# in these cases the first buildPhase will override the second one
|
|
# ! depreceated, use mergeAttrByFunc instead
|
|
mergeAttrsNoOverride = { mergeLists ? ["buildInputs" "propagatedBuildInputs"],
|
|
overrideSnd ? [ "buildPhase" ]
|
|
} : attrs1 : attrs2 :
|
|
fold (n: set :
|
|
setAttr set n ( if (__hasAttr n set)
|
|
then # merge
|
|
if elem n mergeLists # attribute contains list, merge them by concatenating
|
|
then (__getAttr n attrs2) ++ (__getAttr n attrs1)
|
|
else if elem n overrideSnd
|
|
then __getAttr n attrs1
|
|
else throw "error mergeAttrsNoOverride, attribute ${n} given in both attributes - no merge func defined"
|
|
else __getAttr n attrs2 # add attribute not existing in attr1
|
|
)) attrs1 (__attrNames attrs2);
|
|
|
|
|
|
# example usage:
|
|
# mergeAttrByFunc {
|
|
# inherit mergeAttrBy; # defined below
|
|
# buildInputs = [ a b ];
|
|
# } {
|
|
# buildInputs = [ c d ];
|
|
# };
|
|
# will result in
|
|
# { mergeAttrsBy = [...]; buildInputs = [ a b c d ]; }
|
|
# is used by prepareDerivationArgs and can be used when composing using
|
|
# foldArgs, composedArgsAndFun or applyAndFun. Example: composableDerivation in all-packages.nix
|
|
mergeAttrByFunc = x : y :
|
|
let
|
|
mergeAttrBy2 = { mergeAttrBy=mergeAttr; }
|
|
// (maybeAttr "mergeAttrBy" {} x)
|
|
// (maybeAttr "mergeAttrBy" {} y); in
|
|
mergeAttrs [
|
|
x y
|
|
(mapAttrs ( a : v : # merge special names using given functions
|
|
if (__hasAttr a x)
|
|
then if (__hasAttr a y)
|
|
then v (__getAttr a x) (__getAttr a y) # both have attr, use merge func
|
|
else (__getAttr a x) # only x has attr
|
|
else (__getAttr a y) # only y has attr)
|
|
) (removeAttrs mergeAttrBy2
|
|
# don't merge attrs which are neither in x nor y
|
|
(filter (a : (! __hasAttr a x) && (! __hasAttr a y) )
|
|
(__attrNames mergeAttrBy2))
|
|
)
|
|
)
|
|
];
|
|
mergeAttrsByFunc = fold mergeAttrByFunc {};
|
|
# sane defaults (same name as attr name so that inherit can be used)
|
|
mergeAttrBy = # { buildInputs = concatList; [...]; passthru = mergeAttr; [..]; }
|
|
listToAttrs (map (n : nv n concatList) [ "buildInputs" "propagatedBuildInputs" "configureFlags" "prePhases" "postAll" ])
|
|
// listToAttrs (map (n : nv n mergeAttr) [ "passthru" "meta" ]);
|
|
|
|
# returns atribute values as a list
|
|
flattenAttrs = set : map ( attr : builtins.getAttr attr set) (attrNames set);
|
|
mapIf = cond : f : fold ( x : l : if (cond x) then [(f x)] ++ l else l) [];
|
|
|
|
# pick attrs subset_attr_names and apply f
|
|
subsetmap = f : attrs : subset_attr_names :
|
|
listToAttrs (fold ( attr : r : if __hasAttr attr attrs
|
|
then r ++ [ ( nv attr ( f (__getAttr attr attrs) ) ) ] else r ) []
|
|
subset_attr_names );
|
|
|
|
# prepareDerivationArgs tries to make writing configurable derivations easier
|
|
# example:
|
|
# prepareDerivationArgs {
|
|
# mergeAttrBy = {
|
|
# myScript = x : y : x ++ "\n" ++ y;
|
|
# };
|
|
# cfg = {
|
|
# readlineSupport = true;
|
|
# };
|
|
# flags = {
|
|
# readline = {
|
|
# set = {
|
|
# configureFlags = [ "--with-compiler=${compiler}" ];
|
|
# buildInputs = [ compiler ];
|
|
# pass = { inherit compiler; READLINE=1; };
|
|
# assertion = compiler.dllSupport;
|
|
# myScript = "foo";
|
|
# };
|
|
# unset = { configureFlags = ["--without-compiler"]; };
|
|
# };
|
|
# };
|
|
# src = ...
|
|
# buildPhase = '' ... '';
|
|
# name = ...
|
|
# myScript = "bar";
|
|
# };
|
|
# if you don't have need for unset you can omit the surrounding set = { .. } attr
|
|
# all attrs except flags cfg and mergeAttrBy will be merged with the
|
|
# additional data from flags depending on config settings
|
|
# It's used in composableDerivation in all-packages.nix. It's also used
|
|
# heavily in the new python and libs implementation
|
|
prepareDerivationArgs = args:
|
|
let args2 = { cfg = {}; flags = {}; } // args;
|
|
flagName = name : "${name}Support";
|
|
cfgWithDefaults = (listToAttrs (map (n : nv (flagName n) false) (attrNames args2.flags)))
|
|
// args2.cfg;
|
|
opts = flattenAttrs (mapAttrs (a : v :
|
|
let v2 = if (v ? set || v ? unset) then v else { set = v; };
|
|
n = if (__getAttr (flagName a) cfgWithDefaults) then "set" else "unset";
|
|
attr = maybeAttr n {} v2; in
|
|
if (maybeAttr "assertion" true attr)
|
|
then attr
|
|
else throw "assertion of flag ${a} of derivation ${args.name} failed"
|
|
) args2.flags );
|
|
in removeAttrs
|
|
(mergeAttrsByFunc ([args] ++ opts))
|
|
["flags" "cfg" "mergeAttrBy" "fixed" ]; # fixed may be passed as fix argument or such
|
|
# supportFlag functions for convinience
|
|
sFlagEnable = { name, buildInputs ? [], propagatedBuildInputs ? [] } : {
|
|
set = { configureFlags = "--enable-${name}"; inherit buildInputs; inherit propagatedBuildInputs; };
|
|
unset = { configureFlags = "--disable-${name}"; };
|
|
};
|
|
|
|
|
|
|
|
# Marc 2nd proposal: (not everything has been tested in detail yet..)
|
|
# depreceated because it's too complicated. use prepareDerivationArgs instead
|
|
|
|
# usage / example
|
|
# flagConfig = {
|
|
# } // (enableDisableFeature "flagName" "configure_feature" extraAttrs;)
|
|
#
|
|
# is equal to
|
|
# flagConfig = {
|
|
# flagName = { cfgOption = "--enable-configure_feature"; } // extraAttrs;
|
|
# no_flagName = { cfgOption = "--disable-configure_feature"; };
|
|
enableDisableFeature = flagName : configure_feature : extraAttrs :
|
|
listToAttrs [ ( nv flagName ({ cfgOption = "--enable-${configure_feature}"; } // extraAttrs ) )
|
|
( nv "no_${flagName}" ({ cfgOption = "--disable-${configure_feature}"; } ) )];
|
|
|
|
# calls chooseOptionsByFlags2 with some preprocessing
|
|
# chooseOptionsByFlags2 returns an attribute set meant to be used to create new derivaitons.
|
|
# see mkDerivationByConfiguration in all-packages.nix and the examples given below.
|
|
# You can just copy paste them into all-packages.nix to test them..
|
|
|
|
chooseOptionsByFlags = { flagConfig, args, optionals ? [], defaults ? [],
|
|
collectExtraPhaseActions ? [] } :
|
|
let passedOptionals = filter ( x : hasAttr x args ) optionals; # these are in optionals and in args
|
|
# we simply merge in <optional_name> = { buildInputs = <arg.<optional_name>; pass = <arg.optional_name>; }
|
|
flagConfigWithOptionals = flagConfig // ( listToAttrs
|
|
(map ( o : nv o ( { buildInputs = o; pass = nvs o (builtins.getAttr o args); }
|
|
// getAttr [o] {} flagConfig )
|
|
)
|
|
passedOptionals ) );
|
|
|
|
in chooseOptionsByFlags2 flagConfigWithOptionals collectExtraPhaseActions args
|
|
( (getAttr ["flags"] defaults args) ++ passedOptionals);
|
|
|
|
chooseOptionsByFlags2 = flagConfig : collectExtraPhaseActions : args : flags :
|
|
let
|
|
# helper function
|
|
collectFlags = # state : flags :
|
|
fold ( flag : s : (
|
|
if (hasAttr flag s.result) then s # this state has already been visited
|
|
else if (! hasAttr flag flagConfig) then throw "unkown flag `${flag}' specified"
|
|
else let fDesc = (builtins.getAttr flag flagConfig);
|
|
implied = flatten ( getAttr ["implies"] [] fDesc );
|
|
blocked = flatten ( getAttr ["blocks"] [] fDesc );
|
|
# add this flag
|
|
s2 = s // { result = ( setAttr s.result flag (builtins.getAttr flag flagConfig) );
|
|
blockedFlagsBy = s.blockedFlagsBy
|
|
// listToAttrs (map (b: nv b flag ) blocked); };
|
|
# add implied flags
|
|
in collectFlags s2 implied
|
|
));
|
|
|
|
# chosen contains flagConfig but only having those attributes elected by flags
|
|
# (or by implies attributes of elected attributes)
|
|
options = let stateOpts = collectFlags { blockedFlagsBy = {}; result = {}; }
|
|
(flags ++ ( if (hasAttr "mandatory" flagConfig) then ["mandatory"] else [] ));
|
|
# these options have not been chosen (neither by flags nor by implies)
|
|
unsetOptions = filter ( x : (! hasAttr x stateOpts.result) && (hasAttr ("no_"+x) flagConfig))
|
|
( attrNames flagConfig );
|
|
# no add the corresponding no_ attributes as well ..
|
|
state = collectFlags stateOpts (map ( x : "no_" + x ) unsetOptions);
|
|
in # check for blockings:
|
|
assert ( all id ( map ( b: if (hasAttr b state.result)
|
|
then throw "flag ${b} is blocked by flag ${__getAttr b state.blockedFlagsBy}"
|
|
else true )
|
|
(attrNames state.blockedFlagsBy) ) );
|
|
state.result;
|
|
flatOptions = flattenAttrs options;
|
|
|
|
# helper functions :
|
|
collectAttrs = attr : catAttrs attr flatOptions;
|
|
optsConcatStrs = delimiter : attrs : concatStrings
|
|
( intersperse delimiter (flatten ( collectAttrs attrs ) ) );
|
|
|
|
ifStringGetArg = x : if (__isAttrs x) then x # ( TODO implement __isString ?)
|
|
else nvs x (__getAttr x args);
|
|
|
|
in assert ( all id ( mapRecordFlatten ( attr : r : if ( all id ( flatten (getAttr ["assertion"] [] r ) ) )
|
|
then true else throw "assertion failed flag ${attr}" )
|
|
options) );
|
|
( rec {
|
|
|
|
#foldOptions = attr: f : start: fold f start (catAttrs attr flatOptions);
|
|
|
|
# compared to flags flagsSet does also contain the implied flags.. This makes it easy to write assertions. ( assert args.
|
|
inherit options flatOptions collectAttrs optsConcatStrs;
|
|
|
|
buildInputs = map ( attr: if (! hasAttr attr args) then throw "argument ${attr} is missing!" else (builtins.getAttr attr args) )
|
|
(flatten (catAttrs "buildInputs" flatOptions));
|
|
propagatedBuildInputs = map ( attr: if (! hasAttr attr args) then throw "argument ${attr} is missing!" else (builtins.getAttr attr args) )
|
|
(flatten (catAttrs "propagatedBuildInputs" flatOptions));
|
|
|
|
configureFlags = optsConcatStrs " " "cfgOption";
|
|
|
|
#flags = listToAttrs (map ( flag: nv flag (hasAttr flag options) ) (attrNames flagConfig) );
|
|
flags_prefixed = listToAttrs (map ( flag: nv ("flag_set_"+flag) (hasAttr flag options) ) (attrNames flagConfig) );
|
|
|
|
pass = mergeAttrs ( map ifStringGetArg ( flatten (collectAttrs "pass") ) );
|
|
} # now add additional phase actions (see examples)
|
|
// listToAttrs ( map ( x : nv x (optsConcatStrs "\n" x) ) collectExtraPhaseActions ) );
|
|
}
|
|
|
|
/*
|
|
TODO: Perhaps it's better to move this documentation / these tests into some extra packages ..
|
|
|
|
# ###########################################################################
|
|
# configuration tutorial .. examples and tests..
|
|
# Copy this into all-packages.nix and try
|
|
|
|
# The following derviations will all fail..
|
|
# But they will print the passed options so that you can get to know
|
|
# how these configurations ought to work.
|
|
# TODO: There is no nice way to pass an otpion yet.
|
|
# I could imagine something like
|
|
# flags = [ "flagA" "flagB" { flagC = 4; } ];
|
|
|
|
# They are named:
|
|
# simpleYes, simpleNo,
|
|
# defaultsimpleYes, defaultsimpleNo
|
|
# optionalssimpleYes, optionalssimpleNo
|
|
# bitingsimpleYes can only be ran with -iA blockingBiteMonster
|
|
# assertionsimpleNo
|
|
# of course you can use -iA and the attribute name as well to select these examples
|
|
|
|
# dummy build input
|
|
whoGetsTheFlagFirst = gnused;
|
|
whoGetsTheFlagLast = gnumake;
|
|
|
|
# simple example demonstrating containing one flag.
|
|
# features:
|
|
# * configure options are passed automatically
|
|
# * buildInputs are collected (they are special, see the setup script)
|
|
# * they can be passed by additional name as well using pass = { inherit (args) python }
|
|
# ( or short (value not attrs) : pass = "python" )
|
|
# * an attribute named the same way as the flag is added indicating
|
|
# true/ false (flag has been set/ not set)
|
|
# * extra phase dependend commands can be added
|
|
# Its easy to add your own stuff using co.collectAttrs or co.optsConcatStrs
|
|
# ( perhaps this name will change?)
|
|
simpleFlagYesNoF = namePrefix : extraFlagAttrs : mkDerivationByConfiguration ( {
|
|
flagConfig = {
|
|
flag = { name = namePrefix + "simpleYes";
|
|
cfgOption = [ "--Yes" "--you-dont-need-a-list" ];
|
|
buildInputs = [ "whoGetsTheFlagFirst" ];
|
|
pass = { inherit gnumake; };
|
|
extraConfigureCmd = "echo Hello, it worked! ";
|
|
blocks = "bitingMonster";
|
|
};
|
|
no_flag = { name = namePrefix + "simpleNo";
|
|
cfgOption = "--no";
|
|
implies = ["bitingMonster"];
|
|
};
|
|
bitingMonster = {
|
|
extraConfigureCmd = "echo Ill bite you";
|
|
};
|
|
gnutar = { cfgOption="--with-gnutar";
|
|
# buildInputs and pass will be added automatically if gnutar is added to optionals
|
|
};
|
|
# can be used to check configure options of dependencies
|
|
# eg testFlag = { assertion = [ arg.desktop.flag_set_wmii (! arg.desktop.flag_set_gnome) (! arg.desktops.flag_set_kde ]; }
|
|
assertionFlag = { assertion = false; }; # assert is nix language keyword
|
|
|
|
};
|
|
|
|
collectExtraPhaseActions = [ "extraConfigureCmd" ];
|
|
|
|
extraAttrs = co : {
|
|
name = ( __head (co.collectAttrs "name") );
|
|
|
|
unpackPhase = "
|
|
echo my name is
|
|
echo \$name
|
|
echo
|
|
echo flag given \\(should be 1 or empty string\\) ?
|
|
echo \$flag_set_flag
|
|
echo
|
|
echo my build inputs are
|
|
echo \$buildInputs
|
|
echo
|
|
echo my configuration flags are
|
|
echo \$configureFlags
|
|
echo
|
|
echo what about gnumake? Did it pass?
|
|
echo \$gnumake
|
|
echo
|
|
echo configurePhase command is
|
|
echo $\configurePhase
|
|
echo
|
|
echo gnutar passed? \\(optional test\\)
|
|
echo \$gnutar
|
|
echo
|
|
echo dying now
|
|
echo die_Hopefully_Soon
|
|
";
|
|
configurePhase = co.extraConfigureCmd;
|
|
};
|
|
} // extraFlagAttrs );
|
|
|
|
|
|
simpleYes = simpleFlagYesNoF "" {} {
|
|
inherit whoGetsTheFlagFirst lib stdenv;
|
|
flags = ["flag"];
|
|
};
|
|
# note the "I'll bite you" because of the implies attribute
|
|
simpleNo = simpleFlagYesNoF "" {} {
|
|
inherit whoGetsTheFlagFirst lib stdenv;
|
|
flags = [];
|
|
};
|
|
|
|
# specifying defaults by adding a default attribute
|
|
|
|
yesAgainDefault = simpleFlagYesNoF "default" { defaults = [ "flag" ];} {
|
|
inherit whoGetsTheFlagFirst lib stdenv;
|
|
};
|
|
noAgainOverridingDefault = simpleFlagYesNoF "default" { defaults = [ "flag" ];} {
|
|
inherit whoGetsTheFlagFirst lib stdenv;
|
|
flags = [];
|
|
};
|
|
|
|
# requested by Michael Raskin: activate flag automatically if dependency is passed:
|
|
withGnutarOptional = simpleFlagYesNoF "optionals" { optionals = [ "gnutar" ];} {
|
|
flags = [ "flag" ]; # I only need to pass this to trigger name optionalssimpleYes
|
|
inherit whoGetsTheFlagFirst lib stdenv;
|
|
inherit gnutar;
|
|
};
|
|
withoutGnutarOptional = simpleFlagYesNoF "optionals" { optionals = [ "gnutar" ];} {
|
|
inherit whoGetsTheFlagFirst lib stdenv;
|
|
};
|
|
|
|
# blocking example, this shouldn't even start building:
|
|
blockingBiteMonster = simpleFlagYesNoF "biting" {} {
|
|
inherit whoGetsTheFlagFirst lib stdenv;
|
|
flags = [ "flag" "bitingMonster" ];
|
|
};
|
|
|
|
# assertion example this shouldn't even start building:
|
|
assertion = simpleFlagYesNoF "assertion" {} {
|
|
inherit whoGetsTheFlagFirst lib stdenv;
|
|
flags = [ "assertionFlag" ];
|
|
};
|
|
*/
|