about summary refs log tree commit diff
path: root/lib
diff options
context:
space:
mode:
Diffstat (limited to 'lib')
-rw-r--r--lib/attrsets.nix329
-rw-r--r--lib/composable-derivation.nix54
-rw-r--r--lib/customisation.nix116
-rw-r--r--lib/debug.nix119
-rw-r--r--lib/default.nix31
-rw-r--r--lib/licenses.nix235
-rw-r--r--lib/lists.nix235
-rw-r--r--lib/maintainers.nix65
-rw-r--r--lib/meta.nix48
-rw-r--r--lib/misc.nix431
-rw-r--r--lib/modules.nix380
-rw-r--r--lib/options.nix315
-rw-r--r--lib/platforms.nix16
-rw-r--r--lib/properties.nix464
-rw-r--r--lib/sources.nix29
-rw-r--r--lib/strings-with-deps.nix78
-rw-r--r--lib/strings.nix190
-rw-r--r--lib/systems.nix126
-rw-r--r--lib/tests.nix113
-rw-r--r--lib/trivial.nix38
-rw-r--r--lib/types.nix226
21 files changed, 3638 insertions, 0 deletions
diff --git a/lib/attrsets.nix b/lib/attrsets.nix
new file mode 100644
index 000000000000..01d51779c809
--- /dev/null
+++ b/lib/attrsets.nix
@@ -0,0 +1,329 @@
+# Operations on attribute sets.
+
+with {
+  inherit (builtins) head tail isString;
+  inherit (import ./trivial.nix) or;
+  inherit (import ./default.nix) fold;
+  inherit (import ./strings.nix) concatStringsSep;
+  inherit (import ./lists.nix) concatMap concatLists all deepSeqList;
+  inherit (import ./misc.nix) maybeAttr;
+};
+
+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. */
+  attrByPath = attrPath: default: e:
+    let attr = head attrPath;
+    in
+      if attrPath == [] then e
+      else if builtins ? hasAttr && hasAttr attr e
+      then attrByPath (tail attrPath) default (getAttr attr e)
+      else default;
+
+
+  /* Return nested attribute set in which an attribute is set.  For instance
+     ["x" "y"] applied with some value v returns `x.y = v;' */
+  setAttrByPath = attrPath: value:
+    if attrPath == [] then value
+    else listToAttrs [(
+      nameValuePair (head attrPath) (setAttrByPath (tail attrPath) value)
+    )];
+
+
+  getAttrFromPath = attrPath: set:
+    let errorMsg = "cannot find attribute `" + concatStringsSep "." attrPath + "'";
+    in attrByPath attrPath (abort errorMsg) set;
+
+
+  /* Return the specified attributes from a set.
+
+     Example:
+       attrVals ["a" "b" "c"] as
+       => [as.a as.b as.c]
+  */
+  attrVals = nameList: set:
+    map (x: getAttr x set) nameList;
+
+
+  /* Return the values of all attributes in the given set, sorted by
+     attribute name.
+
+     Example:
+       attrValues {c = 3; a = 1; b = 2;}
+       => [1 2 3]
+  */
+  attrValues = attrs: attrVals (attrNames attrs) attrs;
+
+
+  /* Collect each attribute named `attr' from a list of attribute
+     sets.  Sets that don't contain the named attribute are ignored.
+
+     Example:
+       catAttrs "a" [{a = 1;} {b = 0;} {a = 2;}]
+       => [1 2]
+  */
+  catAttrs = attr: l: concatLists (map (s: if hasAttr attr s then [(getAttr attr s)] else []) l);
+
+
+  /* Filter an attribute set by removing all attributes for which the
+     given predicate return false.
+
+     Example:
+       filterAttrs (n: v: n == "foo") { foo = 1; bar = 2; }
+       => { foo = 1; }
+  */
+  filterAttrs = pred: set:
+    listToAttrs (fold (n: ys: let v = getAttr n set; in if pred n v then [(nameValuePair n v)] ++ ys else ys) [] (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 ]; }
+  */
+  foldAttrs = op: nul: list_of_attrs:
+    fold (n: a:
+        fold (name: o:
+          o // (listToAttrs [{inherit name; value = op (getAttr name n) (maybeAttr name nul a); }])
+        ) a (attrNames n)
+    ) {} list_of_attrs;
+
+
+  /* Recursively collect sets that verify a given predicate named `pred'
+     from the set `attrs'.  The recursion is stopped when the predicate is
+     verified.
+
+     Type:
+       collect ::
+         (AttrSet -> Bool) -> AttrSet -> AttrSet
+
+     Example:
+       collect builtins.isList { a = { b = ["b"]; }; c = [1]; }
+       => [["b"] [1]]
+
+       collect (x: x ? outPath)
+          { a = { outPath = "a/"; }; b = { outPath = "b/"; }; }
+       => [{ outPath = "a/"; } { outPath = "b/"; }]
+  */
+  collect = pred: attrs:
+    if pred attrs then
+      [ attrs ]
+    else if builtins.isAttrs attrs then
+      concatMap (collect pred) (attrValues attrs)
+    else
+      [];
+
+
+  /* Utility function that creates a {name, value} pair as expected by
+     builtins.listToAttrs. */
+  nameValuePair = name: value: { inherit name value; };
+
+
+  /* Apply a function to each element in an attribute set.  The
+     function takes two arguments --- the attribute name and its value
+     --- and returns the new value for the attribute.  The result is a
+     new attribute set.
+
+     Example:
+       mapAttrs (name: value: name + "-" + value)
+          { x = "foo"; y = "bar"; }
+       => { x = "x-foo"; y = "y-bar"; }
+  */
+  mapAttrs = f: set:
+    listToAttrs (map (attr: nameValuePair attr (f attr (getAttr attr set))) (attrNames set));
+
+
+  /* Like `mapAttrs', but allows the name of each attribute to be
+     changed in addition to the value.  The applied function should
+     return both the new name and value as a `nameValuePair'.
+
+     Example:
+       mapAttrs' (name: value: nameValuePair ("foo_" + name) ("bar-" + value))
+          { x = "a"; y = "b"; }
+       => { foo_x = "bar-a"; foo_y = "bar-b"; }
+  */
+  mapAttrs' = f: set:
+    listToAttrs (map (attr: f attr (getAttr attr set)) (attrNames set));
+
+
+  /* Call a function for each attribute in the given set and return
+     the result in a list.
+
+     Example:
+       mapAttrsToList (name: value: name + value)
+          { x = "a"; y = "b"; }
+       => [ "xa" "yb" ]
+  */
+  mapAttrsToList = f: attrs:
+    map (name: f name (getAttr name attrs)) (attrNames attrs);
+
+
+  /* Like `mapAttrs', except that it recursively applies itself to
+     attribute sets.  Also, the first argument of the argument
+     function is a *list* of the names of the containing attributes.
+
+     Type:
+       mapAttrsRecursive ::
+         ([String] -> a -> b) -> AttrSet -> AttrSet
+
+     Example:
+       mapAttrsRecursive (path: value: concatStringsSep "-" (path ++ [value]))
+         { n = { a = "A"; m = { b = "B"; c = "C"; }; }; d = "D"; }
+       => { n = { a = "n-a-A"; m = { b = "n-m-b-B"; c = "n-m-c-C"; }; }; d = "d-D"; }
+  */
+  mapAttrsRecursive = mapAttrsRecursiveCond (as: true);
+
+
+  /* Like `mapAttrsRecursive', but it takes an additional predicate
+     function that tells it whether to recursive into an attribute
+     set.  If it returns false, `mapAttrsRecursiveCond' does not
+     recurse, but does apply the map function.  It is returns true, it
+     does recurse, and does not apply the map function.
+
+     Type:
+       mapAttrsRecursiveCond ::
+         (AttrSet -> Bool) -> ([String] -> a -> b) -> AttrSet -> AttrSet
+
+     Example:
+       # To prevent recursing into derivations (which are attribute
+       # sets with the attribute "type" equal to "derivation"):
+       mapAttrsRecursiveCond
+         (as: !(as ? "type" && as.type == "derivation"))
+         (x: ... do something ...)
+         attrs
+  */
+  mapAttrsRecursiveCond = cond: f: set:
+    let
+      recurse = path: set:
+        let
+          g =
+            name: value:
+            if isAttrs value && cond value
+              then recurse (path ++ [name]) value
+              else f (path ++ [name]) value;
+        in mapAttrs g set;
+    in recurse [] set;
+
+
+  /* Generate an attribute set by mapping a function over a list of
+     attribute names.
+
+     Example:
+       genAttrs [ "foo" "bar" ] (name: "x_" + name)
+       => { foo = "x_foo"; bar = "x_bar"; }
+  */
+  genAttrs = names: f:
+    listToAttrs (map (n: nameValuePair n (f n)) names);
+
+
+  /* Check whether the argument is a derivation. */
+  isDerivation = x: isAttrs x && x ? type && x.type == "derivation";
+
+
+  /* If the Boolean `cond' is true, return the attribute set `as',
+     otherwise an empty attribute set. */
+  optionalAttrs = cond: as: if cond then as else {};
+
+
+  /* Merge sets of attributes and use the function f to merge attributes
+     values. */
+  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.
+  zipAttrsWith = f: sets: zipWithNames (concatMap attrNames sets) f sets;
+
+  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
+     the first attribute set and a part of the second attribute set.  When
+     the predicate is verified, the value of the first attribute set is
+     replaced by the value of the second attribute set.
+
+     Example:
+       recursiveUpdateUntil (path: l: r: path == ["foo"]) {
+         # first attribute set
+         foo.bar = 1;
+         foo.baz = 2;
+         bar = 3;
+       } {
+         #second attribute set
+         foo.bar = 1;
+         foo.quz = 2;
+         baz = 4;
+       }
+
+       returns: {
+         foo.bar = 1; # 'foo.*' from the second set
+         foo.quz = 2; #
+         bar = 3;     # 'bar' from the first set
+         baz = 4;     # 'baz' from the second set
+       }
+
+     */
+  recursiveUpdateUntil = pred: lhs: rhs:
+    let f = attrPath:
+      zipAttrsWith (n: values:
+        if tail values == []
+        || pred attrPath (head (tail values)) (head values) then
+          head values
+        else
+          f (attrPath ++ [n]) values
+      );
+    in f [] [rhs lhs];
+
+  /* A recursive variant of the update operator ‘//’.  The recusion
+     stops when one of the attribute values is not an attribute set,
+     in which case the right hand side value takes precedence over the
+     left hand side value.
+
+     Example:
+       recursiveUpdate {
+         boot.loader.grub.enable = true;
+         boot.loader.grub.device = "/dev/hda";
+       } {
+         boot.loader.grub.device = "";
+       }
+
+       returns: {
+         boot.loader.grub.enable = true;
+         boot.loader.grub.device = "";
+       }
+
+     */
+  recursiveUpdate = lhs: rhs:
+    recursiveUpdateUntil (path: lhs: rhs:
+      !(isAttrs lhs && isAttrs rhs)
+    ) lhs rhs;
+
+  matchAttrs = pattern: attrs:
+    fold or false (attrValues (zipAttrsWithNames (attrNames pattern) (n: values:
+      let pat = head values; val = head (tail values); in
+      if length values == 1 then false
+      else if isAttrs pat then isAttrs val && matchAttrs head values
+      else pat == val
+    ) [pattern attrs]));
+
+  # override only the attributes that are already present in the old set
+  # useful for deep-overriding
+  overrideExisting = old: new:
+    old // listToAttrs (map (attr: nameValuePair attr (attrByPath [attr] (getAttr attr old) new)) (attrNames old));
+
+  deepSeqAttrs = x: y: deepSeqList (attrValues x) y;
+}
diff --git a/lib/composable-derivation.nix b/lib/composable-derivation.nix
new file mode 100644
index 000000000000..1099bd152bf6
--- /dev/null
+++ b/lib/composable-derivation.nix
@@ -0,0 +1,54 @@
+{lib, pkgs} :
+let inherit (lib) nv nvs; in
+{
+  # see for example:
+  # - development/interpreters/php_configurable/default.nix
+  # - .. search composableDerivation in all-packages.nix ..
+  #
+  # You should be able to override anything you like easily
+  # grep the mailinglist by title "python proposal" (dec 08)
+  # -> http://mail.cs.uu.nl/pipermail/nix-dev/2008-December/001571.html
+  # to see why this got complicated when using all its features
+  # TODO add newer example using new syntax (kernel derivation proposal -> mailinglist)
+  composableDerivation = {
+        mkDerivation ? pkgs.stdenv.mkDerivation,
+
+        # list of functions to be applied before defaultOverridableDelayableArgs removes removeAttrs names
+        # prepareDerivationArgs handles derivation configurations
+        applyPreTidy ? [ lib.prepareDerivationArgs ],
+
+        # consider adding addtional elements by derivation.merge { removeAttrs = ["elem"]; };
+        removeAttrs ? ["cfg" "flags"]
+
+      }: (lib.defaultOverridableDelayableArgs ( a: mkDerivation a) 
+         {
+           inherit applyPreTidy removeAttrs;
+         }).merge;
+
+  # some utility functions
+  # use this function to generate flag attrs for prepareDerivationArgs
+  # E nable  D isable F eature
+  edf = {name, feat ? name, enable ? {}, disable ? {} , value ? ""}:
+    nvs name {
+    set = {
+      configureFlags = ["--enable-${feat}${if value == "" then "" else "="}${value}"];
+    } // enable;
+    unset = {
+      configureFlags = ["--disable-${feat}"];
+    } // disable;
+  };
+
+  # same for --with and --without-
+  # W ith or W ithout F eature
+  wwf = {name, feat ? name, enable ? {}, disable ? {}, value ? ""}:
+    nvs name {
+    set = enable // {
+      configureFlags = ["--with-${feat}${if value == "" then "" else "="}${value}"]
+                       ++ lib.maybeAttr "configureFlags" [] enable;
+    };
+    unset = disable // {
+      configureFlags = ["--without-${feat}"]
+                       ++ lib.maybeAttr "configureFlags" [] disable;
+    };
+  };
+}
diff --git a/lib/customisation.nix b/lib/customisation.nix
new file mode 100644
index 000000000000..bfa61169efb1
--- /dev/null
+++ b/lib/customisation.nix
@@ -0,0 +1,116 @@
+let lib = import ./default.nix;
+    inherit (builtins) getAttr attrNames isFunction;
+
+in
+
+rec {
+
+
+  /* `overrideDerivation drv f' takes a derivation (i.e., the result
+     of a call to the builtin function `derivation') and returns a new
+     derivation in which the attributes of the original are overriden
+     according to the function `f'.  The function `f' is called with
+     the original derivation attributes.
+
+     `overrideDerivation' allows certain "ad-hoc" customisation
+     scenarios (e.g. in ~/.nixpkgs/config.nix).  For instance, if you
+     want to "patch" the derivation returned by a package function in
+     Nixpkgs to build another version than what the function itself
+     provides, you can do something like this:
+
+       mySed = overrideDerivation pkgs.gnused (oldAttrs: {
+         name = "sed-4.2.2-pre";
+         src = fetchurl {
+           url = ftp://alpha.gnu.org/gnu/sed/sed-4.2.2-pre.tar.bz2;
+           sha256 = "11nq06d131y4wmf3drm0yk502d2xc6n5qy82cg88rb9nqd2lj41k";
+         };
+         patches = [];
+       });
+
+     For another application, see build-support/vm, where this
+     function is used to build arbitrary derivations inside a QEMU
+     virtual machine. */
+     
+  overrideDerivation = drv: f:
+    let
+      newDrv = derivation (drv.drvAttrs // (f drv));
+    in addPassthru newDrv (
+      { meta = drv.meta or {};
+        passthru = if drv ? passthru then drv.passthru else {};
+      }
+      //
+      (drv.passthru or {})
+      //
+      (if (drv ? crossDrv && drv ? nativeDrv)
+       then {
+         crossDrv = overrideDerivation drv.crossDrv f;
+         nativeDrv = overrideDerivation drv.nativeDrv f;
+       }
+       else { }));
+
+
+  # usage: (you can use override multiple times)
+  # let d = makeOverridable stdenv.mkDerivation { name = ..; buildInputs; }
+  #     noBuildInputs = d.override { buildInputs = []; }
+  #     additionalBuildInputs = d.override ( args : args // { buildInputs = args.buildInputs ++ [ additional ]; } )
+  makeOverridable = f: origArgs:
+    let
+      ff = f origArgs;
+    in
+      if builtins.isAttrs ff then (ff //
+        { override = newArgs:
+            makeOverridable f (origArgs // (if builtins.isFunction newArgs then newArgs origArgs else newArgs));
+          deepOverride = newArgs:
+            makeOverridable f (lib.overrideExisting (lib.mapAttrs (deepOverrider newArgs) origArgs) newArgs);
+        })
+      else ff;
+
+  deepOverrider = newArgs: name: x: if builtins.isAttrs x then (
+    if x ? deepOverride then (x.deepOverride newArgs) else
+    if x ? override then (x.override newArgs) else
+    x) else x;
+
+
+  /* Call the package function in the file `fn' with the required
+    arguments automatically.  The function is called with the
+    arguments `args', but any missing arguments are obtained from
+    `autoArgs'.  This function is intended to be partially
+    parameterised, e.g.,
+
+      callPackage = callPackageWith pkgs;
+      pkgs = {
+        libfoo = callPackage ./foo.nix { };
+        libbar = callPackage ./bar.nix { };
+      };
+
+    If the `libbar' function expects an argument named `libfoo', it is
+    automatically passed as an argument.  Overrides or missing
+    arguments can be supplied in `args', e.g.
+
+      libbar = callPackage ./bar.nix {
+        libfoo = null;
+        enableX11 = true;
+      };
+  */
+  callPackageWith = autoArgs: fn: args:
+    let f = if builtins.isFunction fn then fn else import fn; in
+    makeOverridable f ((builtins.intersectAttrs (builtins.functionArgs f) autoArgs) // args);
+
+  /* Add attributes to each output of a derivation without changing the derivation itself */
+  addPassthru = drv: passthru:
+    let
+      outputs = drv.outputs or [ "out" ];
+
+      commonAttrs = drv // (builtins.listToAttrs outputsList) //
+        ({ all = map (x: x.value) outputsList; }) // passthru;
+
+      outputToAttrListElement = outputName:
+        { name = outputName;
+          value = commonAttrs // {
+            inherit (builtins.getAttr outputName drv) outPath drvPath type outputName;
+          };
+        };
+
+      outputsList = map outputToAttrListElement outputs;
+  in builtins.getAttr drv.outputName commonAttrs;
+}
diff --git a/lib/debug.nix b/lib/debug.nix
new file mode 100644
index 000000000000..d627bc861abb
--- /dev/null
+++ b/lib/debug.nix
@@ -0,0 +1,119 @@
+let lib = import ./default.nix;
+
+inherit (builtins) trace attrNamesToStr isAttrs isFunction isList isInt
+        isString isBool head substring attrNames;
+
+inherit (lib) all id mapAttrsFlatten elem;
+
+in
+
+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;
+
+  addErrorContextToAttrs = lib.mapAttrs (a : v : lib.addErrorContext "while evaluating ${a}" v);
+
+  
+  traceVal = if builtins ? trace then x: (builtins.trace x x) else x: x;
+  traceXMLVal = if builtins ? trace then x: (builtins.trace (builtins.toXML x) x) else x: x;
+  traceXMLValMarked = str: if builtins ? trace then x: (builtins.trace ( str + builtins.toXML x) x) else x: x;
+  
+  # this can help debug your code as well - designed to not produce thousands of lines
+  traceShowVal = x : trace (showVal x) x;
+  traceShowValMarked = str: x: trace (str + showVal x) x;
+  attrNamesToStr = a : lib.concatStringsSep "; " (map (x : "${x}=") (attrNames a));
+  showVal = x :
+      if isAttrs x then
+          if x ? outPath then "x is a derivation, name ${if x ? name then x.name else "<no name>"}, { ${attrNamesToStr x} }"
+          else "x is attr set { ${attrNamesToStr x} }"
+      else if isFunction x then "x is a function"
+      else if x == [] then "x is an empty list"
+      else if isList x then "x is a list, first element is: ${showVal (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 if isInt x then "x is an integer `${toString x}'"
+      else if isString x then "x is a string `${substring 0 50 x}...'"
+      else "x is probably a path `${substring 0 50 (toString x)}...'";
+
+  # trace the arguments passed to function and its result 
+  # maybe rewrite these functions in a traceCallXml like style. Then one function is enough
+  traceCall  = n : f : a : let t = n2 : x : traceShowValMarked "${n} ${n2}:" x; in t "result" (f (t "arg 1" a));
+  traceCall2 = n : f : a : b : let t = n2 : x : traceShowValMarked "${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 : traceShowValMarked "${n} ${n2}:" x; in t "result" (f (t "arg 1" a) (t "arg 2" b) (t "arg 3" c));
+
+  traceValIfNot = c: x:
+    if c x then true else trace (showVal x) false;
+
+  /* Evaluate a set of tests.  A test is an attribute set {expr,
+     expected}, denoting an expression and its expected result.  The
+     result is a list of failed tests, each represented as {name,
+     expected, actual}, denoting the attribute name of the failing
+     test and its expected and actual results.  Used for regression
+     testing of the functions in lib; see tests.nix for an example.
+     Only tests having names starting with "test" are run.
+     Add attr { tests = ["testName"]; } to run these test only
+  */
+  runTests = tests: lib.concatLists (lib.attrValues (lib.mapAttrs (name: test:
+    let testsToRun = if tests ? tests then tests.tests else [];
+    in if (substring 0 4 name == "test" ||  elem name testsToRun)
+       && ((testsToRun == []) || elem name tests.tests)
+       && (test.expr != test.expected)
+
+      then [ { inherit name; expected = test.expected; result = test.expr; } ]
+      else [] ) tests));
+  
+  # create a test assuming that list elements are true
+  # usage: { testX = allTrue [ true ]; }
+  testAllTrue = expr : { inherit expr; expected = map (x: true) expr; };
+
+  # evaluate everything once so that errors will occur earlier
+  # hacky: traverse attrs by adding a dummy
+  # ignores functions (should this behavior change?) See strictf
+  #
+  # Note: This should be a primop! Something like seq of haskell would be nice to
+  # have as well. It's used fore debugging only anyway
+  strict = x :
+    let
+        traverse = x :
+          if isString x then true
+          else if isAttrs x then
+            if x ? outPath then true
+            else all id (mapAttrsFlatten (n: traverse) x)
+          else if isList x then
+            all id (map traverse x)
+          else if isBool x then true
+          else if isFunction x then true
+          else if isInt x then true
+          else if x == null then true
+          else true; # a (store) path?
+    in if traverse x then x else throw "else never reached";
+
+  # example: (traceCallXml "myfun" id 3) will output something like
+  # calling myfun arg 1: 3 result: 3
+  # this forces deep evaluation of all arguments and the result!
+  # note: if result doesn't evaluate you'll get no trace at all (FIXME)
+  #       args should be printed in any case
+  traceCallXml = a:
+    if !isInt a then
+      traceCallXml 1 "calling ${a}\n"
+    else
+      let nr = a;
+      in (str: expr:
+          if isFunction expr then
+            (arg: 
+              traceCallXml (builtins.add 1 nr) "${str}\n arg ${builtins.toString nr} is \n ${builtins.toXML (strict arg)}" (expr arg)
+            )
+          else 
+            let r = strict expr;
+            in builtins.trace "${str}\n result:\n${builtins.toXML r}" r
+      );
+}
diff --git a/lib/default.nix b/lib/default.nix
new file mode 100644
index 000000000000..dea82ee077eb
--- /dev/null
+++ b/lib/default.nix
@@ -0,0 +1,31 @@
+let 
+
+  trivial = import ./trivial.nix;
+  lists = import ./lists.nix;
+  strings = import ./strings.nix;
+  stringsWithDeps = import ./strings-with-deps.nix;
+  attrsets = import ./attrsets.nix;
+  sources = import ./sources.nix;
+  modules = import ./modules.nix;
+  options = import ./options.nix;
+  properties = import ./properties.nix;
+  types = import ./types.nix;
+  meta = import ./meta.nix;
+  debug = import ./debug.nix;
+  misc = import ./misc.nix;
+  maintainers = import ./maintainers.nix;
+  platforms = import ./platforms.nix;
+  systems = import ./systems.nix;
+  customisation = import ./customisation.nix;
+  licenses = import ./licenses.nix;
+
+in
+  { inherit trivial lists strings stringsWithDeps attrsets sources options
+      properties modules types meta debug maintainers licenses platforms systems;
+  }
+  # !!! don't include everything at top-level; perhaps only the most
+  # commonly used functions.
+  // trivial // lists // strings // stringsWithDeps // attrsets // sources
+  // properties // options // types // meta // debug // misc // modules
+  // systems
+  // customisation
diff --git a/lib/licenses.nix b/lib/licenses.nix
new file mode 100644
index 000000000000..55517c5e1e5e
--- /dev/null
+++ b/lib/licenses.nix
@@ -0,0 +1,235 @@
+{
+  /* License identifiers loosely based on: http://fedoraproject.org/wiki/Licensing
+   * If you cannot find your license here, then look for a similar license or
+   * add it to this list. The URL mentioned above is a good source for inspiration.
+   */
+
+  artistic2 = {
+    shortName = "Artistic 2.0";
+    fullName = "Artistic 2.0";
+    url = "http://opensource.org/licenses/artistic-license-2.0.php";
+  };
+
+  agpl3 = {
+    shortName = "AGPLv3";
+    fullName = "GNU Affero General Public License version 3 only";
+    url = https://www.gnu.org/licenses/agpl.html;
+  };
+
+  agpl3Plus = {
+    shortName = "AGPLv3+";
+    fullName = "GNU Affero General Public License version 3 or later";
+    url = https://www.gnu.org/licenses/agpl.html;
+  };
+
+  amd = {
+    shortName = "amd";
+    fullName = "AMD License Agreement";
+    url = "http://developer.amd.com/amd-license-agreement/";
+  };
+
+  amdadl = {
+    shortName = "amd-adl";
+    fullName = "amd-adl license";
+    url = "http://sources.gentoo.org/cgi-bin/viewvc.cgi/gentoo-x86/licenses/AMD-ADL?revision=1.1";
+  };
+
+  # Apple Public Source License 2.0;
+  # http://opensource.org/licenses/APSL-2.0
+  apsl20 = "APSL 2.0";
+
+  asl20 = {
+    shortName = "ASL2.0";
+    fullName = "Apache Software License 2.0";
+    url = http://www.apache.org/licenses/LICENSE-2.0;
+  };
+
+  boost = {
+    shortName = "boost";
+    fullName = "Boost Software License";
+    url = http://www.boost.org/LICENSE_1_0.txt;
+  };
+
+  bsd2 = {
+    shortName = "BSD-2";
+    fullName = "BSD license (2 clause)";
+    url = http://opensource.org/licenses/BSD-2-Clause;
+  };
+
+  bsd3 = {
+    shortName = "BSD-3";
+    fullName = "BSD license (3 clause)";
+    url = http://opensource.org/licenses/BSD-3-Clause;
+  };
+
+  bsdOriginal = {
+    shortName = "BSD-original";
+    fullName = "Original BSD license with advertising clause";
+    url = https://fedoraproject.org/wiki/Licensing/BSD;
+  };
+
+  cddl = {
+    shortName = "CDDL";
+    fullName = "Common Development Distribution License ";
+    url = http://www.opensolaris.org/os/licensing/cddllicense.txt;
+  };
+
+  cpl10 = {
+    shortName = "CPL 1.0";
+    fullName = "Common Public License version 1.0";
+    url = http://www.eclipse.org/legal/cpl-v10.html;
+  };
+
+  epl10 = {
+    shortName = "EPL 1.0";
+    fullName = "Eclipse Public License version 1.0";
+    url = http://www.eclipse.org/legal/epl-v10.html;
+  };
+
+  gpl2 = "GPLv2";
+
+  gpl2Oss = {
+    shortName = "GPLv2+OSS";
+    fullName = "GNU General Public License version 2 only (with OSI approved licenses linking exception)";
+    url = http://www.mysql.com/about/legal/licensing/foss-exception;
+  };
+
+  # GNU General Public License version 2 or later;
+  # http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
+  gpl2Plus = "GPLv2+";
+
+  gpl3 = {
+    shortName = "GPLv3";
+    fullName = "GNU General Public License version 3 only";
+    url = http://www.fsf.org/licensing/licenses/gpl.html;
+  };
+
+  gpl3Plus = {
+    shortName = "GPLv3+";
+    fullName = "GNU General Public License version 3 or later";
+    url = http://www.fsf.org/licensing/licenses/gpl.html;
+  };
+
+  gpl3ClasspathPlus = {
+    shortName = "GPLv3+classpath+";
+    fullName = "GNU General Public License version 3 or later (with Classpath exception)";
+    url = https://fedoraproject.org/wiki/Licensing/GPL_Classpath_Exception;
+  };
+
+  isc = {
+    shortName = "ISC";
+    fullName = "Internet Systems Consortium License";
+    url = http://www.opensource.org/licenses/ISC;
+  };
+
+  ipl10 = {
+    shortName = "IPL 1.0";
+    fullName = "IBM Public License Version 1.0";
+    url = http://www.ibm.com/developerworks/opensource/library/os-i18n2/os-ipl.html;
+  };
+
+  ijg = {
+    shortName = "IJG";
+    fullName = "Independent JPEG Group License";
+    url = https://fedoraproject.org/wiki/Licensing/IJG;
+  };
+
+  libtiff = {
+    shortName = "libtiff";
+    fullName = "libtiff license";
+    url = https://fedoraproject.org/wiki/Licensing/libtiff;
+  };
+
+  lgpl2 = "LGPLv2";
+
+  lgpl2Plus = {
+    shortName = "LGPLv2+";
+    fullName = "GNU Library General Public License version 2 or later";
+    url = http://www.gnu.org/licenses/old-licenses/lgpl-2.0.html;
+  };
+
+  lgpl21 = "LGPLv2.1";
+
+  lgpl21Plus = {
+    shortName = "LGPLv2.1+";
+    fullName = "GNU Lesser General Public License version 2.1 or later";
+    url = http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html;
+  };
+
+  llgpl21 = {
+    shortName = "LLGPLv2.1";
+    fullName = "Lisp LGPL; GNU Lesser General Public License version 2.1 with Franz Inc. preamble for clarification of LGPL terms in context of Lisp";
+    url = http://opensource.franz.com/preamble.html;
+  };
+
+  lgpl3 = {
+    shortName = "LGPLv3";
+    fullName = "GNU Lesser General Public License version 3 only";
+    url = http://www.fsf.org/licensing/licenses/lgpl.html;
+  };
+
+  lgpl3Plus = {
+    shortName = "LGPLv3+";
+    fullName = "GNU Lesser General Public License version 3 or later";
+    url = http://www.fsf.org/licensing/licenses/lgpl.html;
+  };
+
+  mit = {
+    shortName = "MIT";
+    fullName = "MIT/X11 license";
+    url = http://www.opensource.org/licenses/mit-license.php;
+  };
+
+  mpl11 = {
+    shortName = "MPL1.1";
+    fullName = "Mozilla Public License version 1.1";
+    url = http://www.mozilla.org/MPL/MPL-1.1.html;
+  };
+
+  openssl = {
+    shortName = "openssl";
+    fullName = "OpenSSL license";
+    url = http://www.openssl.org/source/license.html;
+  };
+
+  publicDomain = {
+    shortName = "Public Domain";
+    fullname = "Public Domain";
+  };
+
+  psfl = {
+    shortName = "PSFL";
+    fullName = "Python Software Foundation License";
+    url = http://docs.python.org/license.html;
+  };
+
+  tcltk = {
+    shortName = "Tcl/Tk";
+    fullName = "Tcl/Tk license";
+    url = http://www.tcl.tk/software/tcltk/license.html;
+  };
+
+  unfree = "unfree";
+
+  unfreeRedistributable = "unfree-redistributable";
+
+  unfreeRedistributableFirmware = "unfree-redistributable-firmware";
+
+  zlib = {
+    shortName = "zlib";
+    fullName = "zlib license";
+    url = http://www.gzip.org/zlib/zlib_license.html;
+  };
+
+  zpt20 = {
+    shortName = "ZPT2.0";
+    fullName = "Zope Public License 2.0";
+    url = "http://old.zope.org/Resources/License/ZPL-2.0";
+  };
+
+  zpt21 = {
+    shortName = "ZPT2.1";
+    fullName = "Zope Public License 2.1";
+    url = "http://old.zope.org/Resources/License/ZPL-2.1";
+  };
+}
diff --git a/lib/lists.nix b/lib/lists.nix
new file mode 100644
index 000000000000..578686ae3668
--- /dev/null
+++ b/lib/lists.nix
@@ -0,0 +1,235 @@
+# General list operations.
+let
+  inherit (import ./trivial.nix) deepSeq;
+
+  inc = builtins.add 1;
+
+  dec = n: builtins.sub n 1;
+
+  inherit (builtins) elemAt;
+in rec {
+  inherit (builtins) head tail length isList add sub lessThan;
+
+
+  # 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.
+  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 = op: nul: list:
+    let
+      len = length list;
+      fold' = n:
+        if n == len
+        then nul
+        else op (elemAt list n) (fold' (inc n));
+    in fold' 0;
+
+  # 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:
+    let
+      len = length list;
+      foldl' = n:
+        if n == minus1
+        then nul
+        else op (foldl' (dec n)) (elemAt list n);
+    in foldl' (dec (length list));
+
+  minus1 = dec 0;
+
+
+  # map with index: `imap (i: v: "${v}-${toString i}") ["a" "b"] ==
+  # ["a-1" "b-2"]'
+  imap = f: list:
+    let
+      len = length list;
+      imap' = n:
+        if n == len
+          then []
+          else [ (f (inc n) (elemAt list n)) ] ++ imap' (inc n);
+    in imap' 0;
+
+    
+  # Concatenate a list of lists.
+  concatLists = builtins.concatLists or (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 =
+    builtins.filter or
+    (pred: list:
+      fold (x: y: if pred x then [x] ++ y else y) [] list);
+
+    
+  # Remove elements equal to 'e' from a list.  Useful for buildInputs.
+  remove = e: filter (x: x != e);
+
+  
+  # Return true if `list' has an element `x'.
+  elem =
+    builtins.elem or
+    (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; 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.
+  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'.
+  any = pred: fold (x: y: if pred x then true else y) false;
+
+
+  # Return true iff function `pred' returns true for all elements of
+  # `list'.
+  all = pred: fold (x: y: if pred x then y else false) true;
+
+
+  # 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 = []; };
+
+
+  zipListsWith = f: fst: snd:
+    let
+      len1 = length fst;
+      len2 = length snd;
+      len = if builtins.lessThan len1 len2 then len1 else len2;
+      zipListsWith' = n:
+        if n != len then
+          [ (f (elemAt fst n) (elemAt snd n)) ]
+          ++ zipListsWith' (inc n)
+        else [];
+    in zipListsWith' 0;
+
+  zipLists = zipListsWith (fst: snd: { inherit fst snd; });
+
+  
+  # Reverse the order of the elements of a list.
+  reverseList = 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 = strictLess: list:
+    let
+      len = length list;
+      first = head list;
+      pivot' = n: acc@{ left, right }: let el = elemAt list n; next = pivot' (inc n); in
+        if n == len
+          then acc
+        else if strictLess first el
+          then next { inherit left; right = [ el ] ++ right; }
+        else
+          next { left = [ el ] ++ left; inherit right; };
+      pivot = pivot' 1 { left = []; right = []; };
+    in
+      if lessThan len 2 then list
+      else (sort strictLess pivot.left) ++  [ first ] ++  (sort strictLess pivot.right);
+
+
+  # Return the first (at most) N elements of a list.
+  take = count: list:
+    let
+      len = length list;
+      take' = n:
+        if n == len || n == count
+          then []
+        else
+          [ (elemAt list n) ] ++ take' (inc n);
+    in take' 0;
+
+    
+  # Remove the first (at most) N elements of a list.
+  drop = count: list:
+    let
+      len = length list;
+      drop' = n:
+        if n == minus1 || lessThan n count
+          then []
+        else
+          drop' (dec n) ++ [ (elemAt list n) ];
+    in drop' (dec len);
+
+    
+  last = list:
+    assert list != []; elemAt list (dec (length list));
+
+
+  # Zip two lists together.
+  zipTwoLists = xs: ys:
+    let
+      len1 = length xs;
+      len2 = length ys;
+      len = if lessThan len1 len2 then len1 else len2;
+      zipTwoLists' = n:
+        if n != len then
+          [ { first = elemAt xs n; second = elemAt ys n; } ]
+          ++ zipTwoLists' (inc n)
+        else [];
+    in zipTwoLists' 0;
+
+  deepSeqList = xs: y: if any (x: deepSeq x false) xs then y else y;
+}
diff --git a/lib/maintainers.nix b/lib/maintainers.nix
new file mode 100644
index 000000000000..06c71b2b7ac8
--- /dev/null
+++ b/lib/maintainers.nix
@@ -0,0 +1,65 @@
+/* -*- coding: utf-8; -*- */
+
+{
+  /* Add your name and email address here.  Keep the list
+     alphabetically sorted.  */
+
+  aforemny = "Alexander Foremny <alexanderforemny@googlemail.com>";
+  algorith = "Dries Van Daele <dries_van_daele@telenet.be>";
+  all = "Nix Committers <nix-commits@lists.science.uu.nl>";
+  amiddelk = "Arie Middelkoop <amiddelk@gmail.com>";
+  amorsillo = "Andrew Morsillo <andrew.morsillo@gmail.com>";
+  andres = "Andres Loeh <ksnixos@andres-loeh.de>";
+  antono = "Antono Vasiljev <self@antono.info>";
+  astsmtl = "Alexander Tsamutali <astsmtl@yandex.ru>";
+  aszlig = "aszlig <aszlig@redmoonstudios.org>";
+  bbenoist = "Baptist BENOIST <return_0@live.com>";
+  bjg = "Brian Gough <bjg@gnu.org>";
+  bjornfor = "Bjørn Forsman <bjorn.forsman@gmail.com>";
+  bluescreen303 = "Mathijs Kwik <mathijs@bluescreen303.nl>";
+  bodil = "Bodil Stokke <nix@bodil.org>";
+  chaoflow = "Florian Friesdorf <flo@chaoflow.net>";
+  coconnor = "Corey O'Connor <coreyoconnor@gmail.com>";
+  coroa = "Jonas Hörsch <jonas@chaoflow.net>";
+  edwtjo = "Edward Tjörnhammar <ed@cflags.cc>";
+  eelco = "Eelco Dolstra <eelco.dolstra@logicblox.com>";
+  ertes = "Ertugrul Söylemez <es@ertes.de>";
+  garbas = "Rok Garbas <rok@garbas.si>";
+  goibhniu = "Cillian de Róiste <cillian.deroiste@gmail.com>";
+  guibert = "David Guibert <david.guibert@gmail.com>";
+  iElectric = "Domen Kozar <domen@dev.si>";
+  iyzsong = "Song Wenwu <iyzsong@gmail.com>";
+  jcumming = "Jack Cummings <jack@mudshark.org>";
+  kkallio = "Karn Kallio <tierpluspluslists@gmail.com>";
+  lovek323 = "Jason O'Conal <jason@oconal.id.au>";
+  ludo = "Ludovic Courtès <ludo@gnu.org>";
+  marcweber = "Marc Weber <marco-oweber@gmx.de>";
+  modulistic = "Pablo Costa <modulistic@gmail.com>";
+  mornfall = "Petr Ročkai <me@mornfall.net>";
+  ocharles = "Oliver Charles <ollie@ocharles.org.uk>";
+  offline = "Jaka Hudoklin <jakahudoklin@gmail.com>";
+  orbitz = "Malcolm Matalka <mmatalka@gmail.com>";
+  page = "Carles Pagès <page@cubata.homelinux.net>";
+  phreedom = "Evgeny Egorochkin <phreedom@yandex.ru>";
+  pierron = "Nicolas B. Pierron <nixos@nbp.name>";
+  piotr = "Piotr Pietraszkiewicz <ppietrasa@gmail.com>";
+  pSub = "Pascal Wittmann <mail@pascal-wittmann.de>";
+  qknight = "Joachim Schiele <js@lastlog.de>";
+  raskin = "Michael Raskin <7c6f434c@mail.ru>";
+  rickynils = "Rickard Nilsson <rickynils@gmail.com>";
+  rob = "Rob Vermaas <rob.vermaas@gmail.com>";
+  roconnor = "Russell O'Connor <roconnor@theorem.ca>";
+  sander = "Sander van der Burg <s.vanderburg@tudelft.nl>";
+  shlevy = "Shea Levy <shea@shealevy.com>";
+  simons = "Peter Simons <simons@cryp.to>";
+  smironov = "Sergey Mironov <ierton@gmail.com>";
+  thammers = "Tobias Hammerschmidt <jawr@gmx.de>";
+  the-kenny = "Moritz Ulrich <moritz@tarn-vedra.de>";
+  urkud = "Yury G. Kudryashov <urkud+nix@ya.ru>";
+  vcunat = "Vladimír Čunát <vcunat@gmail.com>";
+  viric = "Lluís Batlle i Rossell <viric@viric.name>";
+  vizanto = "Danny Wilson <danny@prime.vc>";
+  winden = "Antonio Vargas Gonzalez <windenntw@gmail.com>";
+  z77z = "Marco Maggesi <maggesi@math.unifi.it>";
+  zef = "Zef Hemel <zef@zef.me>";
+}
diff --git a/lib/meta.nix b/lib/meta.nix
new file mode 100644
index 000000000000..a5afce9e0cb1
--- /dev/null
+++ b/lib/meta.nix
@@ -0,0 +1,48 @@
+/* Some functions for manipulating meta attributes, as well as the
+   name attribute. */
+
+rec {
+
+
+  /* Add to or override the meta attributes of the given
+     derivation.
+
+     Example:
+       addMetaAttrs {description = "Bla blah";} somePkg
+  */
+  addMetaAttrs = newAttrs: drv:
+    drv // { meta = (if drv ? meta then drv.meta else {}) // newAttrs; };
+
+
+  /* Change the symbolic name of a package for presentation purposes
+     (i.e., so that nix-env users can tell them apart).
+  */
+  setName = name: drv: drv // {inherit name;};
+
+
+  /* Like `setName', but takes the previous name as an argument.
+
+     Example:
+       updateName (oldName: oldName + "-experimental") somePkg
+  */
+  updateName = updater: drv: drv // {name = updater (drv.name);};
+
+
+  /* Append a suffix to the name of a package.  !!! the suffix should
+     really be appended *before* the version, at least most of the
+     time.
+  */
+  appendToName = suffix: updateName (name: "${name}-${suffix}");
+
+
+  /* Decrease the nix-env priority of the package, i.e., other
+     versions/variants of the package will be preferred.
+  */
+  lowPrio = drv: addMetaAttrs { priority = "10"; } drv;
+
+  /* Increase the nix-env priority of the package, i.e., this
+     version/variant of the package will be preferred.
+  */
+  hiPrio = drv: addMetaAttrs { priority = "-10"; } drv;
+  
+}
diff --git a/lib/misc.nix b/lib/misc.nix
new file mode 100644
index 000000000000..19e5081009de
--- /dev/null
+++ b/lib/misc.nix
@@ -0,0 +1,431 @@
+let lib = import ./default.nix;
+    inherit (builtins) isFunction hasAttr getAttr head tail isList isAttrs isInt attrNames;
+
+in
+
+with import ./lists.nix;
+with import ./attrsets.nix;
+with import ./strings.nix;
+
+rec {
+
+  # returns default if env var is not set
+  maybeEnv = name: default:
+    let value = builtins.getEnv name; in
+    if value == "" then default else value;
+
+  defaultMergeArg = x : y: if builtins.isAttrs y then
+    y
+  else 
+    (y x);
+  defaultMerge = x: y: x // (defaultMergeArg x y);
+  foldArgs = merger: f: init: x: 
+    let arg=(merger init (defaultMergeArg init x));
+      # now add the function with composed args already applied to the final attrs
+        base = (setAttrMerge "passthru" {} (f arg) 
+                        ( z : z // rec { 
+                          function = foldArgs merger f arg; 
+			  args = (lib.attrByPath ["passthru" "args"] {} z) // x;
+                          } ));
+	withStdOverrides = base // {
+	   override = base.passthru.function;
+	   deepOverride = a : (base.passthru.function ((lib.mapAttrs (lib.deepOverrider a) base.passthru.args) // a));
+	   } ;
+        in
+	withStdOverrides;
+    
+
+  # predecessors: proposed replacement for applyAndFun (which has a bug cause it merges twice)
+  # the naming "overridableDelayableArgs" tries to express that you can
+  # - override attr values which have been supplied earlier
+  # - use attr values before they have been supplied by accessing the fix point
+  #   name "fixed"
+  # f: the (delayed overridden) arguments are applied to this
+  #
+  # initial: initial attrs arguments and settings. see defaultOverridableDelayableArgs
+  #
+  # returns: f applied to the arguments // special attributes attrs
+  #     a) merge: merge applied args with new args. Wether an argument is overridden depends on the merge settings
+  #     b) replace: this let's you replace and remove names no matter which merge function has been set
+  #
+  # examples: see test cases "res" below;
+  overridableDelayableArgs =
+          f :        # the function applied to the arguments
+          initial :  # you pass attrs, the functions below are passing a function taking the fix argument
+    let
+        takeFixed = if isFunction initial then initial else (fixed : initial); # transform initial to an expression always taking the fixed argument
+        tidy = args : 
+            let # apply all functions given in "applyPreTidy" in sequence
+                applyPreTidyFun = fold ( n : a : x : n ( a x ) ) lib.id (maybeAttr "applyPreTidy" [] args);
+            in removeAttrs (applyPreTidyFun args) ( ["applyPreTidy"] ++ (maybeAttr  "removeAttrs" [] args) ); # tidy up args before applying them
+        fun = n : x :
+             let newArgs = fixed :
+                     let args = takeFixed fixed; 
+                         mergeFun = getAttr n args;
+                     in if isAttrs x then (mergeFun args x)
+                        else assert isFunction x;
+                             mergeFun args (x ( args // { inherit fixed; }));
+             in overridableDelayableArgs f newArgs;
+    in
+    (f (tidy (lib.fix takeFixed))) // {
+      merge   = fun "mergeFun";
+      replace = fun "keepFun";
+    };
+  defaultOverridableDelayableArgs = f : 
+      let defaults = {
+            mergeFun = mergeAttrByFunc; # default merge function. merge strategie (concatenate lists, strings) is given by mergeAttrBy
+            keepFun = a : b : { inherit (a) removeAttrs mergeFun keepFun mergeAttrBy; } // b; # even when using replace preserve these values
+            applyPreTidy = []; # list of functions applied to args before args are tidied up (usage case : prepareDerivationArgs)
+            mergeAttrBy = mergeAttrBy // {
+              applyPreTidy = a : b : a ++ b;
+              removeAttrs = a : b: a ++ b;
+            };
+            removeAttrs = ["mergeFun" "keepFun" "mergeAttrBy" "removeAttrs" "fixed" ]; # before applying the arguments to the function make sure these names are gone
+          };
+      in (overridableDelayableArgs f defaults).merge;
+
+
+
+  # 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 {};
+
+  
+  # shortcut for attrByPath ["name"] default attrs
+  maybeAttrNullable = name: default: attrs:
+    if attrs == null then default else 
+    if __hasAttr name attrs then (__getAttr name attrs) else default;
+
+  # shortcut for attrByPath ["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 (attrByPath ["flags"] [] attrSet)) then true else
+        attrByPath [name] false attrSet ;
+
+
+  # Input : attrSet, [ [name default] ... ], name
+  # Output : its value or default.
+  getValue = attrSet: argList: name:
+  ( attrByPath [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)) ;
+        
+
+  # This function has O(n^2) performance.
+  uniqList = {inputList, acc ? []} :
+    let go = xs : acc :
+             if xs == []
+             then []
+             else let x = head xs;
+                      y = if elem x acc then [] else [x];
+                  in y ++ go (tail xs) (y ++ acc);
+    in go inputList acc;
+
+  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;
+
+  lazyGenericClosure = {startSet, operator}:
+    let
+      work = list: doneKeys: result:
+        if list == [] then
+          result
+        else
+          let x = head list; key = x.key; in
+          if elem key doneKeys then
+            work (tail list) doneKeys result
+          else
+            work (tail list ++ operator x) ([key] ++ doneKeys) ([x] ++ result);
+    in
+      work startSet [] [];
+
+  genericClosure =
+    if builtins ? genericClosure then builtins.genericClosure
+    else lazyGenericClosure;
+
+  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 {};
+
+
+  innerClosePropagation = acc : xs :
+    if xs == []
+    then acc
+    else let y  = head xs;
+             ys = tail xs;
+         in if ! isAttrs y
+            then innerClosePropagation acc ys
+            else let acc' = [y] ++ acc;
+                 in innerClosePropagation
+                      acc'
+                      (uniqList { inputList = (maybeAttrNullable "propagatedBuildInputs" [] y)
+                                           ++ (maybeAttrNullable "propagatedNativeBuildInputs" [] y)
+                                           ++ ys;
+                                  acc = acc';
+                                }
+                      );
+
+  closePropagation = list: (uniqList {inputList = (innerClosePropagation [] list);});
+
+  # calls a function (f attr value ) for each record item. returns a list
+  mapAttrsFlatten = f : r : map (attr: f attr (builtins.getAttr attr r) ) (attrNames r);
+
+  # attribute set containing one attribute
+  nvs = name : value : listToAttrs [ (nameValuePair 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));
+
+  # 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 )
+           (set2 // 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
+  # ! deprecated, 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, defaultOverridableDelayableArgs 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; };
+  mergeAttrsByFuncDefaultsClean = list: removeAttrs (mergeAttrsByFuncDefaults list) ["mergeAttrBy"];
+
+  # merge attrs based on version key into mkDerivation args, see mergeAttrBy to learn about smart merge defaults
+  #
+  # This function is best explained by an example:
+  #
+  #     {version ? "2.x"} :
+  #
+  #     mkDerivation (mergeAttrsByVersion "package-name" version 
+  #       { # version specific settings
+  #         "git" = { src = ..; preConfigre = "autogen.sh"; buildInputs = [automake autoconf libtool];  };
+  #         "2.x" = { src = ..; };
+  #       }
+  #       {  // shared settings
+  #          buildInputs = [ common build inputs ];
+  #          meta = { .. }
+  #       }
+  #     )
+  #
+  # Please note that e.g. Eelco Dolstra usually prefers having one file for
+  # each version. On the other hand there are valuable additional design goals
+  #  - readability
+  #  - do it once only
+  #  - try to avoid duplication
+  #
+  # Marc Weber and Michael Raskin sometimes prefer keeping older
+  # versions around for testing and regression tests - as long as its cheap to
+  # do so.
+  #
+  # Very often it just happens that the "shared" code is the bigger part.
+  # Then using this function might be appropriate.
+  #
+  # Be aware that its easy to cause recompilations in all versions when using
+  # this function - also if derivations get too complex splitting into multiple
+  # files is the way to go.
+  #
+  # See misc.nix -> versionedDerivation
+  # discussion: nixpkgs: pull/310
+  mergeAttrsByVersion = name: version: attrsByVersion: base:
+    mergeAttrsByFuncDefaultsClean [ { name = "${name}-${version}"; } base (maybeAttr version (throw "bad version ${version} for ${name}") attrsByVersion)];
+
+  # sane defaults (same name as attr name so that inherit can be used)
+  mergeAttrBy = # { buildInputs = concatList; [...]; passthru = mergeAttr; [..]; }
+    listToAttrs (map (n : nameValuePair n lib.concat)
+      [ "nativeBuildInputs" "buildInputs" "propagatedBuildInputs" "configureFlags" "prePhases" "postAll" "patches" ])
+    // listToAttrs (map (n : nameValuePair n lib.mergeAttrs) [ "passthru" "meta" "cfg" "flags" ])
+    // listToAttrs (map (n : nameValuePair n (a: b: "${a}\n${b}") ) [ "preConfigure" "postInstall" ])
+  ;
+
+  # 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?
+  # TODO use args.mergeFun here as well?
+  prepareDerivationArgs = args:
+    let args2 = { cfg = {}; flags = {}; } // args;
+        flagName = name : "${name}Support";
+        cfgWithDefaults = (listToAttrs (map (n : nameValuePair (flagName n) false) (attrNames args2.flags)))
+                          // args2.cfg;
+        opts = attrValues (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" ];
+
+
+  nixType = x:
+      if isAttrs x then
+          if x ? outPath then "derivation"
+          else "aattrs"
+      else if isFunction x then "function"
+      else if isList x then "list"
+      else if x == true then "bool"
+      else if x == false then "bool"
+      else if x == null then "null"
+      else if isInt x then "int"
+      else "string";
+
+}
diff --git a/lib/modules.nix b/lib/modules.nix
new file mode 100644
index 000000000000..acd10e7bf576
--- /dev/null
+++ b/lib/modules.nix
@@ -0,0 +1,380 @@
+# NixOS module handling.
+
+let lib = import ./default.nix; in
+
+with { inherit (builtins) head; };
+with import ./trivial.nix;
+with import ./lists.nix;
+with import ./misc.nix;
+with import ./attrsets.nix;
+with import ./options.nix;
+with import ./properties.nix;
+
+rec {
+
+  # Unfortunately this can also be a string.
+  isPath = x: !(
+     builtins.isFunction x
+  || builtins.isAttrs x
+  || builtins.isInt x
+  || builtins.isBool x
+  || builtins.isList x
+  );
+
+
+  importIfPath = path:
+    if isPath path then
+      import path
+    else
+      path;
+
+
+  applyIfFunction = f: arg:
+    if builtins.isFunction f then
+      f arg
+    else
+      f;
+
+
+  isModule = m:
+       (m ? config && isAttrs m.config && ! isOption m.config)
+    || (m ? options && isAttrs m.options && ! isOption m.options);
+
+
+  # Convert module to a set which has imports / options and config
+  # attributes.
+  unifyModuleSyntax = m:
+    let
+      delayedModule = delayProperties m;
+
+      getImports =
+        toList (rmProperties (delayedModule.require or []));
+      getImportedPaths = filter isPath getImports;
+      getImportedSets = filter (x: !isPath x) getImports;
+
+      getConfig =
+        removeAttrs delayedModule ["require" "key" "imports"];
+
+    in
+      if isModule m then
+        { key = "<unknown location>"; } // m
+      else
+        { key = "<unknown location>";
+          imports = (m.imports or []) ++ getImportedPaths;
+          config = getConfig;
+        } // (
+          if getImportedSets != [] then
+            assert length getImportedSets == 1;
+            { options = head getImportedSets; }
+          else
+            {}
+        );
+
+
+  unifyOptionModule = {key ? "<unknown location>"}: name: index: m: (args:
+    let
+      module = lib.applyIfFunction m args;
+      key_ = rec {
+        file = key;
+        option = name;
+        number = index;
+        outPath = key;
+      };
+    in if lib.isModule module then
+      { key = key_; } // module
+    else
+      { key = key_; options = module; }
+  );
+
+
+  moduleClosure = initModules: args:
+    let
+      moduleImport = origin: index: m:
+        let m' = applyIfFunction (importIfPath m) args;
+        in (unifyModuleSyntax m') // {
+          # used by generic closure to avoid duplicated imports.
+          key =
+            if isPath m then m
+            else m'.key or (newModuleName origin index);
+        };
+
+      getImports = m: m.imports or [];
+
+      newModuleName = origin: index:
+        "${origin.key}:<import-${toString index}>";
+
+      topLevel = {
+        key = "<top-level>";
+      };
+
+    in
+      (lazyGenericClosure {
+        startSet = imap (moduleImport topLevel) initModules;
+        operator = m: imap (moduleImport m) (getImports m);
+      });
+
+
+  moduleApply = funs: module:
+    lib.mapAttrs (name: value:
+      if builtins.hasAttr name funs then
+        let fun = lib.getAttr name funs; in
+        fun value
+      else
+        value
+    ) module;
+
+
+  # Handle mkMerge function left behind after a delay property.
+  moduleFlattenMerge = module:
+    if module ? config &&
+       isProperty module.config &&
+       isMerge module.config.property
+    then
+      (map (cfg: { key = module.key; config = cfg; }) module.config.content)
+      ++ [ (module // { config = {}; }) ]
+    else
+      [ module ];
+
+
+  # Handle mkMerge attributes which are left behind by previous delay
+  # properties and convert them into a list of modules. Delay properties
+  # inside the config attribute of a module and create a second module if a
+  # mkMerge attribute was left behind.
+  #
+  # Module -> [ Module ]
+  delayModule = module:
+    map (moduleApply { config = delayProperties; }) (moduleFlattenMerge module);
+
+
+  evalDefinitions = opt: values:
+    if opt.type.delayOnGlobalEval or false then
+      map (delayPropertiesWithIter opt.type.iter opt.name)
+        (evalLocalProperties values)
+    else
+      evalProperties values;
+
+
+  selectModule = name: m:
+    { inherit (m) key;
+    } // (
+      if m ? options && builtins.hasAttr name m.options then
+        { options = lib.getAttr name m.options; }
+      else {}
+    ) // (
+      if m ? config && builtins.hasAttr name m.config then
+        { config = lib.getAttr name m.config; }
+      else {}
+    );
+
+  filterModules = name: modules:
+    filter (m: m ? config || m ? options) (
+      map (selectModule name) modules
+    );
+
+
+  modulesNames = modules:
+    lib.concatMap (m: []
+    ++ optionals (m ? options) (lib.attrNames m.options)
+    ++ optionals (m ? config) (lib.attrNames m.config)
+    ) modules;
+
+
+  moduleZip = funs: modules:
+    lib.mapAttrs (name: fun:
+      fun (catAttrs name modules)
+    ) funs;
+
+
+  moduleMerge = path: modules:
+    let modules_ = modules; in
+    let
+      addName = name:
+        if path == "" then name else path + "." + name;
+
+      modules = concatLists (map delayModule modules_);
+
+      modulesOf = name: filterModules name modules;
+      declarationsOf = name: filter (m: m ? options) (modulesOf name);
+      definitionsOf  = name: filter (m: m ? config ) (modulesOf name);
+
+      recurseInto = name:
+        moduleMerge (addName name) (modulesOf name);
+
+      recurseForOption = name: modules: args:
+        moduleMerge name (
+          moduleClosure modules args
+        );
+
+      errorSource = modules:
+        "The error may come from the following files:\n" + (
+          lib.concatStringsSep "\n" (
+            map (m:
+              if m ? key then toString m.key else "<unknown location>"
+            ) modules
+          )
+        );
+
+      eol = "\n";
+
+      allNames = modulesNames modules;
+
+      getResults = m:
+        let fetchResult = s: mapAttrs (n: v: v.result) s; in {
+          options = fetchResult m.options;
+          config = fetchResult m.config;
+        };
+
+      endRecursion =  { options = {}; config = {}; };
+
+    in if modules == [] then endRecursion else
+      getResults (fix (crossResults: moduleZip {
+        options = lib.zipWithNames allNames (name: values: rec {
+          config = lib.getAttr name crossResults.config;
+
+          declarations = declarationsOf name;
+          declarationSources =
+            map (m: {
+              source = m.key;
+            }) declarations;
+
+          hasOptions = values != [];
+          isOption = any lib.isOption values;
+
+          decls = # add location to sub-module options.
+            map (m:
+              mapSubOptions
+                (unifyOptionModule {inherit (m) key;} name)
+                m.options
+            ) declarations;
+
+          decl =
+            lib.addErrorContext "${eol
+              }while enhancing option `${addName name}':${eol
+              }${errorSource declarations}${eol
+            }" (
+              addOptionMakeUp
+                { name = addName name; recurseInto = recurseForOption; }
+                (mergeOptionDecls decls)
+            );
+
+          value = decl // (with config; {
+            inherit (config) isNotDefined;
+            isDefined = ! isNotDefined;
+            declarations = declarationSources;
+            definitions = definitionSources;
+            config = strictResult;
+          });
+
+          recurse = (recurseInto name).options;
+
+          result =
+            if isOption then value
+            else if !hasOptions then {}
+            else if all isAttrs values then recurse
+            else
+              throw "${eol
+                }Unexpected type where option declarations are expected.${eol
+                }${errorSource declarations}${eol
+              }";
+
+        });
+
+        config = lib.zipWithNames allNames (name: values_: rec {
+          option = lib.getAttr name crossResults.options;
+
+          definitions = definitionsOf name;
+          definitionSources =
+            map (m: {
+              source = m.key;
+              value = m.config;
+            }) definitions;
+
+          values = values_ ++
+            optionals (option.isOption && option.decl ? extraConfigs)
+              option.decl.extraConfigs;
+
+          defs = evalDefinitions option.decl values;
+
+          isNotDefined = defs == [];
+
+          value =
+            lib.addErrorContext "${eol
+              }while evaluating the option `${addName name}':${eol
+              }${errorSource (modulesOf name)}${eol
+            }" (
+              let opt = option.decl; in
+              opt.apply (
+                if isNotDefined then
+                  opt.default or (throw "Option `${addName name}' not defined and does not have a default value.")
+                else opt.merge defs
+              )
+            );
+
+          strictResult = builtins.tryEval (builtins.toXML value);
+
+          recurse = (recurseInto name).config;
+
+          configIsAnOption = v: isOption (rmProperties v);
+          errConfigIsAnOption =
+            let badModules = filter (m: configIsAnOption m.config) definitions; in
+            "${eol
+              }Option ${addName name} is defined in the configuration section.${eol
+              }${errorSource badModules}${eol
+            }";
+
+          errDefinedWithoutDeclaration =
+            let badModules = definitions; in
+            "${eol
+              }Option '${addName name}' defined without option declaration.${eol
+              }${errorSource badModules}${eol
+            }";
+
+          result =
+            if option.isOption then value
+            else if !option.hasOptions then throw errDefinedWithoutDeclaration
+            else if any configIsAnOption values then throw errConfigIsAnOption
+            else if all isAttrs values then recurse
+            # plain value during the traversal
+            else throw errDefinedWithoutDeclaration;
+
+        });
+      } modules));
+
+
+  fixMergeModules = initModules: {...}@args:
+    lib.fix (result:
+      # This trick avoids an infinite loop because names of attribute
+      # are know and it is not required to evaluate the result of
+      # moduleMerge to know which attributes are present as arguments.
+      let module = { inherit (result) options config; }; in
+      moduleMerge "" (
+        moduleClosure initModules (module // args)
+      )
+    );
+
+
+  # Visit all definitions to raise errors related to undeclared options.
+  checkModule = path: {config, options, ...}@m:
+    let
+      eol = "\n";
+      addName = name:
+        if path == "" then name else path + "." + name;
+    in
+    if lib.isOption options then
+      if options ? options then
+        options.type.fold
+          (cfg: res: res && checkModule (options.type.docPath path) cfg._args)
+          true config
+      else
+        true
+    else if isAttrs options && lib.attrNames m.options != [] then
+      all (name:
+        lib.addErrorContext "${eol
+          }while checking the attribute `${addName name}':${eol
+        }" (checkModule (addName name) (selectModule name m))
+      ) (lib.attrNames m.config)
+    else
+      builtins.trace "try to evaluate config ${lib.showVal config}."
+      false;
+
+}
diff --git a/lib/options.nix b/lib/options.nix
new file mode 100644
index 000000000000..e8e01083a77a
--- /dev/null
+++ b/lib/options.nix
@@ -0,0 +1,315 @@
+# Nixpkgs/NixOS option handling.
+
+let lib = import ./default.nix; in
+
+with { inherit (builtins) head length; };
+with import ./trivial.nix;
+with import ./lists.nix;
+with import ./misc.nix;
+with import ./attrsets.nix;
+with import ./properties.nix;
+
+rec {
+
+  inherit (lib) isType;
+  
+
+  isOption = isType "option";
+  mkOption = attrs: attrs // {
+    _type = "option";
+    # name (this is the name of the attributem it is automatically generated by the traversal)
+    # default (value used when no definition exists)
+    # example (documentation)
+    # description (documentation)
+    # type (option type, provide a default merge function and ensure type correctness)
+    # merge (function used to merge definitions into one definition: [ /type/ ] -> /type/)
+    # apply (convert the option value to ease the manipulation of the option result)
+    # options (set of sub-options declarations & definitions)
+    # extraConfigs (list of possible configurations)
+  };
+
+  mkEnableOption = name: mkOption {
+    default = false;
+    example = true;
+    description = "Whether to enable ${name}";
+    type = lib.types.bool;
+  };
+
+  mapSubOptions = f: opt:
+    if opt ? options then
+      opt // {
+        options = imap f (toList opt.options);
+      }
+    else
+      opt;
+
+  # Make the option declaration more user-friendly by adding default
+  # settings and some verifications based on the declaration content (like
+  # type correctness).
+  addOptionMakeUp = {name, recurseInto}: decl:
+    let
+      init = {
+        inherit name;
+        merge = mergeDefaultOption;
+        apply = lib.id;
+      };
+
+      functionsFromType = opt:
+        opt // (builtins.intersectAttrs { merge = 1; check = 1; } (decl.type or {})); 
+
+      addDeclaration = opt: opt // decl;
+
+      ensureMergeInputType = opt:
+        if opt ? check then
+          opt // {
+            merge = list:
+              if all opt.check list then
+                opt.merge list
+              else
+                throw "One of option ${name} values has a bad type.";
+          }
+        else opt;
+
+      checkDefault = opt:
+        if opt ? check && opt ? default then
+          opt // {
+            default =
+              if opt.check opt.default then
+                opt.default
+              else
+                throw "The default value of option ${name} has a bad type.";
+          }
+        else opt;
+
+      handleOptionSets = opt:
+        if opt ? type && opt.type.hasOptions then
+          let
+            # Evaluate sub-modules.
+            subModuleMerge = path: vals:
+              lib.fix (args:
+                let
+                  result = recurseInto path (opt.options ++ imap (index: v: args: {
+                    key = rec {
+                      #!!! Would be nice if we had the file the val was from
+                      option = path;
+                      number = index;
+                      outPath = "option ${option} config number ${toString number}";
+                    };
+                  } // (lib.applyIfFunction v args)) (toList vals)) args;
+                  name = lib.removePrefix (opt.name + ".") path;
+                  extraArgs = opt.extraArgs or {};
+                  individualExtraArgs = opt.individualExtraArgs or {};
+                in {
+                  inherit (result) config options;
+                  inherit name;
+                } //
+                  (opt.extraArgs or {}) //
+                  (if hasAttr name individualExtraArgs then getAttr name individualExtraArgs else {})
+              );
+
+            # Add _options in sub-modules to make it viewable from other
+            # modules.
+            subModuleMergeConfig = path: vals:
+              let result = subModuleMerge path vals; in
+                { _args = result; } // result.config;
+
+          in
+            opt // {
+              merge = list:
+                opt.type.iter
+                  subModuleMergeConfig
+                  opt.name
+                  (opt.merge list);
+              options =
+                let path = opt.type.docPath opt.name; in
+                  (subModuleMerge path []).options;
+            }
+        else
+          opt;
+    in
+      foldl (opt: f: f opt) init [
+        # default settings
+        functionsFromType
+
+        # user settings
+        addDeclaration
+
+        # override settings
+        ensureMergeInputType
+        checkDefault
+        handleOptionSets
+      ];
+
+  # Merge a list of options containning different field.  This is useful to
+  # separate the merge & apply fields from the interface.
+  mergeOptionDecls = opts:
+    if opts == [] then {}
+    else if length opts == 1 then
+      let opt = head opts; in
+      if opt ? options then
+        opt // { options = toList opt.options; }
+      else
+        opt
+    else
+      fold (opt1: opt2:
+        lib.addErrorContext "opt1 = ${lib.showVal opt1}\nopt2 = ${lib.showVal opt2}" (
+        # You cannot merge if two options have the same field.
+        assert opt1 ? default -> ! opt2 ? default;
+        assert opt1 ? example -> ! opt2 ? example;
+        assert opt1 ? description -> ! opt2 ? description;
+        assert opt1 ? merge -> ! opt2 ? merge;
+        assert opt1 ? apply -> ! opt2 ? apply;
+        assert opt1 ? type -> ! opt2 ? type;
+        opt1 // opt2
+        // optionalAttrs (opt1 ? options || opt2 ? options) {
+            options =
+               (toList (opt1.options or []))
+            ++ (toList (opt2.options or []));
+          }
+        // optionalAttrs (opt1 ? extraConfigs || opt2 ? extraConfigs) {
+            extraConfigs = opt1.extraConfigs or [] ++ opt2.extraConfigs or [];
+          }
+        // optionalAttrs (opt1 ? extraArgs || opt2 ? extraArgs) {
+            extraArgs = opt1.extraArgs or {} // opt2.extraArgs or {};
+          }
+        // optionalAttrs (opt1 ? individualExtraArgs || opt2 ? individualExtraArgs) {
+            individualExtraArgs = zipAttrsWith (name: values:
+              if length values == 1 then head values else (head values // (head (tail values)))
+            ) [ (opt1.individualExtraArgs or {}) (opt2.individualExtraArgs or {}) ];
+          }
+      )) {} opts;
+
+  
+  # !!! This function will be removed because this can be done with the
+  # multiple option declarations.
+  addDefaultOptionValues = defs: opts: opts //
+    builtins.listToAttrs (map (defName:
+      { name = defName;
+        value = 
+          let
+            defValue = builtins.getAttr defName defs;
+            optValue = builtins.getAttr defName opts;
+          in
+          if isOption defValue
+          then
+            # `defValue' is an option.
+            if hasAttr defName opts
+            then builtins.getAttr defName opts
+            else defValue.default
+          else
+            # `defValue' is an attribute set containing options.
+            # So recurse.
+            if hasAttr defName opts && isAttrs optValue 
+            then addDefaultOptionValues defValue optValue
+            else addDefaultOptionValues defValue {};
+      }
+    ) (attrNames defs));
+
+  mergeDefaultOption = list:
+    if length list == 1 then head list
+    else if all builtins.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 builtins.isBool list then fold lib.or false list
+    else if all builtins.isString list then lib.concatStrings list
+    else if all builtins.isInt list && all (x: x == head list) list
+         then head 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 happen."
+    else if length list != 1 then throw "Multiple definitions. Only one is allowed for this option."
+    else head list;
+
+
+  fixableMergeFun = merge: f: config:
+    merge (
+      # generate the list of option sets.
+      f config
+    );
+
+  fixableMergeModules = merge: initModules: {...}@args: config:
+    fixableMergeFun merge (config:
+      lib.moduleClosure initModules (args // { inherit config; })
+    ) config;
+
+
+  fixableDefinitionsOf = initModules: {...}@args:
+    fixableMergeModules (modules: (lib.moduleMerge "" modules).config) initModules args;
+
+  fixableDeclarationsOf = initModules: {...}@args:
+    fixableMergeModules (modules: (lib.moduleMerge "" modules).options) initModules args;
+
+  definitionsOf = initModules: {...}@args:
+    (lib.fix (module:
+      fixableMergeModules (lib.moduleMerge "") initModules args module.config
+    )).config;
+
+  declarationsOf = initModules: {...}@args:
+    (lib.fix (module:
+      fixableMergeModules (lib.moduleMerge "") initModules args module.config
+    )).options;
+
+
+  # Generate documentation template from the list of option declaration like
+  # the set generated with filterOptionSets.
+  optionAttrSetToDocList = ignore: newOptionAttrSetToDocList;
+  newOptionAttrSetToDocList = attrs:
+    let options = collect isOption attrs; in
+      fold (opt: rest:
+        let
+          docOption = {
+            inherit (opt) name;
+            description = if opt ? description then opt.description else
+              throw "Option ${opt.name}: No description.";
+
+            declarations = map (x: toString x.source) opt.declarations;
+            #definitions = map (x: toString x.source) opt.definitions;
+          }
+          // optionalAttrs (opt ? example) { example = scrubOptionValue opt.example; }
+          // optionalAttrs (opt ? default) { default = scrubOptionValue opt.default; }
+          // optionalAttrs (opt ? defaultText) { default = opt.defaultText; };
+
+          subOptions =
+            if opt ? options then
+              newOptionAttrSetToDocList opt.options
+            else
+              [];
+        in
+          [ docOption ] ++ subOptions ++ rest
+      ) [] options;
+
+
+  /* This function recursively removes all derivation attributes from
+     `x' except for the `name' attribute.  This is to make the
+     generation of `options.xml' much more efficient: the XML
+     representation of derivations is very large (on the order of
+     megabytes) and is not actually used by the manual generator. */
+  scrubOptionValue = x: 
+    if isDerivation x then { type = "derivation"; drvPath = x.name; outPath = x.name; name = x.name; }
+    else if isList x then map scrubOptionValue x
+    else if isAttrs x then mapAttrs (n: v: scrubOptionValue v) (removeAttrs x ["_args"])
+    else x;
+
+
+  /* For use in the ‘example’ option attribute.  It causes the given
+     text to be included verbatim in documentation.  This is necessary
+     for example values that are not simple values, e.g.,
+     functions. */
+  literalExample = text: { _type = "literalExample"; inherit text; };
+
+
+}
diff --git a/lib/platforms.nix b/lib/platforms.nix
new file mode 100644
index 000000000000..8be37d7ed1e7
--- /dev/null
+++ b/lib/platforms.nix
@@ -0,0 +1,16 @@
+let lists = import ./lists.nix; in
+
+rec {
+  gnu = linux; /* ++ hurd ++ kfreebsd ++ ... */
+  linux = ["i686-linux" "x86_64-linux" "powerpc-linux" "armv5tel-linux" "armv7l-linux" "mips64el-linux"];
+  darwin = ["x86_64-darwin"];
+  freebsd = ["i686-freebsd" "x86_64-freebsd" "powerpc-freebsd"];
+  openbsd = ["i686-openbsd" "x86_64-openbsd"];
+  netbsd = ["i686-netbsd" "x86_64-netbsd"];
+  cygwin = ["i686-cygwin"];
+  unix = linux ++ darwin ++ freebsd ++ openbsd;
+  all = linux ++ darwin ++ cygwin ++ freebsd ++ openbsd;
+  none = [];
+  allBut = platform: lists.filter (x: platform != x) all;
+  mesaPlatforms = ["i686-linux" "x86_64-linux" "x86_64-darwin" "armv5tel-linux" "armv6l-linux"];
+}
diff --git a/lib/properties.nix b/lib/properties.nix
new file mode 100644
index 000000000000..22aa8d891d8a
--- /dev/null
+++ b/lib/properties.nix
@@ -0,0 +1,464 @@
+# Nixpkgs/NixOS properties.  Generalize the problem of delayable (not yet
+# evaluable) properties like mkIf.
+
+let lib = import ./default.nix; in
+
+with { inherit (builtins) head tail; };
+with import ./trivial.nix;
+with import ./lists.nix;
+with import ./misc.nix;
+with import ./attrsets.nix;
+
+rec {
+
+  inherit (lib) isType;
+
+  # Tell that nothing is defined.  When properties are evaluated, this type
+  # is used to remove an entry.  Thus if your property evaluation semantic
+  # implies that you have to mute the content of an attribute, then your
+  # property should produce this value.
+  isNotdef = isType "notdef";
+  mkNotdef = {_type = "notdef";};
+
+  # General property type, it has a property attribute and a content
+  # attribute.  The property attribute refers to an attribute set which
+  # contains a _type attribute and a list of functions which are used to
+  # evaluate this property.  The content attribute is used to stack properties
+  # on top of each other.
+  #
+  # The optional functions which may be contained in the property attribute
+  # are:
+  #  - onDelay: run on a copied property.
+  #  - onGlobalDelay: run on all copied properties.
+  #  - onEval: run on an evaluated property.
+  #  - onGlobalEval: run on a list of property stack on top of their values.
+  isProperty = isType "property";
+  mkProperty = p@{property, content, ...}: p // {
+    _type = "property";
+  };
+
+  # Go through the stack of properties and apply the function `op' on all
+  # property and call the function `nul' on the final value which is not a
+  # property.  The stack is traversed in reversed order.  The `op' function
+  # should expect a property with a content which have been modified.
+  #
+  # Warning: The `op' function expects only one argument in order to avoid
+  # calls to mkProperties as the argument is already a valid property which
+  # contains the result of the folding inside the content attribute.
+  foldProperty = op: nul: attrs:
+    if isProperty attrs then
+      op (attrs // {
+        content = foldProperty op nul attrs.content;
+      })
+    else
+      nul attrs;
+
+  # Simple function which can be used as the `op' argument of the
+  # foldProperty function.  Properties that you don't want to handle can be
+  # ignored with the `id' function.  `isSearched' is a function which should
+  # check the type of a property and return a boolean value.  `thenFun' and
+  # `elseFun' are functions which behave as the `op' argument of the
+  # foldProperty function.
+  foldFilter = isSearched: thenFun: elseFun: attrs:
+    if isSearched attrs.property then
+      thenFun attrs
+    else
+      elseFun attrs;
+
+
+  # Move properties from the current attribute set to the attribute
+  # contained in this attribute set.  This trigger property handlers called
+  # `onDelay' and `onGlobalDelay'.
+  delayPropertiesWithIter = iter: path: attrs:
+    let cleanAttrs = rmProperties attrs; in
+    if isProperty attrs then
+      iter (a: v:
+        lib.addErrorContext "while moving properties on the attribute `${a}':" (
+          triggerPropertiesGlobalDelay a (
+            triggerPropertiesDelay a (
+              copyProperties attrs v
+      )))) path cleanAttrs
+    else
+      attrs;
+
+  delayProperties = # implicit attrs argument.
+    let
+      # mapAttrs except that it also recurse into potential mkMerge
+      # functions.  This may cause a strictness issue because looking the
+      # type of a string implies evaluating it.
+      iter = fun: path: value:
+        lib.mapAttrs (attr: val:
+          if isProperty val && isMerge val.property then
+            val // { content = map (fun attr) val.content; }
+          else
+            fun attr val
+        ) value;
+    in
+      delayPropertiesWithIter iter "";
+
+  # Call onDelay functions.
+  triggerPropertiesDelay = name: attrs:
+    let
+      callOnDelay = p@{property, ...}:
+        if property ? onDelay then
+          property.onDelay name p
+        else
+          p;
+    in
+      foldProperty callOnDelay id attrs;
+
+  # Call onGlobalDelay functions.
+  triggerPropertiesGlobalDelay = name: attrs:
+    let
+      globalDelayFuns = uniqListExt {
+        getter = property: property._type;
+        inputList = foldProperty (p@{property, content, ...}:
+          if property ? onGlobalDelay then
+            [ property ] ++ content
+          else
+            content
+        ) (a: []) attrs;
+      };
+
+      callOnGlobalDelay = property: content:
+        property.onGlobalDelay name content;
+    in
+      fold callOnGlobalDelay attrs globalDelayFuns;
+
+  # Expect a list of values which may have properties and return the same
+  # list of values where all properties have been evaluated and where all
+  # ignored values are removed.  This trigger property handlers called
+  # `onEval' and `onGlobalEval'.
+  evalProperties = valList:
+    if valList != [] then
+      filter (x: !isNotdef x) (
+        triggerPropertiesGlobalEval (
+          evalLocalProperties valList
+        )
+      )
+    else
+      valList;
+
+  evalLocalProperties = valList:
+    filter (x: !isNotdef x) (
+      map triggerPropertiesEval valList
+    );
+
+  # Call onEval function
+  triggerPropertiesEval = val:
+    foldProperty (p@{property, ...}:
+      if property ? onEval then
+        property.onEval p
+      else
+        p
+    ) id val;
+
+  # Call onGlobalEval function
+  triggerPropertiesGlobalEval = valList:
+    let
+      globalEvalFuns = uniqListExt {
+        getter = property: property._type;
+        inputList =
+          fold (attrs: list:
+            foldProperty (p@{property, content, ...}:
+              if property ? onGlobalEval then
+                [ property ] ++ content
+              else
+                content
+            ) (a: list) attrs
+          ) [] valList;
+      };
+
+      callOnGlobalEval = property: valList: property.onGlobalEval valList;
+    in
+      fold callOnGlobalEval valList globalEvalFuns;
+
+  # Remove all properties on top of a value and return the value.
+  rmProperties =
+    foldProperty (p@{content, ...}: content) id;
+
+  # Copy properties defined on a value on another value.
+  copyProperties = attrs: newAttrs:
+    foldProperty id (x: newAttrs) attrs;
+
+  /* Merge. */
+
+  # Create "merge" statement which is skipped by the delayProperty function
+  # and interpreted by the underlying system using properties (modules).
+
+  # Create a "Merge" property which only contains a condition.
+  isMerge = isType "merge";
+  mkMerge = content: mkProperty {
+    property = {
+      _type = "merge";
+      onDelay = name: val: throw "mkMerge is not the first of the list of properties.";
+      onEval = val: throw "mkMerge is not allowed on option definitions.";
+    };
+    inherit content;
+  };
+
+  /* If. ThenElse. Always. */
+
+  # create "if" statement that can be delayed on sets until a "then-else" or
+  # "always" set is reached.  When an always set is reached the condition
+  # is ignore.
+
+  # Create a "If" property which only contains a condition.
+  isIf = isType "if";
+  mkIf = condition: content: mkProperty {
+    property = {
+      _type = "if";
+      onGlobalDelay = onIfGlobalDelay;
+      onEval = onIfEval;
+      inherit condition;
+    };
+    inherit content;
+  };
+
+  mkAssert = assertion: message: content:
+    mkIf
+      (if assertion then true else throw "\nFailed assertion: ${message}")
+      content;
+
+  # Evaluate the "If" statements when either "ThenElse" or "Always"
+  # statement is encountered.  Otherwise it removes multiple If statements and
+  # replaces them by one "If" statement where the condition is the list of all
+  # conditions joined with a "and" operation.
+  onIfGlobalDelay = name: content:
+    let
+      # extract if statements and non-if statements and repectively put them
+      # in the attribute list and attrs.
+      ifProps =
+        foldProperty
+          (foldFilter (p: isIf p)
+            # then, push the condition inside the list list
+            (p@{property, content, ...}:
+              { inherit (content) attrs;
+                list = [property] ++ content.list;
+              }
+            )
+            # otherwise, add the propertie.
+            (p@{property, content, ...}:
+              { inherit (content) list;
+                attrs = p // { content = content.attrs; };
+              }
+            )
+          )
+          (attrs: { list = []; inherit attrs; })
+          content;
+
+      # compute the list of if statements.
+      evalIf = content: condition: list:
+        if list == [] then
+          mkIf condition content
+        else
+          let p = head list; in
+          evalIf content (condition && p.condition) (tail list);
+    in
+      evalIf ifProps.attrs true ifProps.list;
+
+  # Evaluate the condition of the "If" statement to either get the value or
+  # to ignore the value.
+  onIfEval = p@{property, content, ...}:
+    if property.condition then
+      content
+    else
+      mkNotdef;
+
+  /* mkOverride */
+
+  # Create an "Override" statement which allow the user to define
+  # priorities between values.  The default priority is 100. The lowest
+  # priorities are kept.  The template argument must reproduce the same
+  # attribute set hierarchy to override leaves of the hierarchy.
+  isOverride = isType "override";
+  mkOverrideTemplate = priority: template: content: mkProperty {
+    property = {
+      _type = "override";
+      onDelay = onOverrideDelay;
+      onGlobalEval = onOverrideGlobalEval;
+      inherit priority template;
+    };
+    inherit content;
+  };
+
+  # Like mkOverrideTemplate, but without the template argument.
+  mkOverride = priority: content: mkOverrideTemplate priority {} content;
+
+  # Sugar to override the default value of the option by making a new
+  # default value based on the configuration.
+  mkDefaultValue = mkOverride 1000;
+  mkDefault = mkOverride 1000;
+  mkForce = mkOverride 50;
+  mkStrict = mkOverride 0;
+
+  # Make the template traversal in function of the property traversal.  If
+  # the template define a non-empty attribute set, then the property is
+  # copied only on all mentionned attributes inside the template.
+  # Otherwise, the property is kept on all sub-attribute definitions.
+  onOverrideDelay = name: p@{property, content, ...}:
+    let inherit (property) template; in
+    if isAttrs template && template != {} then
+      if hasAttr name template then
+        p // {
+          property = p.property // {
+            template = builtins.getAttr name template;
+          };
+        }
+      # Do not override the attribute \name\
+      else
+        content
+    # Override values defined inside the attribute \name\.
+    else
+      p;
+
+  # Keep values having lowest priority numbers only throwing away those having
+  # a higher priority assigned.
+  onOverrideGlobalEval = valList:
+    let
+      defaultPrio = 100;
+
+      inherit (builtins) lessThan;
+
+      getPrioVal =
+        foldProperty
+          (foldFilter isOverride
+            (p@{property, content, ...}:
+              if content ? priority && lessThan content.priority property.priority then
+                content
+              else
+                content // {
+                  inherit (property) priority;
+                }
+            )
+            (p@{property, content, ...}:
+              content // {
+                value = p // { content = content.value; };
+              }
+            )
+          ) (value: { inherit value; });
+
+      addDefaultPrio = x:
+        if x ? priority then x
+        else x // { priority = defaultPrio; };
+
+      prioValList = map (x: addDefaultPrio (getPrioVal x)) valList;
+
+      higherPrio =
+        if prioValList == [] then
+          defaultPrio
+        else
+          fold (x: min:
+            if lessThan x.priority min then
+              x.priority
+            else
+              min
+          ) (head prioValList).priority (tail prioValList);
+    in
+      map (x:
+        if x.priority == higherPrio then
+          x.value
+        else
+          mkNotdef
+      ) prioValList;
+
+  /* mkOrder */
+
+  # Order definitions based on there index value.  This property is useful
+  # when the result of the merge function depends on the order on the
+  # initial list.  (e.g. concatStrings) Definitions are ordered based on
+  # their rank.  The lowest ranked definition would be the first to element
+  # of the list used by the merge function.  And the highest ranked
+  # definition would be the last.  Definitions which does not have any rank
+  # value have the default rank of 100.
+  isOrder = isType "order";
+  mkOrder = rank: content: mkProperty {
+    property = {
+      _type = "order";
+      onGlobalEval = onOrderGlobalEval;
+      inherit rank;
+    };
+    inherit content;
+  };
+
+  mkHeader = mkOrder 10;
+  mkFooter = mkOrder 1000;
+
+  # Fetch the rank of each definition (add the default rank is none) and
+  # sort them based on their ranking.
+  onOrderGlobalEval = valList:
+    let
+      defaultRank = 100;
+
+      inherit (builtins) lessThan;
+
+      getRankVal =
+        foldProperty
+          (foldFilter isOrder
+            (p@{property, content, ...}:
+              if content ? rank then
+                content
+              else
+                content // {
+                  inherit (property) rank;
+                }
+            )
+            (p@{property, content, ...}:
+              content // {
+                value = p // { content = content.value; };
+              }
+            )
+          ) (value: { inherit value; });
+
+      addDefaultRank = x:
+        if x ? rank then x
+        else x // { rank = defaultRank; };
+
+      rankValList = map (x: addDefaultRank (getRankVal x)) valList;
+
+      cmp = x: y:
+        builtins.lessThan x.rank y.rank;
+    in
+      map (x: x.value) (sort cmp rankValList);
+
+  /* mkFixStrictness */
+
+  # This is a hack used to restore laziness on some option definitions.
+  # Some option definitions are evaluated when they are not used.  This
+  # error is caused by the strictness of type checking builtins.  Builtins
+  # like 'isAttrs' are too strict because they have to evaluate their
+  # arguments to check if the type is correct.  This evaluation, cause the
+  # strictness of properties.
+  #
+  # Properties can be stacked on top of each other.  The stackability of
+  # properties on top of the option definition is nice for user manipulation
+  # but require to check if the content of the property is not another
+  # property.  Such testing implies to verify if this is an attribute set
+  # and if it possess the type 'property'. (see isProperty & typeOf/isType)
+  #
+  # To avoid strict evaluation of option definitions, 'mkFixStrictness' is
+  # introduced.  This property protects an option definition by replacing
+  # the base of the stack of properties by 'mkNotDef', when this property is
+  # evaluated it returns the original definition.
+  #
+  # This property is useful over any elements which depends on options which
+  # are raising errors when they get evaluated without the proper settings.
+  #
+  # Plain list and attribute set are lazy structures, which means that the
+  # container gets evaluated but not the content.  Thus, using this property
+  # on top of plain list or attribute set is pointless.
+  #
+  # This is a Hack, you should avoid it!
+
+  # This property has a long name because you should avoid it.
+  isFixStrictness = attrs: (typeOf attrs) == "fix-strictness";
+  mkFixStrictness = value:
+    mkProperty {
+      property = {
+        _type = "fix-strictness";
+        onEval = p: value;
+      };
+      content = mkNotdef;
+    };
+
+}
diff --git a/lib/sources.nix b/lib/sources.nix
new file mode 100644
index 000000000000..6f8554d340be
--- /dev/null
+++ b/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 == ".git" || 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/lib/strings-with-deps.nix b/lib/strings-with-deps.nix
new file mode 100644
index 000000000000..3ad3e5991506
--- /dev/null
+++ b/lib/strings-with-deps.nix
@@ -0,0 +1,78 @@
+/*
+Usage:
+
+  You define you custom builder script by adding all build steps to a list.
+  for example:
+       builder = writeScript "fsg-4.4-builder"
+               (textClosure [doUnpack addInputs preBuild doMake installPhase doForceShare]);
+
+  a step is defined by noDepEntry, fullDepEntry or packEntry.
+  To ensure that prerequisite are met those are added before the task itself by
+  textClosureDupList. Duplicated items are removed again.
+
+  See trace/nixpkgs/trunk/pkgs/top-level/builder-defs.nix for some predefined build steps
+
+  Attention:
+
+  let
+    pkgs = (import /etc/nixos/nixpkgs/pkgs/top-level/all-packages.nix) {};
+  in let
+    inherit (pkgs.stringsWithDeps) fullDepEntry packEntry noDepEntry textClosureMap;
+    inherit (pkgs.lib) id;
+
+    nameA = noDepEntry "Text a";
+    nameB = fullDepEntry "Text b" ["nameA"];
+    nameC = fullDepEntry "Text c" ["nameA"];
+
+    stages = {
+      nameHeader = noDepEntry "#! /bin/sh \n";
+      inherit nameA nameB nameC;
+    };
+  in
+    textClosureMap id stages
+    [ "nameHeader" "nameA" "nameB" "nameC"
+      nameC # <- added twice. add a dep entry if you know that it will be added once only [1]
+      "nameB" # <- this will not be added again because the attr name (reference) is used
+    ]
+
+  # result: Str("#! /bin/sh \n\nText a\nText b\nText c\nText c",[])
+
+  [1] maybe this behaviour should be removed to keep things simple (?)
+*/
+
+with import ./lists.nix;
+with import ./attrsets.nix;
+with import ./strings.nix;
+
+rec {
+
+  /* !!! The interface of this function is kind of messed up, since
+     it's way too overloaded and almost but not quite computes a
+     topological sort of the depstrings. */
+
+  textClosureList = predefined: arg:
+    let
+      f = done: todo:
+        if todo == [] then {result = []; inherit done;}
+        else
+          let entry = head todo; in
+          if isAttrs entry then
+            let x = f done entry.deps;
+                y = f x.done (tail todo);
+            in { result = x.result ++ [entry.text] ++ y.result;
+                 done = y.done;
+               }
+          else if hasAttr entry done then f done (tail todo)
+          else f (done // listToAttrs [{name = entry; value = 1;}]) ([(builtins.getAttr entry predefined)] ++ tail todo);
+    in (f {} arg).result;
+
+  textClosureMap = f: predefined: names:
+    concatStringsSep "\n" (map f (textClosureList predefined names));
+
+  noDepEntry = text: {inherit text; deps = [];};
+  fullDepEntry = text: deps: {inherit text deps;};
+  packEntry = deps: {inherit deps; text="";};
+
+  stringAfter = deps: text: { inherit text deps; };
+
+}
diff --git a/lib/strings.nix b/lib/strings.nix
new file mode 100644
index 000000000000..024a9ac7d7a2
--- /dev/null
+++ b/lib/strings.nix
@@ -0,0 +1,190 @@
+/* String manipulation functions. */
+
+let lib = import ./default.nix;
+
+inherit (builtins) add sub lessThan length;
+
+in
+
+rec {
+  inherit (builtins) stringLength substring head tail;
+
+
+  # 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);
+  concatImapStrings = f: list: concatStrings (lib.imap 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 == [] || length list == 1
+    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 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".
+  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, e.g. "${pkg1}/lib:${pkg2}/lib:...".
+  makeLibraryPath = makeSearchPath "lib";
+
+
+  # Idem for Perl search paths.
+  makePerlPath = makeSearchPath "lib/perl5/site_perl";
+
+
+  # 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 map (p: substring p 1 s) (lib.range 0 (sub l 1));
+
+
+  # Manipulate a string charcater by character and replace them by strings
+  # before concatenating the results.
+  stringAsChars = f: s:
+    concatStrings (
+      map f (stringToCharacters s)
+    );
+
+
+  # same as vim escape function.
+  # Each character contained in list is prefixed by "\"
+  escape = list : string :
+    stringAsChars (c: if lib.elem c list then "\\${c}" else c) string;
+
+
+  # still ugly slow. But more correct now
+  # [] for zsh
+  escapeShellArg = lib.escape (stringToCharacters "\\ ';$`()|<>\t*[]");
+
+
+  # replace characters by their substitutes.  This function is equivalent to
+  # the `tr' command except that one character can be replace by multiple
+  # ones.  e.g.,
+  # replaceChars ["<" ">"] ["&lt;" "&gt;"] "<foo>" returns "&lt;foo&gt;".
+  replaceChars = del: new: s:
+    let
+      subst = c:
+        (lib.fold
+          (sub: res: if sub.fst == c then sub else res)
+          {fst = c; snd = c;} (lib.zipLists del new)
+        ).snd;
+    in
+      stringAsChars subst s;
+
+
+  # Case conversion utilities
+  lowerChars = stringToCharacters "abcdefghijklmnopqrstuvwxyz";
+  upperChars = stringToCharacters "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
+  toLower = replaceChars upperChars lowerChars;
+  toUpper = replaceChars lowerChars upperChars;
+
+
+  # Compares strings not requiring context equality
+  # Obviously, a workaround but works on all Nix versions
+  eqStrings = a: b: (a+(substring 0 0 b)) == ((substring 0 0 a)+b);
+
+
+  # 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"].
+  splitString = sep: s:
+    let
+      sepLen = stringLength sep;
+      sLen = stringLength s;
+      lastSearch = sub sLen sepLen;
+      startWithSep = startAt:
+        substring startAt sepLen s == sep;
+
+      recurse = index: startAt:
+        let cutUntil = i: [(substring startAt (sub i startAt) s)]; in
+        if lessThan index lastSearch then
+          if startWithSep index then
+            let restartAt = add index sepLen; in
+            cutUntil index ++ recurse restartAt restartAt
+          else
+            recurse (add index 1) startAt
+        else
+          cutUntil sLen;
+    in
+      recurse 0 0;
+
+
+  # return the suffix of the second argument if the first argument match its
+  # prefix. e.g.,
+  # `removePrefix "foo." "foo.bar.baz"' returns "bar.baz".
+  removePrefix = pre: s:
+    let
+      preLen = stringLength pre;
+      sLen = stringLength s;
+    in
+      if pre == substring 0 preLen s then
+        substring preLen (sub sLen preLen) s
+      else
+        s;
+
+  # Return true iff string v1 denotes a version older than v2.
+  versionOlder = v1: v2: builtins.compareVersions v2 v1 == 1;
+
+
+  # Return true iff string v1 denotes a version equal to or newer than v2.
+  versionAtLeast = v1: v2: !versionOlder v1 v2;
+
+
+  # Get the version of the specified derivation, as specified in its
+  # ‘name’ attribute.
+  getVersion = drv: (builtins.parseDrvName drv.name).version;
+
+
+  # Extract name with version from URL. Ask for separator which is
+  # supposed to start extension
+  nameFromURL = url: sep: let
+    components = splitString "/" url;
+    filename = lib.last components;
+    name = builtins.head (splitString sep filename);
+  in
+  assert ! eqStrings name filename;
+  name;
+
+
+  # Create an --{enable,disable}-<feat> string that can be passed to
+  # standard GNU Autoconf scripts.
+  enableFeature = enable: feat: "--${if enable then "enable" else "disable"}-${feat}";
+
+}
diff --git a/lib/systems.nix b/lib/systems.nix
new file mode 100644
index 000000000000..1ef869fb0120
--- /dev/null
+++ b/lib/systems.nix
@@ -0,0 +1,126 @@
+# Define the list of system with their properties.  Only systems tested for
+# Nixpkgs are listed below
+
+with import ./lists.nix;
+with import ./types.nix;
+with import ./attrsets.nix;
+
+let
+  lib = import ./default.nix;
+  setTypes = type:
+    mapAttrs (name: value:
+      setType type ({inherit name;} // value)
+    );
+in
+
+rec {
+
+  isSignificantByte = isType "significant-byte";
+  significantBytes = setTypes "significant-byte" {
+    bigEndian = {};
+    littleEndian = {};
+  };
+
+
+  isCpuType = x: typeOf x == "cpu-type"
+    && elem x.bits [8 16 32 64 128]
+    && (builtins.lessThan 8 x.bits -> isSignificantByte x.significantByte);
+
+  cpuTypes = with significantBytes;
+    setTypes "cpu-type" {
+      arm =      { bits = 32; significantByte = littleEndian; };
+      armv5tel = { bits = 32; significantByte = littleEndian; };
+      armv7l   = { bits = 32; significantByte = littleEndian; };
+      i686 =     { bits = 32; significantByte = littleEndian; };
+      powerpc =  { bits = 32; significantByte = bigEndian; };
+      x86_64 =   { bits = 64; significantByte = littleEndian; };
+    };
+
+
+  isExecFormat = isType "exec-format";
+  execFormats = setTypes "exec-format" {
+    aout = {}; # a.out
+    elf = {};
+    macho = {};
+    pe = {};
+    unknow = {};
+  };
+
+
+  isKernel = isType "kernel";
+  kernels = with execFormats;
+    setTypes "kernel" {
+      cygwin =  { execFormat = pe; };
+      darwin =  { execFormat = macho; };
+      freebsd = { execFormat = elf; };
+      linux =   { execFormat = elf; };
+      netbsd =  { execFormat = elf; };
+      none =    { execFormat = unknow; };
+      openbsd = { execFormat = elf; };
+      win32 =   { execFormat = pe; };
+    };
+
+
+  isArchitecture = isType "architecture";
+  architectures = setTypes "architecture" {
+    apple = {};
+    pc = {};
+    unknow = {};
+  };
+
+
+  isSystem = x: typeOf x == "system"
+    && isCpuType x.cpu
+    && isArchitecture x.arch
+    && isKernel x.kernel;
+
+  mkSystem = {
+    cpu ? cpuTypes.i686,
+    arch ? architectures.pc,
+    kernel ? kernels.linux,
+    name ? "${cpu.name}-${arch.name}-${kernel.name}"
+  }: setType "system" {
+    inherit name cpu arch kernel;
+  };
+
+
+  isDarwin = matchAttrs { kernel = kernels.darwin; };
+  isLinux = matchAttrs { kernel = kernels.linux; };
+  isi686 = matchAttrs { cpu = cpuTypes.i686; };
+  is64Bit = matchAttrs { cpu = { bits = 64; }; };
+
+
+  # This should revert the job done by config.guess from the gcc compiler.
+  mkSystemFromString = s: let
+    l = lib.splitString "-" s;
+
+    getCpu = name:
+      attrByPath [name] (throw "Unknow cpuType `${name}'.")
+        cpuTypes;
+    getArch = name:
+      attrByPath [name] (throw "Unknow architecture `${name}'.")
+        architectures;
+    getKernel = name:
+      attrByPath [name] (throw "Unknow kernel `${name}'.")
+        kernels;
+
+    system =
+      if builtins.length l == 2 then
+        mkSystem rec {
+          name = s;
+          cpu = getCpu (head l);
+          arch =
+            if isDarwin system
+            then architectures.apple
+            else architectures.pc;
+          kernel = getKernel (head (tail l));
+        }
+      else
+        mkSystem {
+          name = s;
+          cpu = getCpu (head l);
+          arch = getArch (head (tail l));
+          kernel = getKernel (head (tail (tail l)));
+        };
+  in assert isSystem system; system;
+}
diff --git a/lib/tests.nix b/lib/tests.nix
new file mode 100644
index 000000000000..298bdffc3790
--- /dev/null
+++ b/lib/tests.nix
@@ -0,0 +1,113 @@
+let inherit (builtins) add; in
+with import ./default.nix;
+
+runTests {
+
+  testId = {
+    expr = id 1;
+    expected = 1;
+  };
+  
+  testConst = {
+    expr = const 2 3;
+    expected = 2;
+  };
+
+  /*
+  testOr = {
+    expr = or true false;
+    expected = true;
+  };
+  */
+  
+  testAnd = {
+    expr = and true false;
+    expected = false;
+  };
+  
+  testFix = {
+    expr = fix (x: {a = if x ? a then "a" else "b";});
+    expected = {a = "a";};
+  };
+
+  testConcatMapStrings = {
+    expr = concatMapStrings (x: x + ";") ["a" "b" "c"];
+    expected = "a;b;c;";
+  };
+
+  testConcatStringsSep = {
+    expr = concatStringsSep "," ["a" "b" "c"];
+    expected = "a,b,c";
+  };
+
+  testFilter = {
+    expr = filter (x: x != "a") ["a" "b" "c" "a"];
+    expected = ["b" "c"];
+  };
+
+  testFold = {
+    expr = fold (builtins.add) 0 (range 0 100);
+    expected = 5050;
+  };
+
+  testTake = testAllTrue [
+    ([] == (take 0 [  1 2 3 ]))
+    ([1] == (take 1 [  1 2 3 ]))
+    ([ 1 2 ] == (take 2 [  1 2 3 ]))
+    ([ 1 2 3 ] == (take 3 [  1 2 3 ]))
+    ([ 1 2 3 ] == (take 4 [  1 2 3 ]))
+  ];
+
+  testFoldAttrs = {
+    expr = foldAttrs (n: a: [n] ++ a) [] [
+    { a = 2; b = 7; }
+    { a = 3;        c = 8; }
+    ];
+    expected = { a = [ 2 3 ]; b = [7]; c = [8];};
+  };
+
+  testOverridableDelayableArgsTest = {
+    expr = 
+      let res1 = defaultOverridableDelayableArgs id {};
+          res2 = defaultOverridableDelayableArgs id { a = 7; };
+          res3 = let x = defaultOverridableDelayableArgs id { a = 7; };
+                 in (x.merge) { b = 10; };
+          res4 = let x = defaultOverridableDelayableArgs id { a = 7; };
+                in (x.merge) ( x: { b = 10; });
+          res5 = let x = defaultOverridableDelayableArgs id { a = 7; };
+                in (x.merge) ( x: { a = add x.a 3; });
+          res6 = let x = defaultOverridableDelayableArgs id { a = 7; mergeAttrBy = { a = add; }; };
+                     y = x.merge {};
+                in (y.merge) { a = 10; };
+
+          resRem7 = res6.replace (a : removeAttrs a ["a"]);
+
+          resReplace6 = let x = defaultOverridableDelayableArgs id { a = 7; mergeAttrBy = { a = add; }; };
+                            x2 = x.merge { a = 20; }; # now we have 27
+                        in (x2.replace) { a = 10; }; # and override the value by 10
+
+          # fixed tests (delayed args): (when using them add some comments, please)
+          resFixed1 = 
+                let x = defaultOverridableDelayableArgs id ( x : { a = 7; c = x.fixed.b; });
+                    y = x.merge (x : { name = "name-${builtins.toString x.fixed.c}"; });
+                in (y.merge) { b = 10; };
+          strip = attrs : removeAttrs attrs ["merge" "replace"];
+      in all id
+        [ ((strip res1) == { })
+          ((strip res2) == { a = 7; })
+          ((strip res3) == { a = 7; b = 10; })
+          ((strip res4) == { a = 7; b = 10; })
+          ((strip res5) == { a = 10; })
+          ((strip res6) == { a = 17; })
+          ((strip resRem7) == {})
+          ((strip resFixed1) == { a = 7; b = 10; c =10; name = "name-10"; })
+        ];
+    expected = true;
+  };
+
+  testSort = {
+    expr = sort builtins.lessThan [ 40 2 30 42 ];
+    expected = [2 30 40 42];
+  };
+  
+}
diff --git a/lib/trivial.nix b/lib/trivial.nix
new file mode 100644
index 000000000000..8af3474f2a67
--- /dev/null
+++ b/lib/trivial.nix
@@ -0,0 +1,38 @@
+with {
+  inherit (import ./lists.nix) deepSeqList;
+  inherit (import ./attrsets.nix) deepSeqAttrs;
+};
+
+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;
+
+  # Flip the order of the arguments of a binary function.
+  flip = f: a: b: f b a;
+
+  # `seq x y' evaluates x, then returns y.  That is, it forces strict
+  # evaluation of its first argument.
+  seq = x: y: if x == null then y else y;
+  
+  # Like `seq', but recurses into lists and attribute sets to force evaluation
+  # of all list elements/attributes.
+  deepSeq = x: y:
+    if builtins.isList x
+      then deepSeqList x y
+    else if builtins.isAttrs x
+      then deepSeqAttrs x y
+      else seq x y;
+}
diff --git a/lib/types.nix b/lib/types.nix
new file mode 100644
index 000000000000..156d72ac5e73
--- /dev/null
+++ b/lib/types.nix
@@ -0,0 +1,226 @@
+# Definitions related to run-time type checking.  Used in particular
+# to type-check NixOS configurations.
+
+let lib = import ./default.nix; in
+
+with import ./lists.nix;
+with import ./attrsets.nix;
+with import ./options.nix;
+with import ./trivial.nix;
+
+rec {
+
+  isType = type: x: (x._type or "") == type;
+  hasType = x: isAttrs x && x ? _type;
+  typeOf = x: x._type or "";
+
+  setType = typeName: value: value // {
+    _type = typeName;
+  };
+
+
+  # name (name of the type)
+  # check (check the config value. Before returning false it should trace the bad value eg using traceValIfNot)
+  # merge (default merge function)
+  # iter (iterate on all elements contained in this type)
+  # fold (fold all elements contained in this type)
+  # hasOptions (boolean: whatever this option contains an option set)
+  # delayOnGlobalEval (boolean: should properties go through the evaluation of this option)
+  # docPath (path concatenated to the option name contained in the option set)
+  isOptionType = isType "option-type";
+  mkOptionType =
+    { name
+    , check ? (x: true)
+    , merge ? mergeDefaultOption
+    # Handle complex structure types.
+    , iter ? (f: path: v: f path v)
+    , fold ? (op: nul: v: op v nul)
+    , docPath ? lib.id
+    # If the type can contains option sets.
+    , hasOptions ? false
+    , delayOnGlobalEval ? false
+    }:
+
+    { _type = "option-type";
+      inherit name check merge iter fold docPath hasOptions delayOnGlobalEval;
+    };
+
+
+  types = rec {
+
+    bool = mkOptionType {
+      name = "boolean";
+      check = lib.traceValIfNot builtins.isBool;
+      merge = fold lib.or false;
+    };
+
+    int = mkOptionType {
+      name = "integer";
+      check = lib.traceValIfNot builtins.isInt;
+    };
+
+    string = mkOptionType {
+      name = "string";
+      check = lib.traceValIfNot builtins.isString;
+      merge = lib.concatStrings;
+    };
+
+    # Like ‘string’, but add newlines between every value.  Useful for
+    # configuration file contents.
+    lines = mkOptionType {
+      name = "string";
+      check = lib.traceValIfNot builtins.isString;
+      merge = lib.concatStringsSep "\n";
+    };
+
+    envVar = mkOptionType {
+      name = "environment variable";
+      inherit (string) check;
+      merge = lib.concatStringsSep ":";
+    };
+
+    attrs = mkOptionType {
+      name = "attribute set";
+      check = lib.traceValIfNot isAttrs;
+      merge = fold lib.mergeAttrs {};
+    };
+
+    # derivation is a reserved keyword.
+    package = mkOptionType {
+      name = "derivation";
+      check = lib.traceValIfNot isDerivation;
+    };
+
+    path = mkOptionType {
+      name = "path";
+      # Hacky: there is no ‘isPath’ primop.
+      check = lib.traceValIfNot (x: builtins.unsafeDiscardStringContext (builtins.substring 0 1 (toString x)) == "/");
+    };
+
+    # drop this in the future:
+    list = builtins.trace "types.list is deprecated, use types.listOf instead" types.listOf;
+
+    listOf = elemType: mkOptionType { 
+      name = "list of ${elemType.name}s";
+      check = value: lib.traceValIfNot isList value && all elemType.check value;
+      merge = concatLists;
+      iter = f: path: list: map (elemType.iter f (path + ".*")) list;
+      fold = op: nul: list: lib.fold (e: l: elemType.fold op l e) nul list;
+      docPath = path: elemType.docPath (path + ".*");
+      inherit (elemType) hasOptions;
+
+      # You cannot define multiple configurations of one entity, therefore
+      # no reason justify to delay properties inside list elements.
+      delayOnGlobalEval = false;
+    };
+
+    attrsOf = elemType: mkOptionType {
+      name = "attribute set of ${elemType.name}s";
+      check = x: lib.traceValIfNot isAttrs x
+        && all elemType.check (lib.attrValues x); 
+      merge = lib.zipAttrsWith (name: elemType.merge);
+      iter = f: path: set: lib.mapAttrs (name: elemType.iter f (path + "." + name)) set;
+      fold = op: nul: set: fold (e: l: elemType.fold op l e) nul (lib.attrValues set);
+      docPath = path: elemType.docPath (path + ".<name>");
+      inherit (elemType) hasOptions delayOnGlobalEval;
+    };
+
+    # List or attribute set of ...
+    loaOf = elemType:
+      let
+        convertIfList = defIdx: def:
+          if isList def then
+            listToAttrs (
+              flip imap def (elemIdx: elem:
+                nameValuePair "unnamed-${toString defIdx}.${toString elemIdx}" elem))
+          else
+            def;
+        listOnly = listOf elemType;
+        attrOnly = attrsOf elemType;
+
+      in mkOptionType {
+        name = "list or attribute set of ${elemType.name}s";
+        check = x:
+          if isList x       then listOnly.check x
+          else if isAttrs x then attrOnly.check x
+          else lib.traceValIfNot (x: false) x;
+        ## The merge function returns an attribute set
+        merge = defs:
+          attrOnly.merge (imap convertIfList defs);
+        iter = f: path: def:
+          if isList def       then listOnly.iter f path def
+          else if isAttrs def then attrOnly.iter f path def
+          else throw "Unexpected value";
+        fold = op: nul: def:
+          if isList def       then listOnly.fold op nul def
+          else if isAttrs def then attrOnly.fold op nul def
+          else throw "Unexpected value";
+
+        docPath = path: elemType.docPath (path + ".<name?>");
+        inherit (elemType) hasOptions delayOnGlobalEval;
+      }
+    ;
+
+    uniq = elemType: mkOptionType {
+      inherit (elemType) name check iter fold docPath hasOptions;
+      merge = list:
+        if length list == 1 then
+          head list
+        else
+          throw "Multiple definitions of ${elemType.name}. Only one is allowed for this option.";
+    };
+
+    none = elemType: mkOptionType {
+      inherit (elemType) name check iter fold docPath hasOptions;
+      merge = list:
+        throw "No definitions are allowed for this option.";
+    };
+
+    nullOr = elemType: mkOptionType {
+      inherit (elemType) name merge docPath hasOptions;
+      check = x: builtins.isNull x || elemType.check x;
+      iter = f: path: v: if v == null then v else elemType.iter f path v;
+      fold = op: nul: v: if v == null then nul else elemType.fold op nul v;
+    };
+
+    functionTo = elemType: mkOptionType {
+      name = "function that evaluates to a(n) ${elemType.name}";
+      check = lib.traceValIfNot builtins.isFunction;
+      merge = fns:
+        args: elemType.merge (map (fn: fn args) fns);
+      # These are guesses, I don't fully understand iter, fold, delayOnGlobalEval
+      iter = f: path: v:
+        args: elemType.iter f path (v args);
+      fold = op: nul: v:
+        args: elemType.fold op nul (v args);
+      inherit (elemType) delayOnGlobalEval;
+      hasOptions = false;
+    };
+
+    # usually used with listOf, attrsOf, loaOf like this:
+    # users = mkOption {
+    #   type = loaOf optionSet;
+    #
+    #   # you can omit the list if there is one element only
+    #   options = [ {
+    #     name = mkOption {
+    #       description = "name of the user"
+    #       ...
+    #     };
+    #     # more options here
+    #   } { more options } ];
+    # }
+    # TODO: !!! document passing options as an argument to optionSet,
+    # deprecate the current approach.
+    optionSet = mkOptionType {
+      name = "option set";
+      # merge is done in "options.nix > addOptionMakeUp > handleOptionSets"
+      merge = lib.id;
+      check = x: isAttrs x || builtins.isFunction x;
+      hasOptions = true;
+      delayOnGlobalEval = true;
+    };
+
+  };
+
+}