From a086858ef51eb564a378424b21f677428a34fc9e Mon Sep 17 00:00:00 2001 From: zimbatm Date: Mon, 29 Feb 2016 20:20:31 +0000 Subject: [PATCH 1/4] Remove lib.deepSeqList and lib.deepSeqAttrs Both functions are broken and unused in the repo. --- lib/attrsets.nix | 2 +- lib/lists.nix | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/attrsets.nix b/lib/attrsets.nix index 84f6cb3658b..f5184af4230 100644 --- a/lib/attrsets.nix +++ b/lib/attrsets.nix @@ -364,5 +364,5 @@ rec { overrideExisting = old: new: old // listToAttrs (map (attr: nameValuePair attr (attrByPath [attr] old.${attr} new)) (attrNames old)); - deepSeqAttrs = x: y: deepSeqList (attrValues x) y; + deepSeqAttrs = throw "removed 2016-02-29 because unused and broken"; } diff --git a/lib/lists.nix b/lib/lists.nix index 3bcf366f0c2..9e4ab9e2d95 100644 --- a/lib/lists.nix +++ b/lib/lists.nix @@ -246,9 +246,6 @@ rec { init = list: assert list != []; take (length list - 1) list; - deepSeqList = xs: y: if any (x: deepSeq x false) xs then y else y; - - crossLists = f: foldl (fs: args: concatMap (f: map f args) fs) [f]; @@ -270,4 +267,7 @@ rec { # Subtracts list 'e' from another list. O(nm) complexity. subtractLists = e: filter (x: !(elem x e)); + deepSeqList = throw "removed 2016-02-29 because unused and broken"; + + } From 523e3283189710a9a58bd2828f45a7413f3ede3e Mon Sep 17 00:00:00 2001 From: zimbatm Date: Sun, 28 Feb 2016 21:49:42 +0000 Subject: [PATCH 2/4] lib/attrsets: document all the functions --- lib/attrsets.nix | 137 +++++++++++++++++++++++++++++++++++++---------- 1 file changed, 109 insertions(+), 28 deletions(-) diff --git a/lib/attrsets.nix b/lib/attrsets.nix index f5184af4230..4161fa546c8 100644 --- a/lib/attrsets.nix +++ b/lib/attrsets.nix @@ -12,9 +12,15 @@ rec { inherit (builtins) attrNames listToAttrs hasAttr isAttrs getAttr; - /* 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. */ + /* Return an attribute from nested attribute sets. + + Example: + x = { a = { b = 3; }; } + attrByPath ["a" "b"] 6 x + => 3 + attrByPath ["z" "z"] 6 x + => 6 + */ attrByPath = attrPath: default: e: let attr = head attrPath; in @@ -24,8 +30,15 @@ rec { else default; /* Return if an attribute from nested attribute set exists. - For instance ["x" "y"] applied to some set e returns true, if e.x.y exists. False - is returned otherwise. */ + + Example: + x = { a = { b = 3; }; } + hasAttrByPath ["a" "b"] x + => true + hasAttrByPath ["z" "z"] x + => false + + */ hasAttrByPath = attrPath: e: let attr = head attrPath; in @@ -35,14 +48,28 @@ rec { else false; - /* Return nested attribute set in which an attribute is set. For instance - ["x" "y"] applied with some value v returns `x.y = v;' */ + /* Return nested attribute set in which an attribute is set. + + Example: + setAttrByPath ["a" "b"] 3 + => { a = { b = 3; }; } + */ setAttrByPath = attrPath: value: if attrPath == [] then value else listToAttrs [ { name = head attrPath; value = setAttrByPath (tail attrPath) value; } ]; + /* Like `getAttrPath' without a default value. If it doesn't find the + path it will throw. + + Example: + x = { a = { b = 3; }; } + getAttrFromPath ["a" "b"] x + => 3 + getAttrFromPath ["z" "z"] x + => error: cannot find attribute `z.z' + */ getAttrFromPath = attrPath: set: let errorMsg = "cannot find attribute `" + concatStringsSep "." attrPath + "'"; in attrByPath attrPath (abort errorMsg) set; @@ -109,9 +136,11 @@ rec { ) (attrNames set) ); - /* foldAttrs: apply fold functions to values grouped by key. Eg accumulate values as list: - foldAttrs (n: a: [n] ++ a) [] [{ a = 2; } { a = 3; }] - => { a = [ 2 3 ]; } + /* Apply fold functions to values grouped by key. + + Example: + foldAttrs (n: a: [n] ++ a) [] [{ a = 2; } { a = 3; }] + => { a = [ 2 3 ]; } */ foldAttrs = op: nul: list_of_attrs: fold (n: a: @@ -147,7 +176,12 @@ rec { /* Utility function that creates a {name, value} pair as expected by - builtins.listToAttrs. */ + builtins.listToAttrs. + + Example: + nameValuePair "some" 6 + => { name = "some"; value = 6; } + */ nameValuePair = name: value: { inherit name value; }; @@ -248,11 +282,19 @@ rec { listToAttrs (map (n: nameValuePair n (f n)) names); - /* Check whether the argument is a derivation. */ + /* Check whether the argument is a derivation. Any set with + { type = "derivation"; } counts as a derivation. + + Example: + nixpkgs = import {} + isDerivation nixpkgs.ruby + => true + isDerivation "foobar" + => false + */ isDerivation = x: isAttrs x && x ? type && x.type == "derivation"; - - /* Convert a store path to a fake derivation. */ + /* Converts a store path to a fake derivation. */ toDerivation = path: let path' = builtins.storePath path; in { type = "derivation"; @@ -262,32 +304,49 @@ rec { }; - /* If the Boolean `cond' is true, return the attribute set `as', - otherwise an empty attribute set. */ + /* If `cond' is true, return the attribute set `as', + otherwise an empty attribute set. + + Example: + optionalAttrs (true) { my = "set"; } + => { my = "set"; } + optionalAttrs (false) { my = "set"; } + => { } + */ optionalAttrs = cond: as: if cond then as else {}; /* Merge sets of attributes and use the function f to merge attributes - values. */ + values. + + Example: + zipAttrsWithNames ["a"] (name: vs: vs) [{a = "x";} {a = "y"; b = "z";}] + => { a = ["x" "y"]; } + */ zipAttrsWithNames = names: f: sets: listToAttrs (map (name: { inherit name; value = f name (catAttrs name sets); }) names); - # implentation note: Common names appear multiple times in the list of - # names, hopefully this does not affect the system because the maximal - # laziness avoid computing twice the same expression and listToAttrs does - # not care about duplicated attribute names. + /* Implentation note: Common names appear multiple times in the list of + names, hopefully this does not affect the system because the maximal + laziness avoid computing twice the same expression and listToAttrs does + not care about duplicated attribute names. + + Example: + zipAttrsWith (name: values: values) [{a = "x";} {a = "y"; b = "z";}] + => { a = ["x" "y"]; b = ["z"] } + */ zipAttrsWith = f: sets: zipAttrsWithNames (concatMap attrNames sets) f sets; + /* Like `zipAttrsWith' with `(name: values: value)' as the function. + Example: + zipAttrs [{a = "x";} {a = "y"; b = "z";}] + => { a = ["x" "y"]; b = ["z"] } + */ zipAttrs = zipAttrsWith (name: values: values); - /* backward compatibility */ - zipWithNames = zipAttrsWithNames; - zip = builtins.trace "lib.zip is deprecated, use lib.zipAttrsWith instead" zipAttrsWith; - - /* Does the same as the update operator '//' except that attributes are merged until the given pedicate is verified. The predicate should accept 3 arguments which are the path to reach the attribute, a part of @@ -351,6 +410,15 @@ rec { !(isAttrs lhs && isAttrs rhs) ) lhs rhs; + /* Returns true if the pattern is contained in the set. False otherwise. + + FIXME(zimbatm): this example doesn't work !!! + + Example: + sys = mkSystem { } + matchAttrs { cpu = { bits = 64; }; } sys + => true + */ matchAttrs = pattern: attrs: fold or false (attrValues (zipAttrsWithNames (attrNames pattern) (n: values: let pat = head values; val = head (tail values); in @@ -359,10 +427,23 @@ rec { else pat == val ) [pattern attrs])); - # override only the attributes that are already present in the old set - # useful for deep-overriding + /* Override only the attributes that are already present in the old set + useful for deep-overriding. + + Example: + x = { a = { b = 4; c = 3; }; } + overrideExisting x { a = { b = 6; d = 2; }; } + => { a = { b = 6; d = 2; }; } + */ overrideExisting = old: new: old // listToAttrs (map (attr: nameValuePair attr (attrByPath [attr] old.${attr} new)) (attrNames old)); + + /*** deprecated stuff ***/ + deepSeqAttrs = throw "removed 2016-02-29 because unused and broken"; + zipWithNames = zipAttrsWithNames; + zip = builtins.trace + "lib.zip is deprecated, use lib.zipAttrsWith instead" zipAttrsWith; + } From c71e2d42359f9900ea2c290d141c0d606471da16 Mon Sep 17 00:00:00 2001 From: zimbatm Date: Sun, 28 Feb 2016 23:27:06 +0000 Subject: [PATCH 3/4] lib/lists: document all functions --- lib/lists.nix | 268 ++++++++++++++++++++++++++++++++++++++++---------- 1 file changed, 215 insertions(+), 53 deletions(-) diff --git a/lib/lists.nix b/lib/lists.nix index 9e4ab9e2d95..deb7dcfde42 100644 --- a/lib/lists.nix +++ b/lib/lists.nix @@ -6,17 +6,26 @@ rec { inherit (builtins) head tail length isList elemAt concatLists filter elem genList; + /* Create a list consisting of a single element. `singleton x' is + sometimes more convenient with respect to indentation than `[x]' + when x spans multiple lines. - # Create a list consisting of a single element. `singleton x' is - # sometimes more convenient with respect to indentation than `[x]' - # when x spans multiple lines. + Example: + singleton "foo" + => [ "foo" ] + */ singleton = x: [x]; + /* "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" 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). + Example: + concat = fold (a: b: a + b) "z" + concat [ "a" "b" "c" ] + => "abcnul" + */ fold = op: nul: list: let len = length list; @@ -26,8 +35,14 @@ rec { else op (elemAt list n) (fold' (n + 1)); in fold' 0; - # Left fold: `fold op nul [x_1 x_2 ... x_n] == op (... (op (op nul - # x_1) x_2) ... x_n)'. + /* Left fold: `fold op nul [x_1 x_2 ... x_n] == op (... (op (op nul + x_1) x_2) ... x_n)'. + + Example: + lconcat = foldl (a: b: a + b) "z" + lconcat [ "a" "b" "c" ] + => "zabc" + */ foldl = op: nul: list: let len = length list; @@ -37,13 +52,22 @@ rec { else op (foldl' (n - 1)) (elemAt list n); in foldl' (length list - 1); + /* Strict version of foldl. - # Strict version of foldl. + The difference is that evaluation is forced upon access. Usually used + with small whole results (in contract with lazily-generated list or large + lists where only a part is consumed.) + */ foldl' = builtins.foldl' or foldl; + /* Map with index - # Map with index: `imap (i: v: "${v}-${toString i}") ["a" "b"] == - # ["a-1" "b-2"]'. FIXME: why does this start to count at 1? + FIXME(zimbatm): why does this start to count at 1? + + Example: + imap (i: v: "${v}-${toString i}") ["a" "b"] + => [ "a-1" "b-2" ] + */ imap = if builtins ? genList then f: list: genList (n: f (n + 1) (elemAt list n)) (length list) @@ -57,73 +81,141 @@ rec { else [ (f (n + 1) (elemAt list n)) ] ++ imap' (n + 1); in imap' 0; + /* Map and concatenate the result. - # Map and concatenate the result. + Example: + concatMap (x: [x] ++ ["z"]) ["a" "b"] + => [ "a" "z" "b" "z" ] + */ 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. - # 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]'. + Example: + flatten [1 [2 [3] 4] 5] + => [1 2 3 4 5] + flatten 1 + => [1] + */ flatten = x: if isList x then foldl' (x: y: x ++ (flatten y)) [] x else [x]; + /* Remove elements equal to 'e' from a list. Useful for buildInputs. - # Remove elements equal to 'e' from a list. Useful for buildInputs. + Example: + remove 3 [ 1 3 4 3 ] + => [ 1 4 ] + */ remove = e: filter (x: x != e); + /* 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. - # 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. + Example: + findSingle (x: x == 3) "none" "multiple" [ 1 3 3 ] + => "multiple" + findSingle (x: x == 3) "none" "multiple" [ 1 3 ] + => 3 + findSingle (x: x == 3) "none" "multiple" [ 1 9 ] + => "none" + */ findSingle = pred: default: multiple: list: let found = filter pred list; len = length found; in if len == 0 then default else if len != 1 then multiple else head found; + /* Find the first element in the list matching the specified + predicate or returns `default' if no such element exists. - # Find the first element in the list matching the specified - # predicate or returns `default' if no such element exists. + Example: + findFirst (x: x > 3) 7 [ 1 6 4 ] + => 6 + findFirst (x: x > 9) 7 [ 1 6 4 ] + => 7 + */ findFirst = pred: default: list: let found = filter pred list; in if found == [] then default else head found; + /* Return true iff function `pred' returns true for at least element + of `list'. - # Return true iff function `pred' returns true for at least element - # of `list'. + Example: + any isString [ 1 "a" { } ] + => true + any isString [ 1 { } ] + => false + */ any = builtins.any or (pred: fold (x: y: if pred x then true else y) false); + /* Return true iff function `pred' returns true for all elements of + `list'. - # Return true iff function `pred' returns true for all elements of - # `list'. + Example: + all (x: x < 3) [ 1 2 ] + => true + all (x: x < 3) [ 1 2 3 ] + => false + */ all = builtins.all or (pred: fold (x: y: if pred x then y else false) true); + /* Count how many times function `pred' returns true for the elements + of `list'. - # Count how many times function `pred' returns true for the elements - # of `list'. + Example: + count (x: x == 3) [ 3 2 3 4 6 ] + => 2 + */ count = pred: foldl' (c: x: if pred x then c + 1 else c) 0; + /* 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'). - # 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'). + Example: + optional true "foo" + => [ "foo" ] + optional false "foo" + => [ ] + */ optional = cond: elem: if cond then [elem] else []; + /* Return a list or an empty list, dependening on a boolean value. - # Return a list or an empty list, dependening on a boolean value. + Example: + optionals true [ 2 3 ] + => [ 2 3 ] + optionals false [ 2 3 ] + => [ ] + */ 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. + /* 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. + + Example: + toList [ 1 2 ] + => [ 1 2 ] + toList "hi" + => [ "hi "] + */ toList = x: if isList x then x else [x]; + /* Return a list of integers from `first' up to and including `last'. - # Return a list of integers from `first' up to and including `last'. + Example: + range 2 4 + => [ 2 3 4 ] + range 3 2 + => [ ] + */ range = if builtins ? genList then first: last: @@ -136,9 +228,13 @@ rec { then [] else [first] ++ range (first + 1) last; + /* Splits the elements of a list in two lists, `right' and + `wrong', depending on the evaluation of a predicate. - # Partition the elements of a list in two lists, `right' and - # `wrong', depending on the evaluation of a predicate. + Example: + partition (x: x > 2) [ 5 1 2 3 4 ] + => { right = [ 5 3 4 ]; wrong = [ 1 2 ]; } + */ partition = pred: fold (h: t: if pred h @@ -146,7 +242,14 @@ rec { else { right = t.right; wrong = [h] ++ t.wrong; } ) { right = []; wrong = []; }; + /* Merges two lists of the same size together. If the sizes aren't the same + the merging stops at the shortest. How both lists are merged is defined + by the first argument. + Example: + zipListsWith (a: b: a + b) ["h" "l"] ["e" "o"] + => ["he" "lo"] + */ zipListsWith = if builtins ? genList then f: fst: snd: genList (n: f (elemAt fst n) (elemAt snd n)) (min (length fst) (length snd)) @@ -161,21 +264,37 @@ rec { else []; in zipListsWith' 0; + /* Merges two lists of the same size together. If the sizes aren't the same + the merging stops at the shortest. + + Example: + zipLists [ 1 2 ] [ "a" "b" ] + => [ { fst = 1; snd = "a"; } { fst = 2; snd = "b"; } ] + */ zipLists = zipListsWith (fst: snd: { inherit fst snd; }); + /* Reverse the order of the elements of a list. - # Reverse the order of the elements of a list. + Example: + + reverseList [ "b" "o" "j" ] + => [ "j" "o" "b" ] + */ reverseList = if builtins ? genList then xs: let l = length xs; in genList (n: elemAt xs (l - n - 1)) l else fold (e: acc: acc ++ [ e ]) []; + /* Sort a list based on a comparator function which compares two + elements and returns true if the first argument is strictly below + the second argument. The returned list is sorted in an increasing + order. The implementation does a quick-sort. - # Sort a list based on a comparator function which compares two - # elements and returns true if the first argument is strictly below - # the second argument. The returned list is sorted in an increasing - # order. The implementation does a quick-sort. + Example: + sort (a: b: a < b) [ 5 3 7 ] + => [ 3 5 7 ] + */ sort = builtins.sort or ( strictLess: list: let @@ -193,8 +312,14 @@ rec { if len < 2 then list else (sort strictLess pivot.left) ++ [ first ] ++ (sort strictLess pivot.right)); + /* Return the first (at most) N elements of a list. - # Return the first (at most) N elements of a list. + Example: + take 2 [ "a" "b" "c" "d" ] + => [ "a" "b" ] + take 2 [ ] + => [ ] + */ take = if builtins ? genList then count: sublist 0 count @@ -209,8 +334,14 @@ rec { [ (elemAt list n) ] ++ take' (n + 1); in take' 0; + /* Remove the first (at most) N elements of a list. - # Remove the first (at most) N elements of a list. + Example: + drop 2 [ "a" "b" "c" "d" ] + => [ "c" "d" ] + drop 2 [ ] + => [ ] + */ drop = if builtins ? genList then count: list: sublist count (length list) list @@ -225,9 +356,15 @@ rec { drop' (n - 1) ++ [ (elemAt list n) ]; in drop' (len - 1); + /* Return a list consisting of at most ‘count’ elements of ‘list’, + starting at index ‘start’. - # Return a list consisting of at most ‘count’ elements of ‘list’, - # starting at index ‘start’. + Example: + sublist 1 3 [ "a" "b" "c" "d" "e" ] + => [ "b" "c" "d" ] + sublist 1 3 [ ] + => [ ] + */ sublist = start: count: list: let len = length list; in genList @@ -236,20 +373,36 @@ rec { else if start + count > len then len - start else count); + /* Return the last element of a list. - # Return the last element of a list. + Example: + last [ 1 2 3 ] + => 3 + */ last = list: assert list != []; elemAt list (length list - 1); + /* Return all elements but the last - # Return all elements but the last + Example: + init [ 1 2 3 ] + => [ 1 2 ] + */ init = list: assert list != []; take (length list - 1) list; + /* FIXME(zimbatm) Not used anywhere + */ crossLists = f: foldl (fs: args: concatMap (f: map f args) fs) [f]; - # Remove duplicate elements from the list. O(n^2) complexity. + /* Remove duplicate elements from the list. O(n^2) complexity. + + Example: + + unique [ 3 2 3 4 ] + => [ 3 2 4 ] + */ unique = list: if list == [] then [] @@ -259,15 +412,24 @@ rec { xs = unique (drop 1 list); in [x] ++ remove x xs; + /* Intersects list 'e' and another list. O(nm) complexity. - # Intersects list 'e' and another list. O(nm) complexity. + Example: + intersectLists [ 1 2 3 ] [ 6 3 2 ] + => [ 3 2 ] + */ intersectLists = e: filter (x: elem x e); + /* Subtracts list 'e' from another list. O(nm) complexity. - # Subtracts list 'e' from another list. O(nm) complexity. + Example: + subtractLists [ 3 2 ] [ 1 2 3 4 5 3 ] + => [ 1 4 5 ] + */ subtractLists = e: filter (x: !(elem x e)); + /*** deprecated stuff ***/ + deepSeqList = throw "removed 2016-02-29 because unused and broken"; - } From 7883ca774b1fe8b59c455053d434b92d7b65d3d5 Mon Sep 17 00:00:00 2001 From: zimbatm Date: Sun, 28 Feb 2016 23:27:35 +0000 Subject: [PATCH 4/4] lib/strings: document all the functions --- lib/strings.nix | 334 +++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 273 insertions(+), 61 deletions(-) diff --git a/lib/strings.nix b/lib/strings.nix index fc6c2152b9f..a2a4be11e1b 100644 --- a/lib/strings.nix +++ b/lib/strings.nix @@ -10,65 +10,147 @@ rec { inherit (builtins) stringLength substring head tail isString replaceStrings; + /* Concatenate a list of strings. - # Concatenate a list of strings. + Example: + concatStrings ["foo" "bar"] + => "foobar" + */ concatStrings = if builtins ? concatStringsSep then builtins.concatStringsSep "" else lib.foldl' (x: y: x + y) ""; + /* Map a function over a list and concatenate the resulting strings. - # Map a function over a list and concatenate the resulting strings. + Example: + concatMapStrings (x: "a" + x) ["foo" "bar"] + => "afooabar" + */ concatMapStrings = f: list: concatStrings (map f list); + + /* Like `concatMapStrings' except that the f functions also gets the + position as a parameter. + + Example: + concatImapStrings (pos: x: "${toString pos}-${x}") ["foo" "bar"] + => "1-foo2-bar" + */ concatImapStrings = f: list: concatStrings (lib.imap f list); + /* Place an element between each element of a list - # Place an element between each element of a list, e.g., - # `intersperse "," ["a" "b" "c"]' returns ["a" "," "b" "," "c"]. + Example: + intersperse "/" ["usr" "local" "bin"] + => ["usr" "/" "local" "/" "bin"]. + */ intersperse = separator: list: if list == [] || length list == 1 then list else tail (lib.concatMap (x: [separator x]) list); + /* Concatenate a list of strings with a separator between each element - # Concatenate a list of strings with a separator between each element, e.g. - # concatStringsSep " " ["foo" "bar" "xyzzy"] == "foo bar xyzzy" + Example: + concatStringsSep "/" ["usr" "local" "bin"] + => "usr/local/bin" + */ concatStringsSep = builtins.concatStringsSep or (separator: list: concatStrings (intersperse separator list)); + /* First maps over the list and then concatenates it. + + Example: + concatMapStringsSep "-" (x: toUpper x) ["foo" "bar" "baz"] + => "FOO-BAR-BAZ" + */ concatMapStringsSep = sep: f: list: concatStringsSep sep (map f list); + + /* First imaps over the list and then concatenates it. + + Example: + + concatImapStringsSep "-" (pos: x: toString (x / pos)) [ 6 6 6 ] + => "6-3-2" + */ concatImapStringsSep = sep: f: list: concatStringsSep sep (lib.imap f list); + /* Construct a Unix-style search path consisting of each `subDir" + directory of the given list of packages. - # Construct a Unix-style search path consisting of each `subDir" - # directory of the given list of packages. For example, - # `makeSearchPath "bin" ["x" "y" "z"]' returns "x/bin:y/bin:z/bin". + Example: + makeSearchPath "bin" ["/root" "/usr" "/usr/local"] + => "/root/bin:/usr/bin:/usr/local/bin" + makeSearchPath "bin" ["/"] + => "//bin" + */ makeSearchPath = subDir: packages: concatStringsSep ":" (map (path: path + "/" + subDir) packages); + /* Construct a library search path (such as RPATH) containing the + libraries for a set of packages - # Construct a library search path (such as RPATH) containing the - # libraries for a set of packages, e.g. "${pkg1}/lib:${pkg2}/lib:...". + Example: + makeLibraryPath [ "/usr" "/usr/local" ] + => "/usr/lib:/usr/local/lib" + pkgs = import { } + makeLibraryPath [ pkgs.openssl pkgs.zlib ] + => "/nix/store/9rz8gxhzf8sw4kf2j2f1grr49w8zx5vj-openssl-1.0.1r/lib:/nix/store/wwh7mhwh269sfjkm6k5665b5kgp7jrk2-zlib-1.2.8/lib" + */ makeLibraryPath = makeSearchPath "lib"; - # Construct a binary search path (such as $PATH) containing the - # binaries for a set of packages, e.g. "${pkg1}/bin:${pkg2}/bin:...". + /* Construct a binary search path (such as $PATH) containing the + binaries for a set of packages. + + Example: + makeBinPath ["/root" "/usr" "/usr/local"] + => "/root/bin:/usr/bin:/usr/local/bin" + */ makeBinPath = makeSearchPath "bin"; - # Idem for Perl search paths. + /* Construct a perl search path (such as $PERL5LIB) + + FIXME(zimbatm): this should be moved in perl-specific code + + Example: + pkgs = import { } + makePerlPath [ pkgs.perlPackages.NetSMTP ] + => "/nix/store/n0m1fk9c960d8wlrs62sncnadygqqc6y-perl-Net-SMTP-1.25/lib/perl5/site_perl" + */ makePerlPath = makeSearchPath "lib/perl5/site_perl"; + /* Dependening on the boolean `cond', return either the given string + or the empty string. Useful to contatenate against a bigger string. - # Dependening on the boolean `cond', return either the given string - # or the empty string. + Example: + optionalString true "some-string" + => "some-string" + optionalString false "some-string" + => "" + */ optionalString = cond: string: if cond then string else ""; + /* Determine whether a string has given prefix. - # Determine whether a string has given prefix/suffix. + Example: + hasPrefix "foo" "foobar" + => true + hasPrefix "foo" "barfoo" + => false + */ hasPrefix = pref: str: substring 0 (stringLength pref) str == pref; + + /* Determine whether a string has given suffix. + + Example: + hasSuffix "foo" "foobar" + => false + hasSuffix "foo" "barfoo" + => true + */ hasSuffix = suff: str: let lenStr = stringLength str; @@ -76,36 +158,55 @@ rec { in lenStr >= lenSuff && substring (lenStr - lenSuff) lenStr str == suff; + /* Convert a string to a list of characters (i.e. singleton strings). + 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. + Also note that Nix treats strings as a list of bytes and thus doesn't + handle unicode. - # 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. + Example: + stringToCharacters "" + => [ ] + stringToCharacters "abc" + => [ "a" "b" "c" ] + stringToCharacters "💩" + => [ "�" "�" "�" "�" ] + */ stringToCharacters = s: map (p: substring p 1 s) (lib.range 0 (stringLength s - 1)); + /* Manipulate a string character by character and replace them by + strings before concatenating the results. - # Manipulate a string charactter by character and replace them by - # strings before concatenating the results. + Example: + stringAsChars (x: if x == "a" then "i" else x) "nax" + => "nix" + */ stringAsChars = f: s: concatStrings ( map f (stringToCharacters s) ); + /* Escape occurrence of the elements of ‘list’ in ‘string’ by + prefixing it with a backslash. - # Escape occurrence of the elements of ‘list’ in ‘string’ by - # prefixing it with a backslash. For example, ‘escape ["(" ")"] - # "(foo)"’ returns the string ‘\(foo\)’. + Example: + escape ["(" ")"] "(foo)" + => "\\(foo\\)" + */ escape = list: replaceChars list (map (c: "\\${c}") list); + /* Escape all characters that have special meaning in the Bourne shell. - # Escape all characters that have special meaning in the Bourne shell. + Example: + escapeShellArg "so([<>])me" + => "so\\(\\[\\<\\>\\]\\)me" + */ escapeShellArg = lib.escape (stringToCharacters "\\ ';$`()|<>\t*[]"); - - # Obsolete - use replaceStrings instead. + /* Obsolete - use replaceStrings instead. */ replaceChars = builtins.replaceStrings or ( del: new: s: let @@ -119,21 +220,52 @@ rec { in stringAsChars subst s); - # Case conversion utilities. lowerChars = stringToCharacters "abcdefghijklmnopqrstuvwxyz"; upperChars = stringToCharacters "ABCDEFGHIJKLMNOPQRSTUVWXYZ"; + + /* Converts an ASCII string to lower-case. + + Example: + toLower "HOME" + => "home" + */ toLower = replaceChars upperChars lowerChars; + + /* Converts an ASCII string to upper-case. + + Example: + toLower "home" + => "HOME" + */ toUpper = replaceChars lowerChars upperChars; + /* Appends string context from another string. This is an implementation + detail of Nix. - # Appends string context from another string. + Strings in Nix carry an invisible `context' which is a list of strings + representing store paths. If the string is later used in a derivation + attribute, the derivation will properly populate the inputDrvs and + inputSrcs. + + Example: + pkgs = import { }; + addContextFrom pkgs.coreutils "bar" + => "bar" + */ addContextFrom = a: b: substring 0 0 a + b; + /* Cut a string with a separator and produces a list of strings which + were separated by this separator. - # Cut a string with a separator and produces a list of strings which - # were separated by this separator; e.g., `splitString "." - # "foo.bar.baz"' returns ["foo" "bar" "baz"]. + NOTE: this function is not performant and should be avoided + + Example: + splitString "." "foo.bar.baz" + => [ "foo" "bar" "baz" ] + splitString "/" "/usr/local/bin" + => [ "" "usr" "local" "bin" ] + */ splitString = _sep: _s: let sep = addContextFrom _s _sep; @@ -157,10 +289,15 @@ rec { in recurse 0 0; + /* Return the suffix of the second argument if the first argument matches + its prefix. - # return the suffix of the second argument if the first argument match its - # prefix. e.g., - # `removePrefix "foo." "foo.bar.baz"' returns "bar.baz". + Example: + removePrefix "foo." "foo.bar.baz" + => "bar.baz" + removePrefix "xxx" "foo.bar.baz" + => "foo.bar.baz" + */ removePrefix = pre: s: let preLen = stringLength pre; @@ -171,6 +308,15 @@ rec { else s; + /* Return the prefix of the second argument if the first argument matches + its suffix. + + Example: + removeSuffix "front" "homefront" + => "home" + removeSuffix "xxx" "homefront" + => "homefront" + */ removeSuffix = suf: s: let sufLen = stringLength suf; @@ -181,25 +327,49 @@ rec { else s; - # Return true iff string v1 denotes a version older than v2. + /* Return true iff string v1 denotes a version older than v2. + + Example: + versionOlder "1.1" "1.2" + => true + versionOlder "1.1" "1.1" + => false + */ versionOlder = v1: v2: builtins.compareVersions v2 v1 == 1; + /* Return true iff string v1 denotes a version equal to or newer than v2. - # Return true iff string v1 denotes a version equal to or newer than v2. + Example: + versionAtLeast "1.1" "1.0" + => true + versionAtLeast "1.1" "1.1" + => true + versionAtLeast "1.1" "1.2" + => false + */ versionAtLeast = v1: v2: !versionOlder v1 v2; + /* This function takes an argument that's either a derivation or a + derivation's "name" attribute and extracts the version part from that + argument. - # This function takes an argument that's either a derivation or a - # derivation's "name" attribute and extracts the version part from that - # argument. For example: - # - # lib.getVersion "youtube-dl-2016.01.01" ==> "2016.01.01" - # lib.getVersion pkgs.youtube-dl ==> "2016.01.01" + Example: + getVersion "youtube-dl-2016.01.01" + => "2016.01.01" + getVersion pkgs.youtube-dl + => "2016.01.01" + */ getVersion = x: (builtins.parseDrvName (x.name or x)).version; + /* Extract name with version from URL. Ask for separator which is + supposed to start extension. - # Extract name with version from URL. Ask for separator which is - # supposed to start extension. + Example: + nameFromURL "https://nixos.org/releases/nix/nix-1.7/nix-1.7-x86_64-linux.tar.bz2" "-" + => "nix" + nameFromURL "https://nixos.org/releases/nix/nix-1.7/nix-1.7-x86_64-linux.tar.bz2" "_" + => "nix-1.7-x86" + */ nameFromURL = url: sep: let components = splitString "/" url; @@ -207,14 +377,24 @@ rec { name = builtins.head (splitString sep filename); in assert name != filename; name; + /* Create an --{enable,disable}- string that can be passed to + standard GNU Autoconf scripts. - # Create an --{enable,disable}- string that can be passed to - # standard GNU Autoconf scripts. + Example: + enableFeature true "shared" + => "--enable-shared" + enableFeature false "shared" + => "--disable-shared" + */ enableFeature = enable: feat: "--${if enable then "enable" else "disable"}-${feat}"; + /* Create a fixed width string with additional prefix to match + required width. - # Create a fixed width string with additional prefix to match - # required width. + Example: + fixedWidthString 5 "0" (toString 15) + => "00015" + */ fixedWidthString = width: filler: str: let strw = lib.stringLength str; @@ -223,25 +403,58 @@ rec { assert strw <= width; if strw == width then str else filler + fixedWidthString reqWidth filler str; + /* Format a number adding leading zeroes up to fixed width. - # Format a number adding leading zeroes up to fixed width. + Example: + fixedWidthNumber 5 15 + => "00015" + */ fixedWidthNumber = width: n: fixedWidthString width "0" (toString n); + /* Check whether a value is a store path. - # Check whether a value is a store path. + Example: + isStorePath "/nix/store/d945ibfx9x185xf04b890y4f9g3cbb63-python-2.7.11/bin/python" + => false + isStorePath "/nix/store/d945ibfx9x185xf04b890y4f9g3cbb63-python-2.7.11/" + => true + isStorePath pkgs.python + => true + */ isStorePath = x: builtins.substring 0 1 (toString x) == "/" && dirOf (builtins.toPath x) == builtins.storeDir; - # Convert string to int - # Obviously, it is a bit hacky to use fromJSON that way. + /* Convert string to int + Obviously, it is a bit hacky to use fromJSON that way. + + Example: + toInt "1337" + => 1337 + toInt "-4" + => -4 + toInt "3.14" + => error: floating point JSON numbers are not supported + */ toInt = str: let may_be_int = builtins.fromJSON str; in if builtins.isInt may_be_int then may_be_int else throw "Could not convert ${str} to int."; - # Read a list of paths from `file', relative to the `rootPath'. Lines - # beginning with `#' are treated as comments and ignored. Whitespace - # is significant. + /* Read a list of paths from `file', relative to the `rootPath'. Lines + beginning with `#' are treated as comments and ignored. Whitespace + is significant. + + NOTE: this function is not performant and should be avoided + + Example: + readPathsFromFile /prefix + ./pkgs/development/libraries/qt-5/5.4/qtbase/series + => [ "/prefix/dlopen-resolv.patch" "/prefix/tzdir.patch" + "/prefix/dlopen-libXcursor.patch" "/prefix/dlopen-openssl.patch" + "/prefix/dlopen-dbus.patch" "/prefix/xdg-config-dirs.patch" + "/prefix/nix-profiles-library-paths.patch" + "/prefix/compose-search-path.patch" ] + */ readPathsFromFile = rootPath: file: let root = toString rootPath; @@ -253,5 +466,4 @@ rec { absolutePaths = builtins.map (path: builtins.toPath (root + "/" + path)) relativePaths; in absolutePaths; - }