From 599015e8b071bc8d38779fbfc37961db1ac0f464 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Mon, 9 Feb 2009 16:51:03 +0000 Subject: [PATCH] * Split lib/default.nix into several files, as it had become a big mess. Also cleaned up some functions: - foldl appeared broken (it recursively called fold). - Renamed logicalAND/logicalOR to and/or. - Removed listOfListsToAttrs, eqStrings: obsolete. - Removed isInList, which does the same thing as elem. - stringToCharacters: don't return a "" at the end of the list. - Renamed concatList to concat, as concatList (singular) is a misnomer: it takes two lists. Likewise, renamed mergeAttr to mergeAttrs. misc.nix still contains a lot of stuff that should be refactored and moved to other files. svn path=/nixpkgs/trunk/; revision=14013 --- pkgs/applications/audio/flac/default.nix | 35 +- pkgs/development/compilers/ocaml/default.nix | 11 +- .../interpreters/python-new/2.5/default.nix | 6 +- pkgs/lib/attrsets.nix | 22 + pkgs/lib/debug.nix | 15 + pkgs/lib/default.nix | 884 +----------------- pkgs/lib/lists.nix | 118 +++ pkgs/lib/misc.nix | 385 ++++++++ pkgs/lib/options.nix | 267 ++++++ pkgs/lib/sources.nix | 29 + pkgs/lib/strings.nix | 85 ++ pkgs/lib/trivial.nix | 18 + pkgs/top-level/all-packages.nix | 7 +- pkgs/top-level/builder-defs.nix | 2 + 14 files changed, 982 insertions(+), 902 deletions(-) create mode 100644 pkgs/lib/attrsets.nix create mode 100644 pkgs/lib/debug.nix create mode 100644 pkgs/lib/lists.nix create mode 100644 pkgs/lib/misc.nix create mode 100644 pkgs/lib/options.nix create mode 100644 pkgs/lib/sources.nix create mode 100644 pkgs/lib/strings.nix create mode 100644 pkgs/lib/trivial.nix diff --git a/pkgs/applications/audio/flac/default.nix b/pkgs/applications/audio/flac/default.nix index 6095e59fa07..ad9d3356cda 100644 --- a/pkgs/applications/audio/flac/default.nix +++ b/pkgs/applications/audio/flac/default.nix @@ -1,19 +1,16 @@ -args: with args; -let - flacFun = version: hash: - stdenv.mkDerivation rec { - name = "flac-${version}"; - src = fetchurl ({ - url = "http://downloads.xiph.org/releases/flac/${name}.tar.gz"; - } // hash); - buildInputs = [libogg]; - meta = { - homepage = http://flac.sourceforge.net; - }; - }; -in -stdenv.lib.listOfListsToAttrs [ - [ "default" (flacFun "1.2.1" { sha256 = "1pry5lgzfg57pga1zbazzdd55fkgk3v5qy4axvrbny5lrr5s8dcn"; }) ] - [ "1.2.1" (flacFun "1.2.1" { sha256 = "1pry5lgzfg57pga1zbazzdd55fkgk3v5qy4axvrbny5lrr5s8dcn"; }) ] - [ "1.1.2" (flacFun "1.1.2" { md5 = "2bfc127cdda02834d0491ab531a20960"; }) ] -] +{stdenv, fetchurl, libogg}: + +stdenv.mkDerivation rec { + name = "flac-1.2.1"; + + src = fetchurl { + url = mirror://sourceforge/flac/flac-1.2.1.tar.gz; + sha256 = "1pry5lgzfg57pga1zbazzdd55fkgk3v5qy4axvrbny5lrr5s8dcn"; + }; + + buildInputs = [libogg]; + + meta = { + homepage = http://flac.sourceforge.net; + }; +} diff --git a/pkgs/development/compilers/ocaml/default.nix b/pkgs/development/compilers/ocaml/default.nix index 837a685198b..f3ef70b2a55 100644 --- a/pkgs/development/compilers/ocaml/default.nix +++ b/pkgs/development/compilers/ocaml/default.nix @@ -1,7 +1,8 @@ args: -args.stdenv.lib.listOfListsToAttrs [ - [ "3.08.0" (import ./3.08.0.nix args) ] - [ "3.09.1" (import ./3.09.1.nix args) ] - [ "3.10.0" (import ./3.10.0.nix args) ] - [ "default" (import ./3.09.1.nix args) ] + +builtins.listToAttrs [ + { name = "3.08.0"; value = import ./3.08.0.nix args; } + { name = "3.09.1"; value = import ./3.09.1.nix args; } + { name = "3.10.0"; value = import ./3.10.0.nix args; } + { name = "default"; value = import ./3.09.1.nix args; } ] diff --git a/pkgs/development/interpreters/python-new/2.5/default.nix b/pkgs/development/interpreters/python-new/2.5/default.nix index dfe86683b50..1c2f22f66bb 100644 --- a/pkgs/development/interpreters/python-new/2.5/default.nix +++ b/pkgs/development/interpreters/python-new/2.5/default.nix @@ -124,7 +124,7 @@ in buildPhase = ''python setup.py $setupFlags build''; installPhase = ''python setup.py $setupFlags install --prefix=$out''; mergeAttrBy = { - setupFlags = lib.concatList; + setupFlags = lib.concat; }; }; @@ -219,8 +219,8 @@ in done ''; mergeAttrBy = { - phases = lib.concatList; - pySrcs = lib.concatList; + phases = lib.concat; + pySrcs = lib.concat; pyCheck = x : y : "${x}\n${y}"; }; } diff --git a/pkgs/lib/attrsets.nix b/pkgs/lib/attrsets.nix new file mode 100644 index 00000000000..201fb91bd00 --- /dev/null +++ b/pkgs/lib/attrsets.nix @@ -0,0 +1,22 @@ +# Operations on attribute sets. + +with { inherit (builtins) head tail; }; + +rec { + inherit (builtins) attrNames listToAttrs hasAttr isAttrs; + + + # 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. !!! 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 && hasAttr attr e + then getAttr (tail attrPath) default (builtins.getAttr attr e) + else default; + + +} \ No newline at end of file diff --git a/pkgs/lib/debug.nix b/pkgs/lib/debug.nix new file mode 100644 index 00000000000..23d1a85c72f --- /dev/null +++ b/pkgs/lib/debug.nix @@ -0,0 +1,15 @@ +rec { + + + # Wrapper aroung the primop `addErrorContext', which shouldn't used + # directly. It evaluates and returns `val', but if an evaluation + # error occurs, the text in `msg' is added to the error context + # (stack trace) printed by Nix. + addErrorContext = + if builtins ? addErrorContext + then builtins.addErrorContext + else msg: val: val; + + + +} \ No newline at end of file diff --git a/pkgs/lib/default.nix b/pkgs/lib/default.nix index 349d38f9055..ee3b1ad085d 100644 --- a/pkgs/lib/default.nix +++ b/pkgs/lib/default.nix @@ -1,873 +1,17 @@ -# Utility functions. +let -let - - inherit (builtins) - head tail isList stringLength substring lessThan sub - listToAttrs attrNames hasAttr; + trivial = import ./trivial.nix; + lists = import ./lists.nix; + strings = import ./strings.nix; + attrsets = import ./attrsets.nix; + sources = import ./sources.nix; + options = import ./options.nix; + debug = import ./attrsets.nix; + misc = import ./misc.nix; 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 = ; } - 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 merge 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)); - - # Haskell's fold - foldl = op: nul: list: - if list == [] - then nul - else fold op (op nul (head list)) (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 elements by following the properties of the elements. - # next : return the list of following elements. - # seen : lists of elements already visited. - # default: result if 'x' is empty. - # x : list of values that have to be processed. - uniqFlatten = next: seen: default: x: - if x == [] - then default - else - let h = head x; t = tail x; n = next h; in - if elem h seen - then uniqFlatten next seen default t - else uniqFlatten next (seen ++ [h]) (default ++ [h]) (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 the condition - # is ignore. - - isIf = attrs: (typeOf attrs) == "if"; - mkIf = condition: thenelse: - if isIf thenelse then - mkIf (condition && thenelse.condition) thenelse.thenelse - else { - _type = "if"; - inherit condition thenelse; - }; - - - isNotdef = attrs: (typeOf attrs) == "notdef"; - mkNotdef = {_type = "notdef";}; - - - 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: - if condition then val else mkNotdef - ); - - 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 = 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 if all (x: x == toString x) list then concatStrings list - else throw "Cannot merge values."; - - mergeTypedOption = typeName: predicate: merge: list: - if all predicate list then merge list - else throw "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; - - mergeOneOption = list: - if list == [] then abort "This case should never happens." - else if tail list != [] then throw "Multiple definitions. Only one is allowed for this option." - else head list; - - - # Handle the traversal of option sets. All sets inside 'opts' are zipped - # and options declaration and definition are separated. If no option are - # declared at a specific depth, then the function recurse into the values. - # Other cases are handled by the optionHandler which contains two - # functions that are used to defined your goal. - # - export is a function which takes two arguments which are the option - # and the list of values. - # - notHandle is a function which takes the list of values are not handle - # by this function. - handleOptionSets = optionHandler@{export, notHandle, ...}: path: opts: - if all __isAttrs opts then - zip (attr: opts: - let - # Compute the path to reach the attribute. - name = if path == "" then attr else path + "." + attr; - - # Divide the definitions of the attribute "attr" between - # declaration (isOption) and definitions (!isOption). - test = partition isOption opts; - decls = test.right; defs = test.wrong; - - # Return the option declaration and add missing default - # attributes. - opt = { - inherit name; - merge = mergeDefaultOption; - apply = id; - } // (head decls); - - # Return the list of option sets. - optAttrs = map delayIf defs; - - # return the list of option values. - # Remove undefined values that are coming from evalIf. - optValues = filter (x: !isNotdef x) (map evalIf defs); - in - if decls == [] then handleOptionSets optionHandler name optAttrs - else addErrorContext "while evaluating the option ${name}:" ( - if tail decls != [] then throw "Multiple options." - else export opt optValues - ) - ) opts - else addErrorContext "while evaluating ${path}:" (notHandle opts); - - # Merge option sets and produce a set of values which is the merging of - # all options declare and defined. If no values are defined for an - # option, then the default value is used otherwise it use the merge - # function of each option to get the result. - mergeOptionSets = noOption: newMergeOptionSets; # ignore argument - newMergeOptionSets = - handleOptionSets { - export = opt: values: - opt.apply ( - if values == [] then - if opt ? default then opt.default - else throw "Not defined." - else opt.merge values - ); - notHandle = opts: throw "Used without option declaration."; - }; - - # Keep all option declarations. - filterOptionSets = - handleOptionSets { - export = opt: values: opt; - notHandle = opts: {}; - }; - - # 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. - # - # * cfg[0-9]: configuration - # * cfgSet[0-9]: configuration set - # - # merge: the function used to merge options sets. - # pkgs: is the set of packages available. (nixpkgs) - # opts: list of option sets or option set functions. - # config: result of this evaluation. - fixOptionSetsFun = merge: pkgs: opts: config: - let - # remove possible mkIf to access the require attribute. - noImportConditions = cfgSet0: - let cfgSet1 = delayIf cfgSet0; in - if cfgSet1 ? require then - cfgSet1 // { require = rmIf cfgSet1.require; } - else - cfgSet1; - - # call configuration "files" with one of the existing convention. - argumentHandler = cfg: - let - # {..} - cfg0 = cfg; - # {pkgs, config, ...}: {..} - cfg1 = cfg { inherit pkgs config merge; }; - # pkgs: config: {..} - cfg2 = cfg {} {}; - in - if __isFunction cfg0 then - if builtins.isAttrs cfg1 then cfg1 - else builtins.trace "Use '{pkgs, config, ...}:'." cfg2 - else cfg0; - - preprocess = cfg0: - let cfg1 = argumentHandler cfg0; - cfg2 = noImportConditions cfg1; - in cfg2; - - getRequire = x: toList (getAttr ["require"] [] (preprocess x)); - rmRequire = x: removeAttrs (preprocess x) ["require"]; - in - merge "" ( - map rmRequire ( - uniqFlatten getRequire [] [] (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 {}; - - # Wrapper aroung addErrorContext. The builtin should not be used - # directly. - addErrorContext = - if builtins ? addErrorContext - then builtins.addErrorContext - else msg: val: val; - - 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 ""}, { ${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)) - ) - ) - ]; - mergeAttrsByFuncDefaults = foldl mergeAttrByFunc { inherit mergeAttrBy; }; - # 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" "cfg" "flags" ]); - - # 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 - # - # should we check for misspelled cfg options? - 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 - (mergeAttrsByFuncDefaults ([args] ++ opts ++ [{ passthru = cfgWithDefaults; }])) - ["flags" "cfg" "mergeAttrBy" "fixed" ]; # fixed may be passed as fix argument or such - -} + { inherit trivial lists strings attrsets sources options debug; } + # !!! don't include everything at top-level; perhaps only the most + # commonly used functions. + // trivial // lists // strings // attrsets // sources // options + // debug // misc diff --git a/pkgs/lib/lists.nix b/pkgs/lib/lists.nix new file mode 100644 index 00000000000..b186e97a55b --- /dev/null +++ b/pkgs/lib/lists.nix @@ -0,0 +1,118 @@ +# General list operations. + +rec { + inherit (builtins) head tail isList; + + + # "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)); + + + # Left fold: `fold op nul [x_1 x_2 ... x_n] == op (... (op (op nul + # x_1) x_2) ... x_n)'. + foldl = op: nul: list: + if list == [] + then nul + else foldl op (op nul (head list)) (tail list); + + + # Concatenate a list of lists. + concatLists = fold (x: y: x ++ y) []; + + + # Map and concatenate the result. + concatMap = f: list: concatLists (map f list); + + + # 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]; + + + # 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; + + + # 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); + + + # 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 []; + + + # If argument is a list, return it; else, wrap it in a singleton + # list. If you're using this, you should almost certainly + # reconsider if there isn't a more "well-typed" approach. + toList = x: if builtins.isList x then x else [x]; + + + # 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; + + + # Partition the elements of a list in two lists, `right' and + # `wrong', 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 = []; }; + + +} diff --git a/pkgs/lib/misc.nix b/pkgs/lib/misc.nix new file mode 100644 index 00000000000..548b8d22edf --- /dev/null +++ b/pkgs/lib/misc.nix @@ -0,0 +1,385 @@ +let lib = import ./default.nix; in + +with import ./lists.nix; +with import ./attrsets.nix; +with import ./strings.nix; + +rec { + + + # 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 = ; } + applyAndFun = f : merge : x : assert (__isAttrs x || __isFunction x); + let takeFix = if (__isFunction x) then x else (attr: merge attr x); in + setAttrMerge "passthru" {} (lib.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 merge 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 []; + + + # shortcut for getAttr ["name"] default attrs + maybeAttr = name: default: attrs: + if (__hasAttr name attrs) then (__getAttr name attrs) else default; + + + # 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 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 (elem name (getAttr ["flags"] [] attrSet)) then true else + getAttr [name] false attrSet ; + + + # 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 lib.and true + (map (x: let name = (head x) ; in + + ((checkFlag attrSet name) -> + (fold lib.and true + (map (y: let val=(getValue attrSet argList y); in + (val!=null) && (val!=false)) + (tail x))))) condList)) ; + + + uniqList = {inputList, outputList ? []}: + if (inputList == []) then outputList else + let x=head inputList; + newOutputList = outputList ++ + (if elem x outputList 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)); + + # flatten a list of elements by following the properties of the elements. + # next : return the list of following elements. + # seen : lists of elements already visited. + # default: result if 'x' is empty. + # x : list of values that have to be processed. + uniqFlatten = next: seen: default: x: + if x == [] + then default + else + let h = head x; t = tail x; n = next h; in + if elem h seen + then uniqFlatten next seen default t + else uniqFlatten next (seen ++ [h]) (default ++ [h]) (n ++ t) + ; + + 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 ""}, { ${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);}); + + # 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; + + 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=lib.mergeAttrs; } + // (maybeAttr "mergeAttrBy" {} x) + // (maybeAttr "mergeAttrBy" {} y); in + fold lib.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)) + ) + ) + ]; + mergeAttrsByFuncDefaults = foldl mergeAttrByFunc { inherit mergeAttrBy; }; + # sane defaults (same name as attr name so that inherit can be used) + mergeAttrBy = # { buildInputs = concatList; [...]; passthru = mergeAttr; [..]; } + listToAttrs (map (n : nv n lib.concat) [ "buildInputs" "propagatedBuildInputs" "configureFlags" "prePhases" "postAll" ]) + // listToAttrs (map (n : nv n lib.mergeAttrs) [ "passthru" "meta" "cfg" "flags" ]); + + # 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 + # + # should we check for misspelled cfg options? + 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 + (mergeAttrsByFuncDefaults ([args] ++ opts ++ [{ passthru = cfgWithDefaults; }])) + ["flags" "cfg" "mergeAttrBy" "fixed" ]; # fixed may be passed as fix argument or such + + +} \ No newline at end of file diff --git a/pkgs/lib/options.nix b/pkgs/lib/options.nix new file mode 100644 index 00000000000..6d8bf4633de --- /dev/null +++ b/pkgs/lib/options.nix @@ -0,0 +1,267 @@ +# Nixpkgs/NixOS option handling. + +let lib = import ./default.nix; in + +with { inherit (builtins) head tail; }; +with import ./lists.nix; +with import ./attrsets.nix; + +rec { + + + 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 = 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 fold lib.mergeAttrs {} list + else if all (x: true == x || false == x) list then fold lib.or false list + else if all (x: x == toString x) list then lib.concatStrings list + else throw "Cannot merge values."; + + mergeTypedOption = typeName: predicate: merge: list: + if all predicate list then merge list + else throw "Expect a ${typeName}."; + + mergeEnableOption = mergeTypedOption "boolean" + (x: true == x || false == x) (fold lib.or false); + + mergeListOption = mergeTypedOption "list" + __isList concatLists; + + mergeStringOption = mergeTypedOption "string" + (x: if builtins ? isString then builtins.isString x else x + "") + lib.concatStrings; + + mergeOneOption = list: + if list == [] then abort "This case should never happens." + else if tail list != [] then throw "Multiple definitions. Only one is allowed for this option." + else head list; + + + # Handle the traversal of option sets. All sets inside 'opts' are zipped + # and options declaration and definition are separated. If no option are + # declared at a specific depth, then the function recurse into the values. + # Other cases are handled by the optionHandler which contains two + # functions that are used to defined your goal. + # - export is a function which takes two arguments which are the option + # and the list of values. + # - notHandle is a function which takes the list of values are not handle + # by this function. + handleOptionSets = optionHandler@{export, notHandle, ...}: path: opts: + if all __isAttrs opts then + lib.zip (attr: opts: + let + # Compute the path to reach the attribute. + name = if path == "" then attr else path + "." + attr; + + # Divide the definitions of the attribute "attr" between + # declaration (isOption) and definitions (!isOption). + test = partition isOption opts; + decls = test.right; defs = test.wrong; + + # Return the option declaration and add missing default + # attributes. + opt = { + inherit name; + merge = mergeDefaultOption; + apply = lib.id; + } // (head decls); + + # Return the list of option sets. + optAttrs = map delayIf defs; + + # return the list of option values. + # Remove undefined values that are coming from evalIf. + optValues = filter (x: !isNotdef x) (map evalIf defs); + in + if decls == [] then handleOptionSets optionHandler name optAttrs + else lib.addErrorContext "while evaluating the option ${name}:" ( + if tail decls != [] then throw "Multiple options." + else export opt optValues + ) + ) opts + else lib.addErrorContext "while evaluating ${path}:" (notHandle opts); + + # Merge option sets and produce a set of values which is the merging of + # all options declare and defined. If no values are defined for an + # option, then the default value is used otherwise it use the merge + # function of each option to get the result. + mergeOptionSets = noOption: newMergeOptionSets; # ignore argument + newMergeOptionSets = + handleOptionSets { + export = opt: values: + opt.apply ( + if values == [] then + if opt ? default then opt.default + else throw "Not defined." + else opt.merge values + ); + notHandle = opts: throw "Used without option declaration."; + }; + + # Keep all option declarations. + filterOptionSets = + handleOptionSets { + export = opt: values: opt; + notHandle = opts: {}; + }; + + # 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. + # + # * cfg[0-9]: configuration + # * cfgSet[0-9]: configuration set + # + # merge: the function used to merge options sets. + # pkgs: is the set of packages available. (nixpkgs) + # opts: list of option sets or option set functions. + # config: result of this evaluation. + fixOptionSetsFun = merge: pkgs: opts: config: + let + # remove possible mkIf to access the require attribute. + noImportConditions = cfgSet0: + let cfgSet1 = delayIf cfgSet0; in + if cfgSet1 ? require then + cfgSet1 // { require = rmIf cfgSet1.require; } + else + cfgSet1; + + # call configuration "files" with one of the existing convention. + argumentHandler = cfg: + let + # {..} + cfg0 = cfg; + # {pkgs, config, ...}: {..} + cfg1 = cfg { inherit pkgs config merge; }; + # pkgs: config: {..} + cfg2 = cfg {} {}; + in + if __isFunction cfg0 then + if builtins.isAttrs cfg1 then cfg1 + else builtins.trace "Use '{pkgs, config, ...}:'." cfg2 + else cfg0; + + preprocess = cfg0: + let cfg1 = argumentHandler cfg0; + cfg2 = noImportConditions cfg1; + in cfg2; + + getRequire = x: toList (getAttr ["require"] [] (preprocess x)); + rmRequire = x: removeAttrs (preprocess x) ["require"]; + in + merge "" ( + map rmRequire ( + lib.uniqFlatten getRequire [] [] (toList opts) + ) + ); + + fixOptionSets = merge: pkgs: opts: + lib.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} : ${lib.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))); + + + /* If. ThenElse. Always. */ + # !!! cleanup needed + + # create "if" statement that can be dealyed on sets until a "then-else" or + # "always" set is reached. When an always set is reached the condition + # is ignore. + + isIf = attrs: (typeOf attrs) == "if"; + mkIf = condition: thenelse: + if isIf thenelse then + mkIf (condition && thenelse.condition) thenelse.thenelse + else { + _type = "if"; + inherit condition thenelse; + }; + + + isNotdef = attrs: (typeOf attrs) == "notdef"; + mkNotdef = {_type = "notdef";}; + + + 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: + if condition then val else mkNotdef + ); + + delayIf = pushIf (condition: val: + # rewrite the condition on sub-attributes. + lib.mapAttrs (name: mkIf condition) val + ); + +} \ No newline at end of file diff --git a/pkgs/lib/sources.nix b/pkgs/lib/sources.nix new file mode 100644 index 00000000000..5a321bce708 --- /dev/null +++ b/pkgs/lib/sources.nix @@ -0,0 +1,29 @@ +# Functions for copying sources to the Nix store. + +let lib = import ./default.nix; in + +rec { + + + # 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. + (lib.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" && lib.any (ext: lib.hasSuffix ext base) exts; + in builtins.filterSource filter path; + + +} diff --git a/pkgs/lib/strings.nix b/pkgs/lib/strings.nix new file mode 100644 index 00000000000..8986d9c42d9 --- /dev/null +++ b/pkgs/lib/strings.nix @@ -0,0 +1,85 @@ +/* String manipulation functions. */ + +let lib = import ./default.nix; in + +rec { + inherit (builtins) stringLength substring head tail lessThan sub; + + + # Concatenate a list of strings. + concatStrings = lib.fold (x: y: x + y) ""; + + + # Map a function over a list and concatenate the resulting strings. + 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)); + + + # Concatenate a list of strings with a separator between each element, e.g. + # concatStringsSep " " ["foo" "bar" "xyzzy"] == "foo bar xyzzy" + concatStringsSep = separator: list: + concatStrings (intersperse separator list); + + + # Construct an RPATH containing the libraries for a set of packages, + # e.g. "${pkg1}/lib:${pkg2}/lib:...". + makeLibraryPath = paths: concatStringsSep ":" (map (path: path + "/lib") paths); + + + # Dependening on the boolean `cond', return either the given string + # or the empty string. + optionalString = cond: string: if cond then string else ""; + + + # 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; + + + # Convert a string to a list of characters (i.e. singleton strings). + # For instance, "abc" becomes ["a" "b" "c"]. This allows you to, + # e.g., map a function over each character. However, note that this + # will likely be horribly inefficient; Nix is not a general purpose + # programming language. Complex string manipulations should, if + # appropriate, be done in a derivation. + stringToCharacters = s: let l = stringLength s; in + if l == 0 + then [] + else [(substring 0 1 s)] ++ stringToCharacters (substring 1 (builtins.sub l 1) s); + + + # !!! this function seems broken - it doesn't escape all special + # characters, and in any case this should be done in a builder. + escapeShellArg = s: + let escapeChar = x: if x == "'" then "'\"'\"'" else x; + in "'" + concatStrings (map escapeChar (stringToCharacters s)) + "'"; + + + # !!! what is this for? + defineShList = name: list: "\n${name}=(${concatStringsSep " " (map escapeShellArg list)})\n"; + + + # arg: http://foo/bar/bz.ext returns bz.ext + # !!! isn't this what the `baseNameOf' primop does? + 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; + + +} \ No newline at end of file diff --git a/pkgs/lib/trivial.nix b/pkgs/lib/trivial.nix new file mode 100644 index 00000000000..a4fbd164fe5 --- /dev/null +++ b/pkgs/lib/trivial.nix @@ -0,0 +1,18 @@ +rec { + + # Identity function. + id = x: x; + + # Constant function. + const = x: y: x; + + # Named versions corresponding to some builtin operators. + concat = x: y: x ++ y; + or = x: y: x || y; + and = x: y: x && y; + mergeAttrs = x : y : x // y; + + # Take a function and evaluate it with its own returned value. + fix = f: let result = f result; in result; + +} diff --git a/pkgs/top-level/all-packages.nix b/pkgs/top-level/all-packages.nix index a948c744065..e486f0ecf27 100644 --- a/pkgs/top-level/all-packages.nix +++ b/pkgs/top-level/all-packages.nix @@ -5303,7 +5303,7 @@ let }; perlTaskCatalystTutorial = buildPerlPackage { - name = "Task-Catalyst-Tutorial"; + name = "Task-Catalyst-Tutorial-0.03"; src = fetchurl { url = mirror://cpan/authors/id/J/JR/JROCKWAY/Task-Catalyst-Tutorial-0.03.tar.gz; sha256 = "1f9nqg8h455s8dah482hf78i3h2bilji26i5m3lzr2cxws98lsak"; @@ -7480,12 +7480,10 @@ let firefox3Wrapper = wrapFirefox firefox3 "firefox" ""; firefox3b1BinWrapper = lowPrio (wrapFirefox firefox3b1Bin "firefox" ""); - flacAlts = import ../applications/audio/flac { + flac = import ../applications/audio/flac { inherit fetchurl stdenv libogg; }; - flac = getVersion "flac" flacAlts; - flashplayer = flashplayer10; flashplayer9 = import ../applications/networking/browsers/mozilla-plugins/flashplayer-9 { @@ -8618,7 +8616,6 @@ let saneBackends chmlib python libzip gmp sqlite libidn runCommand lib openbabel ocaml facile stdenv jasper fam indilib libnova libarchive dbus bison; - #flac = builtins.getAttr "1.1.2" flacAlts; cdparanoia = cdparanoiaIII; inherit (xlibs) inputproto kbproto scrnsaverproto xextproto xf86miscproto diff --git a/pkgs/top-level/builder-defs.nix b/pkgs/top-level/builder-defs.nix index 4efed47f6d0..2012278ffd7 100644 --- a/pkgs/top-level/builder-defs.nix +++ b/pkgs/top-level/builder-defs.nix @@ -12,6 +12,8 @@ args: with args; with stringsWithDeps; with lib; "README" "AUTHORS" "ChangeLog" "CHANGES" "LICENCE" "COPYRIGHT"] ++ (optional (getAttr ["forceCopyDoc"] true args) "doc"); + hasSuffixHack = a: b: hasSuffix (a+(substring 0 0 b)) ((substring 0 0 a)+b); + archiveType = s: (if hasSuffixHack ".tar" s then "tar" else if (hasSuffixHack ".tar.gz" s) || (hasSuffixHack ".tgz" s) then "tgz"