about summary refs log tree commit diff
path: root/lib
diff options
context:
space:
mode:
Diffstat (limited to 'lib')
-rw-r--r--lib/customisation.nix18
-rw-r--r--lib/debug.nix24
-rw-r--r--lib/default.nix8
-rw-r--r--lib/deprecated.nix8
-rw-r--r--lib/filesystem.nix20
-rw-r--r--lib/fixed-points.nix78
-rw-r--r--lib/generators.nix37
-rw-r--r--lib/licenses.nix5
-rw-r--r--lib/lists.nix22
-rw-r--r--lib/maintainers.nix60
-rw-r--r--lib/meta.nix9
-rw-r--r--lib/modules.nix2
-rw-r--r--lib/strings.nix11
-rw-r--r--lib/systems/default.nix13
-rw-r--r--lib/systems/doubles.nix38
-rw-r--r--lib/systems/examples.nix130
-rw-r--r--lib/systems/inspect.nix43
-rw-r--r--lib/systems/parse.nix53
-rw-r--r--lib/systems/platforms.nix83
-rw-r--r--lib/tests/misc.nix (renamed from lib/tests.nix)219
-rw-r--r--lib/tests/release.nix58
-rw-r--r--lib/tests/systems.nix14
-rw-r--r--lib/trivial.nix76
-rw-r--r--lib/types.nix17
24 files changed, 785 insertions, 261 deletions
diff --git a/lib/customisation.nix b/lib/customisation.nix
index 6d6c023408782..98a46ca6c6168 100644
--- a/lib/customisation.nix
+++ b/lib/customisation.nix
@@ -51,6 +51,24 @@ rec {
        else { }));
 
 
+  /* `makeOverridable` takes a function from attribute set to attribute set and
+     injects `override` attibute which can be used to override arguments of
+     the function.
+
+       nix-repl> x = {a, b}: { result = a + b; }
+
+       nix-repl> y = lib.makeOverridable x { a = 1; b = 2; }
+
+       nix-repl> y
+       { override = «lambda»; overrideDerivation = «lambda»; result = 3; }
+
+       nix-repl> y.override { a = 10; }
+       { override = «lambda»; overrideDerivation = «lambda»; result = 12; }
+
+     Please refer to "Nixpkgs Contributors Guide" section
+     "<pkg>.overrideDerivation" to learn about `overrideDerivation` and caveats
+     related to its use.
+  */
   makeOverridable = f: origArgs:
     let
       ff = f origArgs;
diff --git a/lib/debug.nix b/lib/debug.nix
index 5b3878554c5d8..925e0d405a79a 100644
--- a/lib/debug.nix
+++ b/lib/debug.nix
@@ -20,8 +20,32 @@ rec {
   traceXMLValMarked = str: x: trace (str + builtins.toXML x) x;
 
   # strict trace functions (traced structure is fully evaluated and printed)
+
+  /* `builtins.trace`, but the value is `builtins.deepSeq`ed first. */
   traceSeq = x: y: trace (builtins.deepSeq x x) y;
+
+  /* Like `traceSeq`, but only down to depth n.
+   * This is very useful because lots of `traceSeq` usages
+   * lead to an infinite recursion.
+   */
+  traceSeqN = depth: x: y: with lib;
+    let snip = v: if      isList  v then noQuotes "[…]" v
+                  else if isAttrs v then noQuotes "{…}" v
+                  else v;
+        noQuotes = str: v: { __pretty = const str; val = v; };
+        modify = n: fn: v: if (n == 0) then fn v
+                      else if isList  v then map (modify (n - 1) fn) v
+                      else if isAttrs v then mapAttrs
+                        (const (modify (n - 1) fn)) v
+                      else v;
+    in trace (generators.toPretty { allowPrettyValues = true; }
+               (modify depth snip x)) y;
+
+  /* `traceSeq`, but the same value is traced and returned */
   traceValSeq = v: traceVal (builtins.deepSeq v v);
+  /* `traceValSeq` but with fixed depth */
+  traceValSeqN = depth: v: traceSeqN depth v v;
+
 
   # this can help debug your code as well - designed to not produce thousands of lines
   traceShowVal = x: trace (showVal x) x;
diff --git a/lib/default.nix b/lib/default.nix
index e692637abf197..3893e349db369 100644
--- a/lib/default.nix
+++ b/lib/default.nix
@@ -5,8 +5,9 @@
  */
 let
 
-  # trivial, often used functions
+  # often used, or depending on very little
   trivial = import ./trivial.nix;
+  fixedPoints = import ./fixed-points.nix;
 
   # datatypes
   attrsets = import ./attrsets.nix;
@@ -42,7 +43,7 @@ let
   filesystem = import ./filesystem.nix;
 
 in
-  { inherit trivial
+  { inherit trivial fixedPoints
             attrsets lists strings stringsWithDeps
             customisation maintainers meta sources
             modules options types
@@ -55,6 +56,7 @@ in
   }
   # !!! don't include everything at top-level; perhaps only the most
   # commonly used functions.
-  // trivial // lists // strings // stringsWithDeps // attrsets // sources
+  // trivial // fixedPoints
+  // lists // strings // stringsWithDeps // attrsets // sources
   // options // types // meta // debug // misc // modules
   // customisation
diff --git a/lib/deprecated.nix b/lib/deprecated.nix
index 983e8d26892bf..d4e78c39250cf 100644
--- a/lib/deprecated.nix
+++ b/lib/deprecated.nix
@@ -423,4 +423,12 @@ rec {
       else if isInt x then "int"
       else "string";
 
+  /* deprecated:
+
+     For historical reasons, imap has an index starting at 1.
+
+     But for consistency with the rest of the library we want an index
+     starting at zero.
+  */
+  imap = imap1;
 }
diff --git a/lib/filesystem.nix b/lib/filesystem.nix
index 91b04d81c13be..3925beb213478 100644
--- a/lib/filesystem.nix
+++ b/lib/filesystem.nix
@@ -1,4 +1,22 @@
-{ # locateDominatingFile :  RegExp
+{ # haskellPathsInDir : Path -> Map String Path
+  # A map of all haskell packages defined in the given path,
+  # identified by having a cabal file with the same name as the
+  # directory itself.
+  haskellPathsInDir = root:
+    let # Files in the root
+        root-files = builtins.attrNames (builtins.readDir root);
+        # Files with their full paths
+        root-files-with-paths =
+          map (file:
+            { name = file; value = root + "/${file}"; }
+          ) root-files;
+        # Subdirectories of the root with a cabal file.
+        cabal-subdirs =
+          builtins.filter ({ name, value }:
+            builtins.pathExists (value + "/${name}.cabal")
+          ) root-files-with-paths;
+    in builtins.listToAttrs cabal-subdirs;
+  # locateDominatingFile :  RegExp
   #                      -> Path
   #                      -> Nullable { path : Path;
   #                                    matches : [ MatchResults ];
diff --git a/lib/fixed-points.nix b/lib/fixed-points.nix
new file mode 100644
index 0000000000000..a11b5a6f4bdc5
--- /dev/null
+++ b/lib/fixed-points.nix
@@ -0,0 +1,78 @@
+rec {
+  # Compute the fixed point of the given function `f`, which is usually an
+  # attribute set that expects its final, non-recursive representation as an
+  # argument:
+  #
+  #     f = self: { foo = "foo"; bar = "bar"; foobar = self.foo + self.bar; }
+  #
+  # Nix evaluates this recursion until all references to `self` have been
+  # resolved. At that point, the final result is returned and `f x = x` holds:
+  #
+  #     nix-repl> fix f
+  #     { bar = "bar"; foo = "foo"; foobar = "foobar"; }
+  #
+  #  Type: fix :: (a -> a) -> a
+  #
+  # See https://en.wikipedia.org/wiki/Fixed-point_combinator for further
+  # details.
+  fix = f: let x = f x; in x;
+
+  # A variant of `fix` that records the original recursive attribute set in the
+  # result. This is useful in combination with the `extends` function to
+  # implement deep overriding. See pkgs/development/haskell-modules/default.nix
+  # for a concrete example.
+  fix' = f: let x = f x // { __unfix__ = f; }; in x;
+
+  # Modify the contents of an explicitly recursive attribute set in a way that
+  # honors `self`-references. This is accomplished with a function
+  #
+  #     g = self: super: { foo = super.foo + " + "; }
+  #
+  # that has access to the unmodified input (`super`) as well as the final
+  # non-recursive representation of the attribute set (`self`). `extends`
+  # differs from the native `//` operator insofar as that it's applied *before*
+  # references to `self` are resolved:
+  #
+  #     nix-repl> fix (extends g f)
+  #     { bar = "bar"; foo = "foo + "; foobar = "foo + bar"; }
+  #
+  # The name of the function is inspired by object-oriented inheritance, i.e.
+  # think of it as an infix operator `g extends f` that mimics the syntax from
+  # Java. It may seem counter-intuitive to have the "base class" as the second
+  # argument, but it's nice this way if several uses of `extends` are cascaded.
+  extends = f: rattrs: self: let super = rattrs self; in super // f self super;
+
+  # Compose two extending functions of the type expected by 'extends'
+  # into one where changes made in the first are available in the
+  # 'super' of the second
+  composeExtensions =
+    f: g: self: super:
+      let fApplied = f self super;
+          super' = super // fApplied;
+      in fApplied // g self super';
+
+  # Create an overridable, recursive attribute set. For example:
+  #
+  #     nix-repl> obj = makeExtensible (self: { })
+  #
+  #     nix-repl> obj
+  #     { __unfix__ = «lambda»; extend = «lambda»; }
+  #
+  #     nix-repl> obj = obj.extend (self: super: { foo = "foo"; })
+  #
+  #     nix-repl> obj
+  #     { __unfix__ = «lambda»; extend = «lambda»; foo = "foo"; }
+  #
+  #     nix-repl> obj = obj.extend (self: super: { foo = super.foo + " + "; bar = "bar"; foobar = self.foo + self.bar; })
+  #
+  #     nix-repl> obj
+  #     { __unfix__ = «lambda»; bar = "bar"; extend = «lambda»; foo = "foo + "; foobar = "foo + bar"; }
+  makeExtensible = makeExtensibleWithCustomName "extend";
+
+  # Same as `makeExtensible` but the name of the extending attribute is
+  # customized.
+  makeExtensibleWithCustomName = extenderName: rattrs:
+    fix' rattrs // {
+      ${extenderName} = f: makeExtensibleWithCustomName extenderName (extends f rattrs);
+   };
+}
diff --git a/lib/generators.nix b/lib/generators.nix
index 4d3c920b0ae3b..4419c3c88917e 100644
--- a/lib/generators.nix
+++ b/lib/generators.nix
@@ -90,4 +90,41 @@ rec {
     * parsers as well.
     */
   toYAML = {}@args: toJSON args;
+
+  /* Pretty print a value, akin to `builtins.trace`.
+    * Should probably be a builtin as well.
+    */
+  toPretty = {
+    /* If this option is true, attrsets like { __pretty = fn; val = …; }
+       will use fn to convert val to a pretty printed representation.
+       (This means fn is type Val -> String.) */
+    allowPrettyValues ? false
+  }@args: v: with builtins;
+    if      isInt      v then toString v
+    else if isBool     v then (if v == true then "true" else "false")
+    else if isString   v then "\"" + v + "\""
+    else if null ==    v then "null"
+    else if isFunction v then
+      let fna = functionArgs v;
+          showFnas = concatStringsSep "," (libAttr.mapAttrsToList
+                       (name: hasDefVal: if hasDefVal then "(${name})" else name)
+                       fna);
+      in if fna == {}    then "<λ>"
+                         else "<λ:{${showFnas}}>"
+    else if isList     v then "[ "
+        + libStr.concatMapStringsSep " " (toPretty args) v
+      + " ]"
+    else if isAttrs    v then
+      # apply pretty values if allowed
+      if attrNames v == [ "__pretty" "val" ] && allowPrettyValues
+         then v.__pretty v.val
+      # TODO: there is probably a better representation?
+      else if v ? type && v.type == "derivation" then "<δ>"
+      else "{ "
+          + libStr.concatStringsSep " " (libAttr.mapAttrsToList
+              (name: value:
+                "${toPretty args name} = ${toPretty args value};") v)
+        + " }"
+    else "toPretty: should never happen (v = ${v})";
+
 }
diff --git a/lib/licenses.nix b/lib/licenses.nix
index 0919699b41e37..8dd1c210b7fa9 100644
--- a/lib/licenses.nix
+++ b/lib/licenses.nix
@@ -45,6 +45,11 @@ lib.mapAttrs (n: v: v // { shortName = n; }) rec {
     fullName = "Apple Public Source License 2.0";
   };
 
+  arphicpl = {
+    fullName = "Arphic Public License";
+    url = https://www.freedesktop.org/wiki/Arphic_Public_License/;
+  };
+
   artistic1 = spdx {
     spdxId = "Artistic-1.0";
     fullName = "Artistic License 1.0";
diff --git a/lib/lists.nix b/lib/lists.nix
index fd746f4f97b1f..6a8fd8a184082 100644
--- a/lib/lists.nix
+++ b/lib/lists.nix
@@ -77,15 +77,21 @@ rec {
   */
   foldl' = builtins.foldl' or foldl;
 
-  /* Map with index
+  /* Map with index starting from 0
 
-     FIXME(zimbatm): why does this start to count at 1?
+     Example:
+       imap0 (i: v: "${v}-${toString i}") ["a" "b"]
+       => [ "a-0" "b-1" ]
+  */
+  imap0 = f: list: genList (n: f n (elemAt list n)) (length list);
+
+  /* Map with index starting from 1
 
      Example:
-       imap (i: v: "${v}-${toString i}") ["a" "b"]
+       imap1 (i: v: "${v}-${toString i}") ["a" "b"]
        => [ "a-1" "b-2" ]
   */
-  imap = f: list: genList (n: f (n + 1) (elemAt list n)) (length list);
+  imap1 = f: list: genList (n: f (n + 1) (elemAt list n)) (length list);
 
   /* Map and concatenate the result.
 
@@ -471,4 +477,12 @@ rec {
   */
   subtractLists = e: filter (x: !(elem x e));
 
+  /* Test if two lists have no common element.
+     It should be slightly more efficient than (intersectLists a b == [])
+  */
+  mutuallyExclusive = a: b:
+    (builtins.length a) == 0 ||
+    (!(builtins.elem (builtins.head a) b) &&
+     mutuallyExclusive (builtins.tail a) b);
+
 }
diff --git a/lib/maintainers.nix b/lib/maintainers.nix
index f3204ab3b345d..31760c94eb0fc 100644
--- a/lib/maintainers.nix
+++ b/lib/maintainers.nix
@@ -16,6 +16,7 @@
   acowley = "Anthony Cowley <acowley@gmail.com>";
   adelbertc = "Adelbert Chang <adelbertc@gmail.com>";
   adev = "Adrien Devresse <adev@adev.name>";
+  adisbladis = "Adam Hose <adis@blad.is>";
   Adjective-Object = "Maxwell Huang-Hobbs <mhuan13@gmail.com>";
   adnelson = "Allen Nelson <ithinkican@gmail.com>";
   adolfogc = "Adolfo E. García Castro <adolfo.garcia.cr@gmail.com>";
@@ -42,7 +43,9 @@
   andrewrk = "Andrew Kelley <superjoe30@gmail.com>";
   andsild = "Anders Sildnes <andsild@gmail.com>";
   aneeshusa = "Aneesh Agrawal <aneeshusa@gmail.com>";
+  ankhers = "Justin Wood <justin.k.wood@gmail.com>";
   antono = "Antono Vasiljev <self@antono.info>";
+  apeschar = "Albert Peschar <albert@peschar.net>";
   apeyroux = "Alexandre Peyroux <alex@px.io>";
   ardumont = "Antoine R. Dumont <eniotna.t@gmail.com>";
   aristid = "Aristid Breitkreuz <aristidb@gmail.com>";
@@ -60,6 +63,7 @@
   bachp = "Pascal Bach <pascal.bach@nextrem.ch>";
   badi = "Badi' Abdul-Wahid <abdulwahidc@gmail.com>";
   balajisivaraman = "Balaji Sivaraman<sivaraman.balaji@gmail.com>";
+  barrucadu = "Michael Walker <mike@barrucadu.co.uk>";
   basvandijk = "Bas van Dijk <v.dijk.bas@gmail.com>";
   Baughn = "Svein Ove Aas <sveina@gmail.com>";
   bcarrell = "Brandon Carrell <brandoncarrell@gmail.com>";
@@ -70,6 +74,8 @@
   benwbooth = "Ben Booth <benwbooth@gmail.com>";
   berdario = "Dario Bertini <berdario@gmail.com>";
   bergey = "Daniel Bergey <bergey@teallabs.org>";
+  bhipple = "Benjamin Hipple <bhipple@protonmail.com>";
+  binarin = "Alexey Lebedeff <binarin@binarin.ru>";
   bjg = "Brian Gough <bjg@gnu.org>";
   bjornfor = "Bjørn Forsman <bjorn.forsman@gmail.com>";
   bluescreen303 = "Mathijs Kwik <mathijs@bluescreen303.nl>";
@@ -97,6 +103,7 @@
   chris-martin = "Chris Martin <ch.martin@gmail.com>";
   chrisjefferson = "Christopher Jefferson <chris@bubblescope.net>";
   christopherpoole = "Christopher Mark Poole <mail@christopherpoole.net>";
+  ciil = "Simon Lackerbauer <simon@lackerbauer.com>";
   ckampka = "Christian Kampka <christian@kampka.net>";
   cko = "Christine Koppelt <christine.koppelt@gmail.com>";
   cleverca22 = "Michael Bishop <cleverca22@gmail.com>";
@@ -130,6 +137,7 @@
   deepfire = "Kosyrev Serge <_deepfire@feelingofgreen.ru>";
   demin-dmitriy = "Dmitriy Demin <demindf@gmail.com>";
   DerGuteMoritz = "Moritz Heidkamp <moritz@twoticketsplease.de>";
+  dermetfan = "Robin Stumm <serverkorken@gmail.com>";
   DerTim1 = "Tim Digel <tim.digel@active-group.de>";
   desiderius = "Didier J. Devroye <didier@devroye.name>";
   devhell = "devhell <\"^\"@regexmail.net>";
@@ -137,20 +145,24 @@
   dfoxfranke = "Daniel Fox Franke <dfoxfranke@gmail.com>";
   dgonyeo = "Derek Gonyeo <derek@gonyeo.com>";
   dipinhora = "Dipin Hora <dipinhora+github@gmail.com>";
+  disassembler = "Samuel Leathers <disasm@gmail.com>";
   dmalikov = "Dmitry Malikov <malikov.d.y@gmail.com>";
   DmitryTsygankov = "Dmitry Tsygankov <dmitry.tsygankov@gmail.com>";
   dmjio = "David Johnson <djohnson.m@gmail.com>";
   dochang = "Desmond O. Chang <dochang@gmail.com>";
   domenkozar = "Domen Kozar <domen@dev.si>";
+  dotlambda = "Robert Schütz <rschuetz17@gmail.com>";
   doublec = "Chris Double <chris.double@double.co.nz>";
   dpaetzel = "David Pätzel <david.a.paetzel@gmail.com>";
   drets = "Dmytro Rets <dmitryrets@gmail.com>";
   drewkett = "Andrew Burkett <burkett.andrew@gmail.com>";
   dsferruzza = "David Sferruzza <david.sferruzza@gmail.com>";
   dtzWill = "Will Dietz <nix@wdtz.org>";
+  dywedir = "Vladyslav M. <dywedir@protonmail.ch>";
   e-user = "Alexander Kahl <nixos@sodosopa.io>";
   ebzzry = "Rommel Martinez <ebzzry@gmail.com>";
   edanaher = "Evan Danaher <nixos@edanaher.net>";
+  edef = "edef <edef@edef.eu>";
   ederoyd46 = "Matthew Brown <matt@ederoyd.co.uk>";
   eduarrrd = "Eduard Bachmakov <e.bachmakov@gmail.com>";
   edwtjo = "Edward Tjörnhammar <ed@cflags.cc>";
@@ -158,8 +170,10 @@
   ehegnes = "Eric Hegnes <eric.hegnes@gmail.com>";
   ehmry = "Emery Hemingway <emery@vfemail.net>";
   eikek = "Eike Kettner <eike.kettner@posteo.de>";
+  ekleog = "Leo Gaspard <leo@gaspard.io>";
   elasticdog = "Aaron Bull Schaefer <aaron@elasticdog.com>";
   eleanor = "Dejan Lukan <dejan@proteansec.com>";
+  elijahcaine = "Elijah Caine <elijahcainemv@gmail.com>";
   elitak = "Eric Litak <elitak@gmail.com>";
   ellis = "Ellis Whitehead <nixos@ellisw.net>";
   eperuffo = "Emanuele Peruffo <info@emanueleperuffo.com>";
@@ -173,7 +187,9 @@
   exlevan = "Alexey Levan <exlevan@gmail.com>";
   expipiplus1 = "Joe Hermaszewski <nix@monoid.al>";
   fadenb = "Tristan Helmich <tristan.helmich+nixos@gmail.com>";
+  fare = "Francois-Rene Rideau <fahree@gmail.com>";
   falsifian = "James Cook <james.cook@utoronto.ca>";
+  florianjacob = "Florian Jacob <projects+nixos@florianjacob.de>";
   flosse = "Markus Kohlhase <mail@markus-kohlhase.de>";
   fluffynukeit = "Daniel Austin <dan@fluffynukeit.com>";
   fmthoma = "Franz Thoma <f.m.thoma@googlemail.com>";
@@ -194,6 +210,7 @@
   garrison = "Jim Garrison <jim@garrison.cc>";
   gavin = "Gavin Rogers <gavin@praxeology.co.uk>";
   gebner = "Gabriel Ebner <gebner@gebner.org>";
+  georgewhewell = "George Whewell <georgerw@gmail.com>";
   gilligan = "Tobias Pflug <tobias.pflug@gmail.com>";
   giogadi = "Luis G. Torres <lgtorres42@gmail.com>";
   gleber = "Gleb Peregud <gleber.p@gmail.com>";
@@ -204,6 +221,7 @@
   goodrone = "Andrew Trachenko <goodrone@gmail.com>";
   gpyh = "Yacine Hmito <yacine.hmito@gmail.com>";
   grahamc = "Graham Christensen <graham@grahamc.com>";
+  grburst = "Julius Elias <grburst@openmailbox.org>";
   gridaphobe = "Eric Seidel <eric@seidel.io>";
   guibert = "David Guibert <david.guibert@gmail.com>";
   guillaumekoenig = "Guillaume Koenig <guillaume.edward.koenig@gmail.com>";
@@ -212,11 +230,14 @@
   havvy = "Ryan Scheel <ryan.havvy@gmail.com>";
   hbunke = "Hendrik Bunke <bunke.hendrik@gmail.com>";
   hce = "Hans-Christian Esperer <hc@hcesperer.org>";
+  hectorj = "Hector Jusforgues <hector.jusforgues+nixos@gmail.com>";
   heel = "Sergii Paryzhskyi <parizhskiy@gmail.com>";
   henrytill = "Henry Till <henrytill@gmail.com>";
+  hhm = "hhm <heehooman+nixpkgs@gmail.com>";
   hinton = "Tom Hinton <t@larkery.com>";
   hodapp = "Chris Hodapp <hodapp87@gmail.com>";
   hrdinka = "Christoph Hrdinka <c.nix@hrdinka.at>";
+  htr = "Hugo Tavares Reis <hugo@linux.com>";
   iand675 = "Ian Duncan <ian@iankduncan.com>";
   ianwookim = "Ian-Woo Kim <ianwookim@gmail.com>";
   igsha = "Igor Sharonov <igor.sharonov@gmail.com>";
@@ -225,32 +246,38 @@
   ivan-tkatchev = "Ivan Tkatchev <tkatchev@gmail.com>";
   j-keck = "Jürgen Keck <jhyphenkeck@gmail.com>";
   jagajaga = "Arseniy Seroka <ars.seroka@gmail.com>";
+  jammerful = "jammerful <jammerful@gmail.com>";
   jansol = "Jan Solanti <jan.solanti@paivola.fi>";
   javaguirre = "Javier Aguirre <contacto@javaguirre.net>";
-  jb55 = "William Casarin <bill@casarin.me>";
+  jb55 = "William Casarin <jb55@jb55.com>";
   jbedo = "Justin Bedő <cu@cua0.org>";
   jcumming = "Jack Cummings <jack@mudshark.org>";
   jdagilliland = "Jason Gilliland <jdagilliland@gmail.com>";
   jefdaj = "Jeffrey David Johnson <jefdaj@gmail.com>";
+  jensbin = "Jens Binkert <jensbin@protonmail.com>";
   jerith666 = "Matt McHenry <github@matt.mchenryfamily.org>";
   jfb = "James Felix Black <james@yamtime.com>";
+  jfrankenau = "Johannes Frankenau <johannes@frankenau.net>";
   jgeerds = "Jascha Geerds <jascha@jgeerds.name>";
   jgertm = "Tim Jaeger <jger.tm@gmail.com>";
   jgillich = "Jakob Gillich <jakob@gillich.me>";
   jhhuh = "Ji-Haeng Huh <jhhuh.note@gmail.com>";
   jirkamarsik = "Jirka Marsik <jiri.marsik89@gmail.com>";
   jlesquembre = "José Luis Lafuente <jl@lafuente.me>";
+  jluttine = "Jaakko Luttinen <jaakko.luttinen@iki.fi>";
   joachifm = "Joachim Fasting <joachifm@fastmail.fm>";
   joamaki = "Jussi Maki <joamaki@gmail.com>";
   joelmo = "Joel Moberg <joel.moberg@gmail.com>";
   joelteon = "Joel Taylor <me@joelt.io>";
   johbo = "Johannes Bornhold <johannes@bornhold.name>";
+  johnramsden = "John Ramsden <johnramsden@riseup.net>";
   joko = "Ioannis Koutras <ioannis.koutras@gmail.com>";
   jonafato = "Jon Banafato <jon@jonafato.com>";
   jpbernardy = "Jean-Philippe Bernardy <jeanphilippe.bernardy@gmail.com>";
   jpierre03 = "Jean-Pierre PRUNARET <nix@prunetwork.fr>";
   jpotier = "Martin Potier <jpo.contributes.to.nixos@marvid.fr>";
   jraygauthier = "Raymond Gauthier <jraygauthier@gmail.com>";
+  jtojnar = "Jan Tojnar <jtojnar@gmail.com>";
   juliendehos = "Julien Dehos <dehos@lisic.univ-littoral.fr>";
   jwiegley = "John Wiegley <johnw@newartisans.com>";
   jwilberding = "Jordan Wilberding <jwilberding@afiniate.com>";
@@ -258,12 +285,14 @@
   kaiha = "Kai Harries <kai.harries@gmail.com>";
   kamilchm = "Kamil Chmielewski <kamil.chm@gmail.com>";
   kampfschlaefer = "Arnold Krille <arnold@arnoldarts.de>";
+  kentjames = "James Kent <jameschristopherkent@gmail.com";
   kevincox = "Kevin Cox <kevincox@kevincox.ca>";
   khumba = "Bryan Gardiner <bog@khumba.net>";
   KibaFox = "Kiba Fox <kiba.fox@foxypossibilities.com>";
   kierdavis = "Kier Davis <kierdavis@gmail.com>";
   kkallio = "Karn Kallio <tierpluspluslists@gmail.com>";
   knedlsepp = "Josef Kemetmüller <josef.kemetmueller@gmail.com>";
+  konimex = "Muhammad Herdiansyah <herdiansyah@openmailbox.org>";
   koral = "Koral <koral@mailoo.org>";
   kovirobi = "Kovacsics Robert <kovirobi@gmail.com>";
   kragniz = "Louis Taylor <louis@kragniz.eu>";
@@ -291,9 +320,12 @@
   lsix = "Lancelot SIX <lsix@lancelotsix.com>";
   lucas8 = "Luc Chabassier <luc.linux@mailoo.org>";
   ludo = "Ludovic Courtès <ludo@gnu.org>";
+  lufia = "Kyohei Kadota <lufia@lufia.org>";
   luispedro = "Luis Pedro Coelho <luis@luispedro.org>";
   lukego = "Luke Gorrie <luke@snabb.co>";
   lw = "Sergey Sofeychuk <lw@fmap.me>";
+  lyt = "Tim Liou <wheatdoge@gmail.com>";
+  m3tti = "Mathaeus Sander <mathaeus.peter.sander@gmail.com>";
   ma27 = "Maximilian Bosch <maximilian@mbosch.me>";
   madjar = "Georges Dubus <georges.dubus@compiletoi.net>";
   magnetophon = "Bart Brouns <bart@magnetophon.nl>";
@@ -326,6 +358,7 @@
   michaelpj = "Michael Peyton Jones <michaelpj@gmail.com>";
   michalrus = "Michal Rus <m@michalrus.com>";
   michelk = "Michel Kuhlmann <michel@kuhlmanns.info>";
+  midchildan = "midchildan <midchildan+nix@gmail.com>";
   mikefaille = "Michaël Faille <michael@faille.io>";
   miltador = "Vasiliy Solovey <miltador@yandex.ua>";
   mimadrid = "Miguel Madrid <mimadrid@ucm.es>";
@@ -348,6 +381,7 @@
   msackman = "Matthew Sackman <matthew@wellquite.org>";
   mschristiansen = "Mikkel Christiansen <mikkel@rheosystems.com>";
   msteen = "Matthijs Steen <emailmatthijs@gmail.com>";
+  mt-caret = "Masayuki Takeda <mtakeda.enigsol@gmail.com>";
   mtreskin = "Max Treskin <zerthurd@gmail.com>";
   mudri = "James Wood <lamudri@gmail.com>";
   muflax = "Stefan Dorn <mail@muflax.com>";
@@ -356,11 +390,12 @@
   nand0p = "Fernando Jose Pando <nando@hex7.com>";
   Nate-Devv = "Nathan Moore <natedevv@gmail.com>";
   nathan-gs = "Nathan Bijnens <nathan@nathan.gs>";
-  nckx = "Tobias Geerinckx-Rice <tobias.geerinckx.rice@gmail.com>";
+  nckx = "Tobias Geerinckx-Rice <github@tobias.gr>";
   ndowens = "Nathan Owens <ndowens04@gmail.com>";
   neeasade = "Nathan Isom <nathanisom27@gmail.com>";
   nequissimus = "Tim Steinbach <tim@nequissimus.com>";
   nfjinjing = "Jinjing Wang <nfjinjing@gmail.com>";
+  nh2 = "Niklas Hambüchen <mail@nh2.me>";
   nhooyr = "Anmol Sethi <anmol@aubble.com>";
   nickhu = "Nick Hu <me@nickhu.co.uk>";
   nicknovitski = "Nick Novitski <nixpkgs@nicknovitski.com>";
@@ -381,6 +416,7 @@
   okasu = "Okasu <oka.sux@gmail.com>";
   olcai = "Erik Timan <dev@timan.info>";
   olejorgenb = "Ole Jørgen Brønner <olejorgenb@yahoo.no>";
+  olynch = "Owen Lynch <owen@olynch.me>";
   orbekk = "KJ Ørbekk <kjetil.orbekk@gmail.com>";
   orbitz = "Malcolm Matalka <mmatalka@gmail.com>";
   orivej = "Orivej Desh <orivej@gmx.fr>";
@@ -442,24 +478,27 @@
   relrod = "Ricky Elrod <ricky@elrod.me>";
   renzo = "Renzo Carbonara <renzocarbonara@gmail.com>";
   retrry = "Tadas Barzdžius <retrry@gmail.com>";
+  rht = "rht <rhtbot@protonmail.com>";
   rick68 = "Wei-Ming Yang <rick68@gmail.com>";
   rickynils = "Rickard Nilsson <rickynils@gmail.com>";
+  ris = "Robert Scott <code@humanleg.org.uk>";
   rlupton20 = "Richard Lupton <richard.lupton@gmail.com>";
   rnhmjoj = "Michele Guerini Rocco <micheleguerinirocco@me.com>";
   rob = "Rob Vermaas <rob.vermaas@gmail.com>";
   robberer = "Longrin Wischnewski <robberer@freakmail.de>";
   robbinch = "Robbin C. <robbinch33@gmail.com>";
+  roberth = "Robert Hensing <nixpkgs@roberthensing.nl>";
   robgssp = "Rob Glossop <robgssp@gmail.com>";
   roblabla = "Robin Lambertz <robinlambertz+dev@gmail.com>";
   roconnor = "Russell O'Connor <roconnor@theorem.ca>";
   romildo = "José Romildo Malaquias <malaquias@gmail.com>";
   rongcuid = "Rongcui Dong <rongcuid@outlook.com>";
-  ronny = "Ronny Pfannschmidt <nixos@ronnypfannschmidt.de>";
   rszibele = "Richard Szibele <richard@szibele.com>";
   rtreffer = "Rene Treffer <treffer+nixos@measite.de>";
   rushmorem = "Rushmore Mushambi <rushmore@webenchanter.com>";
   rvl = "Rodney Lorrimar <dev+nix@rodney.id.au>";
   rvlander = "Gaëtan André <rvlander@gaetanandre.eu>";
+  rvolosatovs = "Roman Volosatovs <rvolosatovs@riseup.net";
   ryanartecona = "Ryan Artecona <ryanartecona@gmail.com>";
   ryansydnor = "Ryan Sydnor <ryan.t.sydnor@gmail.com>";
   ryantm = "Ryan Mulligan <ryan@ryantm.com>";
@@ -469,6 +508,7 @@
   s1lvester = "Markus Silvester <s1lvester@bockhacker.me>";
   samuelrivas = "Samuel Rivas <samuelrivas@gmail.com>";
   sander = "Sander van der Burg <s.vanderburg@tudelft.nl>";
+  sargon = "Daniel Ehlers <danielehlers@mindeye.net>";
   schmitthenner = "Fabian Schmitthenner <development@schmitthenner.eu>";
   schneefux = "schneefux <schneefux+nixos_pkg@schneefux.xyz>";
   schristo = "Scott Christopher <schristopher@konputa.com>";
@@ -491,6 +531,7 @@
   skrzyp = "Jakub Skrzypnik <jot.skrzyp@gmail.com>";
   sleexyz = "Sean Lee <freshdried@gmail.com>";
   smironov = "Sergey Mironov <grrwlf@gmail.com>";
+  snyh = "Xia Bin <snyh@snyh.org>";
   solson = "Scott Olson <scott@solson.me>";
   spacefrogg = "Michael Raitza <spacefrogg-nixos@meterriblecrew.net>";
   spencerjanssen = "Spencer Janssen <spencerjanssen@gmail.com>";
@@ -505,6 +546,7 @@
   steveej = "Stefan Junker <mail@stefanjunker.de>";
   SuprDewd = "Bjarki Ágúst Guðmundsson <suprdewd@gmail.com>";
   swarren83 = "Shawn Warren <shawn.w.warren@gmail.com>";
+  swflint = "Samuel W. Flint <swflint@flintfam.org>";
   swistak35 = "Rafał Łasocha <me@swistak35.com>";
   szczyp = "Szczyp <qb@szczyp.com>";
   sztupi = "Attila Sztupak <attila.sztupak@gmail.com>";
@@ -513,6 +555,7 @@
   takikawa = "Asumu Takikawa <asumu@igalia.com>";
   taktoa = "Remy Goldschmidt <taktoa@gmail.com>";
   taku0 = "Takuo Yonezawa <mxxouy6x3m_github@tatapa.org>";
+  tari = "Peter Marheine <peter@taricorp.net>";
   tavyc = "Octavian Cerna <octavian.cerna@gmail.com>";
   teh = "Tom Hunger <tehunger@gmail.com>";
   telotortium = "Robert Irelan <rirelan@gmail.com>";
@@ -526,18 +569,22 @@
   tohl = "Tomas Hlavaty <tom@logand.com>";
   tokudan = "Daniel Frank <git@danielfrank.net>";
   tomberek = "Thomas Bereknyei <tomberek@gmail.com>";
+  tomsmeets = "Tom Smeets <tom@tsmeets.nl>";
   travisbhartwell = "Travis B. Hartwell <nafai@travishartwell.net>";
+  trevorj = "Trevor Joynson <nix@trevor.joynson.io>";
   trino = "Hubert Mühlhans <muehlhans.hubert@ekodia.de>";
   tstrobel = "Thomas Strobel <4ZKTUB6TEP74PYJOPWIR013S2AV29YUBW5F9ZH2F4D5UMJUJ6S@hash.domains>";
   ttuegel = "Thomas Tuegel <ttuegel@mailbox.org>";
   tv = "Tomislav Viljetić <tv@shackspace.de>";
   tvestelind = "Tomas Vestelind <tomas.vestelind@fripost.org>";
   tvorog = "Marsel Zaripov <marszaripov@gmail.com>";
+  tweber = "Thorsten Weber <tw+nixpkgs@360vier.de>";
   twey = "James ‘Twey’ Kay <twey@twey.co.uk>";
   uralbash = "Svintsov Dmitry <root@uralbash.ru>";
   utdemir = "Utku Demir <me@utdemir.com>";
   #urkud = "Yury G. Kudryashov <urkud+nix@ya.ru>"; inactive since 2012
   uwap = "uwap <me@uwap.name>";
+  vaibhavsagar = "Vaibhav Sagar <vaibhavsagar@gmail.com>";
   vandenoever = "Jos van den Oever <jos@vandenoever.info>";
   vanzef = "Ivan Solyankin <vanzef@gmail.com>";
   vbgl = "Vincent Laporte <Vincent.Laporte@gmail.com>";
@@ -551,10 +598,13 @@
   vklquevs = "vklquevs <vklquevs@gmail.com>";
   vlstill = "Vladimír Štill <xstill@fi.muni.cz>";
   vmandela = "Venkateswara Rao Mandela <venkat.mandela@gmail.com>";
+  vmchale = "Vanessa McHale <tmchale@wisc.edu>";
+  valeriangalliat = "Valérian Galliat <val@codejam.info>";
   volhovm = "Mikhail Volkhov <volhovm.cs@gmail.com>";
   volth = "Jaroslavas Pocepko <jaroslavas@volth.com>";
   vozz = "Oliver Hunt <oliver.huntuk@gmail.com>";
   vrthra = "Rahul Gopinath <rahul@gopinath.org>";
+  vyp = "vyp <elisp.vim@gmail.com>";
   wedens = "wedens <kirill.wedens@gmail.com>";
   willtim = "Tim Philip Williams <tim.williams.public@gmail.com>";
   winden = "Antonio Vargas Gonzalez <windenntw@gmail.com>";
@@ -571,13 +621,17 @@
   yarr = "Dmitry V. <savraz@gmail.com>";
   yochai = "Yochai <yochai@titat.info>";
   yorickvp = "Yorick van Pelt <yorickvanpelt@gmail.com>";
+  yuriaisaka = "Yuri Aisaka <yuri.aisaka+nix@gmail.com>";
   yurrriq = "Eric Bailey <eric@ericb.me>";
   z77z = "Marco Maggesi <maggesi@math.unifi.it>";
   zagy = "Christian Zagrodnick <cz@flyingcircus.io>";
+  zalakain = "Unai Zalakain <contact@unaizalakain.info>";
+  zarelit = "David Costa <david@zarel.net>";
   zauberpony = "Elmar Athmer <elmar@athmer.org>";
   zef = "Zef Hemel <zef@zef.me>";
   zimbatm = "zimbatm <zimbatm@zimbatm.com>";
   zohl = "Al Zohali <zohl@fmap.me>";
   zoomulator = "Kim Simmons <zoomulator@gmail.com>";
   zraexy = "David Mell <zraexy@gmail.com>";
+  zx2c4 = "Jason A. Donenfeld <Jason@zx2c4.com>";
 }
diff --git a/lib/meta.nix b/lib/meta.nix
index 44e3cc011f187..8f77bbe014844 100644
--- a/lib/meta.nix
+++ b/lib/meta.nix
@@ -17,6 +17,11 @@ rec {
     drv // { meta = (drv.meta or {}) // newAttrs; };
 
 
+  /* Disable Hydra builds of given derivation.
+  */
+  dontDistribute = drv: addMetaAttrs { hydraPlatforms = []; } drv;
+
+
   /* Change the symbolic name of a package for presentation purposes
      (i.e., so that nix-env users can tell them apart).
   */
@@ -45,7 +50,7 @@ rec {
   /* 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;
+  lowPrio = drv: addMetaAttrs { priority = 10; } drv;
 
 
   /* Apply lowPrio to an attrset with derivations
@@ -56,7 +61,7 @@ rec {
   /* 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;
+  hiPrio = drv: addMetaAttrs { priority = -10; } drv;
 
 
   /* Apply hiPrio to an attrset with derivations
diff --git a/lib/modules.nix b/lib/modules.nix
index 91e2eae0595e3..3da689a6bdb00 100644
--- a/lib/modules.nix
+++ b/lib/modules.nix
@@ -98,7 +98,7 @@ rec {
   /* Close a set of modules under the ‘imports’ relation. */
   closeModules = modules: args:
     let
-      toClosureList = file: parentKey: imap (n: x:
+      toClosureList = file: parentKey: imap1 (n: x:
         if isAttrs x || isFunction x then
           let key = "${parentKey}:anon-${toString n}"; in
           unifyModuleSyntax file key (unpackSubmodule (applyIfFunction key) x args)
diff --git a/lib/strings.nix b/lib/strings.nix
index d48624257cf01..a03694d1b1d72 100644
--- a/lib/strings.nix
+++ b/lib/strings.nix
@@ -33,7 +33,7 @@ rec {
        concatImapStrings (pos: x: "${toString pos}-${x}") ["foo" "bar"]
        => "1-foo2-bar"
   */
-  concatImapStrings = f: list: concatStrings (lib.imap f list);
+  concatImapStrings = f: list: concatStrings (lib.imap1 f list);
 
   /* Place an element between each element of a list
 
@@ -70,7 +70,7 @@ rec {
        concatImapStringsSep "-" (pos: x: toString (x / pos)) [ 6 6 6 ]
        => "6-3-2"
   */
-  concatImapStringsSep = sep: f: list: concatStringsSep sep (lib.imap f list);
+  concatImapStringsSep = sep: f: list: concatStringsSep sep (lib.imap1 f list);
 
   /* Construct a Unix-style search path consisting of each `subDir"
      directory of the given list of packages.
@@ -438,8 +438,13 @@ rec {
        => true
        isStorePath pkgs.python
        => true
+       isStorePath [] || isStorePath 42 || isStorePath {} || …
+       => false
   */
-  isStorePath = x: builtins.substring 0 1 (toString x) == "/" && dirOf (builtins.toPath x) == builtins.storeDir;
+  isStorePath = x:
+       builtins.isString x
+    && builtins.substring 0 1 (toString x) == "/"
+    && dirOf (builtins.toPath x) == builtins.storeDir;
 
   /* Convert string to int
      Obviously, it is a bit hacky to use fromJSON that way.
diff --git a/lib/systems/default.nix b/lib/systems/default.nix
index d956969a18f92..c22c99561969d 100644
--- a/lib/systems/default.nix
+++ b/lib/systems/default.nix
@@ -1,7 +1,11 @@
+let inherit (import ../attrsets.nix) mapAttrs; in
+
 rec {
   doubles = import ./doubles.nix;
   parse = import ./parse.nix;
+  inspect = import ./inspect.nix;
   platforms = import ./platforms.nix;
+  examples = import ./examples.nix;
 
   # Elaborate a `localSystem` or `crossSystem` so that it contains everything
   # necessary.
@@ -18,6 +22,13 @@ rec {
       config = parse.tripleFromSystem final.parsed;
       # Just a guess, based on `system`
       platform = platforms.selectBySystem final.system;
-    } // args;
+      libc =
+        /**/ if final.isDarwin then "libSystem"
+        else if final.isMinGW  then "msvcrt"
+        else if final.isLinux  then "glibc"
+        # TODO(@Ericson2314) think more about other operating systems
+        else                        "native/impure";
+    } // mapAttrs (n: v: v final.parsed) inspect.predicates
+      // args;
   in final;
 }
diff --git a/lib/systems/doubles.nix b/lib/systems/doubles.nix
index 9b17a51531a33..0168eb42f2f73 100644
--- a/lib/systems/doubles.nix
+++ b/lib/systems/doubles.nix
@@ -1,8 +1,9 @@
-let lists = import ../lists.nix; in
-let parse = import ./parse.nix; in
-let inherit (import ../attrsets.nix) matchAttrs; in
-
 let
+  lists = import ../lists.nix;
+  parse = import ./parse.nix;
+  inherit (import ./inspect.nix) predicates;
+  inherit (import ../attrsets.nix) matchAttrs;
+
   all = [
     "aarch64-linux"
     "armv5tel-linux" "armv6l-linux" "armv7l-linux"
@@ -25,20 +26,21 @@ in rec {
   allBut = platforms: lists.filter (x: !(builtins.elem x platforms)) all;
   none = [];
 
-  arm = filterDoubles (matchAttrs { cpu = { family = "arm"; bits = 32; }; });
-  i686 = filterDoubles parse.isi686;
-  mips = filterDoubles (matchAttrs { cpu = { family = "mips"; }; });
-  x86_64 = filterDoubles parse.isx86_64;
-
-  cygwin = filterDoubles parse.isCygwin;
-  darwin = filterDoubles parse.isDarwin;
-  freebsd = filterDoubles (matchAttrs { kernel = parse.kernels.freebsd; });
-  gnu = filterDoubles (matchAttrs { kernel = parse.kernels.linux; abi = parse.abis.gnu; }); # Should be better
-  illumos = filterDoubles (matchAttrs { kernel = parse.kernels.solaris; });
-  linux = filterDoubles parse.isLinux;
-  netbsd = filterDoubles (matchAttrs { kernel = parse.kernels.netbsd; });
-  openbsd = filterDoubles (matchAttrs { kernel = parse.kernels.openbsd; });
-  unix = filterDoubles parse.isUnix;
+  arm     = filterDoubles predicates.isArm32;
+  i686    = filterDoubles predicates.isi686;
+  mips    = filterDoubles predicates.isMips;
+  x86_64  = filterDoubles predicates.isx86_64;
+
+  cygwin  = filterDoubles predicates.isCygwin;
+  darwin  = filterDoubles predicates.isDarwin;
+  freebsd = filterDoubles predicates.isFreeBSD;
+  # Should be better, but MinGW is unclear, and HURD is bit-rotted.
+  gnu     = filterDoubles (matchAttrs { kernel = parse.kernels.linux;  abi = parse.abis.gnu; });
+  illumos = filterDoubles predicates.isSunOS;
+  linux   = filterDoubles predicates.isLinux;
+  netbsd  = filterDoubles predicates.isNetBSD;
+  openbsd = filterDoubles predicates.isOpenBSD;
+  unix    = filterDoubles predicates.isUnix;
 
   mesaPlatforms = ["i686-linux" "x86_64-linux" "x86_64-darwin" "armv5tel-linux" "armv6l-linux" "armv7l-linux" "aarch64-linux"];
 }
diff --git a/lib/systems/examples.nix b/lib/systems/examples.nix
new file mode 100644
index 0000000000000..e394f43831c9f
--- /dev/null
+++ b/lib/systems/examples.nix
@@ -0,0 +1,130 @@
+# These can be passed to nixpkgs as either the `localSystem` or
+# `crossSystem`. They are put here for user convenience, but also used by cross
+# tests and linux cross stdenv building, so handle with care!
+
+let platforms = import ./platforms.nix; in
+
+rec {
+  #
+  # Linux
+  #
+
+  sheevaplug = rec {
+    config = "armv5tel-unknown-linux-gnueabi";
+    bigEndian = false;
+    arch = "armv5tel";
+    float = "soft";
+    withTLS = true;
+    libc = "glibc";
+    platform = platforms.sheevaplug;
+    openssl.system = "linux-generic32";
+    inherit (platform) gcc;
+  };
+
+  raspberryPi = rec {
+    config = "armv6l-unknown-linux-gnueabihf";
+    bigEndian = false;
+    arch = "armv6l";
+    float = "hard";
+    fpu = "vfp";
+    withTLS = true;
+    libc = "glibc";
+    platform = platforms.raspberrypi;
+    openssl.system = "linux-generic32";
+    inherit (platform) gcc;
+  };
+
+  armv7l-hf-multiplatform = rec {
+    config = "arm-unknown-linux-gnueabihf";
+    bigEndian = false;
+    arch = "armv7-a";
+    float = "hard";
+    fpu = "vfpv3-d16";
+    withTLS = true;
+    libc = "glibc";
+    platform = platforms.armv7l-hf-multiplatform;
+    openssl.system = "linux-generic32";
+    inherit (platform) gcc;
+  };
+
+  aarch64-multiplatform = rec {
+    config = "aarch64-unknown-linux-gnu";
+    bigEndian = false;
+    arch = "aarch64";
+    withTLS = true;
+    libc = "glibc";
+    platform = platforms.aarch64-multiplatform;
+    inherit (platform) gcc;
+  };
+
+  scaleway-c1 = armv7l-hf-multiplatform // rec {
+    platform = platforms.scaleway-c1;
+    inherit (platform) gcc;
+    inherit (gcc) fpu;
+  };
+
+  pogoplug4 = rec {
+    arch = "armv5tel";
+    config = "armv5tel-softfloat-linux-gnueabi";
+    float = "soft";
+
+    platform = platforms.pogoplug4;
+
+    inherit (platform) gcc;
+    libc = "glibc";
+
+    withTLS = true;
+    openssl.system = "linux-generic32";
+  };
+
+  fuloongminipc = rec {
+    config = "mips64el-unknown-linux-gnu";
+    bigEndian = false;
+    arch = "mips";
+    float = "hard";
+    withTLS = true;
+    libc = "glibc";
+    platform = platforms.fuloong2f_n32;
+    openssl.system = "linux-generic32";
+    inherit (platform) gcc;
+  };
+
+  #
+  # Darwin
+  #
+
+  iphone64 = {
+    config = "aarch64-apple-darwin14";
+    arch = "arm64";
+    libc = "libSystem";
+    platform = {};
+  };
+
+  iphone32 = {
+    config = "arm-apple-darwin10";
+    arch = "armv7-a";
+    libc = "libSystem";
+    platform = {};
+  };
+
+  #
+  # Windows
+  #
+
+  # 32 bit mingw-w64
+  mingw32 = {
+    config = "i686-pc-mingw32";
+    arch = "x86"; # Irrelevant
+    libc = "msvcrt"; # This distinguishes the mingw (non posix) toolchain
+    platform = {};
+  };
+
+  # 64 bit mingw-w64
+  mingwW64 = {
+    # That's the triplet they use in the mingw-w64 docs.
+    config = "x86_64-pc-mingw32";
+    arch = "x86_64"; # Irrelevant
+    libc = "msvcrt"; # This distinguishes the mingw (non posix) toolchain
+    platform = {};
+  };
+}
diff --git a/lib/systems/inspect.nix b/lib/systems/inspect.nix
new file mode 100644
index 0000000000000..a81e88989c0aa
--- /dev/null
+++ b/lib/systems/inspect.nix
@@ -0,0 +1,43 @@
+with import ./parse.nix;
+with import ../attrsets.nix;
+with import ../lists.nix;
+
+rec {
+  patterns = rec {
+    "32bit"      = { cpu = { bits = 32; }; };
+    "64bit"      = { cpu = { bits = 64; }; };
+    i686         = { cpu = cpuTypes.i686; };
+    x86_64       = { cpu = cpuTypes.x86_64; };
+    PowerPC      = { cpu = cpuTypes.powerpc; };
+    x86          = { cpu = { family = "x86"; }; };
+    Arm          = { cpu = { family = "arm"; }; };
+    Mips         = { cpu = { family = "mips"; }; };
+    BigEndian    = { cpu = { significantByte = significantBytes.bigEndian; }; };
+    LittleEndian = { cpu = { significantByte = significantBytes.littleEndian; }; };
+
+    BSD          = { kernel = { families = { inherit (kernelFamilies) bsd; }; }; };
+    Unix         = [ BSD Darwin Linux SunOS Hurd Cygwin ];
+
+    Darwin       = { kernel = kernels.darwin; };
+    Linux        = { kernel = kernels.linux; };
+    SunOS        = { kernel = kernels.solaris; };
+    FreeBSD      = { kernel = kernels.freebsd; };
+    Hurd         = { kernel = kernels.hurd; };
+    NetBSD       = { kernel = kernels.netbsd; };
+    OpenBSD      = { kernel = kernels.openbsd; };
+    Windows      = { kernel = kernels.windows; };
+    Cygwin       = { kernel = kernels.windows; abi = abis.cygnus; };
+    MinGW        = { kernel = kernels.windows; abi = abis.gnu; };
+
+    Arm32        = recursiveUpdate Arm patterns."32bit";
+    Arm64        = recursiveUpdate Arm patterns."64bit";
+  };
+
+  matchAnyAttrs = patterns:
+    if builtins.isList patterns then attrs: any (pattern: matchAttrs pattern attrs) patterns
+    else matchAttrs patterns;
+
+  predicates = mapAttrs'
+    (name: value: nameValuePair ("is" + name) (matchAnyAttrs value))
+    patterns;
+}
diff --git a/lib/systems/parse.nix b/lib/systems/parse.nix
index 8f65a69b17ab6..befe90f9abeb0 100644
--- a/lib/systems/parse.nix
+++ b/lib/systems/parse.nix
@@ -8,6 +8,7 @@
 with import ../lists.nix;
 with import ../types.nix;
 with import ../attrsets.nix;
+with (import ./inspect.nix).predicates;
 
 let
   lib = import ../default.nix;
@@ -43,7 +44,7 @@ rec {
     i686     = { bits = 32; significantByte = littleEndian; family = "x86"; };
     x86_64   = { bits = 64; significantByte = littleEndian; family = "x86"; };
     mips64el = { bits = 32; significantByte = littleEndian; family = "mips"; };
-    powerpc  = { bits = 32; significantByte = bigEndian;    family = "powerpc"; };
+    powerpc  = { bits = 32; significantByte = bigEndian;    family = "power"; };
   };
 
   isVendor = isType "vendor";
@@ -67,22 +68,25 @@ rec {
   isKernelFamily = isType "kernel-family";
   kernelFamilies = setTypes "kernel-family" {
     bsd = {};
-    unix = {};
   };
 
   isKernel = x: isType "kernel" x;
   kernels = with execFormats; with kernelFamilies; setTypesAssert "kernel"
     (x: isExecFormat x.execFormat && all isKernelFamily (attrValues x.families))
   {
-    darwin  = { execFormat = macho;   families = { inherit unix; }; };
-    freebsd = { execFormat = elf;     families = { inherit unix bsd; }; };
-    linux   = { execFormat = elf;     families = { inherit unix; }; };
-    netbsd  = { execFormat = elf;     families = { inherit unix bsd; }; };
-    none    = { execFormat = unknown; families = { inherit unix; }; };
-    openbsd = { execFormat = elf;     families = { inherit unix bsd; }; };
-    solaris = { execFormat = elf;     families = { inherit unix; }; };
+    darwin  = { execFormat = macho;   families = { }; };
+    freebsd = { execFormat = elf;     families = { inherit bsd; }; };
+    hurd    = { execFormat = elf;     families = { }; };
+    linux   = { execFormat = elf;     families = { }; };
+    netbsd  = { execFormat = elf;     families = { inherit bsd; }; };
+    none    = { execFormat = unknown; families = { }; };
+    openbsd = { execFormat = elf;     families = { inherit bsd; }; };
+    solaris = { execFormat = elf;     families = { }; };
     windows = { execFormat = pe;      families = { }; };
   } // { # aliases
+    # TODO(@Ericson2314): Handle these Darwin version suffixes more generally.
+    darwin10 = kernels.darwin;
+    darwin14 = kernels.darwin;
     win32 = kernels.windows;
   };
 
@@ -106,23 +110,12 @@ rec {
       inherit cpu vendor kernel abi;
     };
 
-  is64Bit = matchAttrs { cpu = { bits = 64; }; };
-  is32Bit = matchAttrs { cpu = { bits = 32; }; };
-  isi686 = matchAttrs { cpu = cpuTypes.i686; };
-  isx86_64 = matchAttrs { cpu = cpuTypes.x86_64; };
-
-  isDarwin = matchAttrs { kernel = kernels.darwin; };
-  isLinux = matchAttrs { kernel = kernels.linux; };
-  isUnix = matchAttrs { kernel = { families = { inherit (kernelFamilies) unix; }; }; };
-  isWindows = matchAttrs { kernel = kernels.windows; };
-  isCygwin = matchAttrs { kernel = kernels.windows; abi = abis.cygnus; };
-  isMinGW = matchAttrs { kernel = kernels.windows; abi = abis.gnu; };
-
-
   mkSkeletonFromList = l: {
     "2" = # We only do 2-part hacks for things Nix already supports
       if elemAt l 1 == "cygwin"
-        then { cpu = elemAt l 0;                      kernel = "windows"; abi = "cygnus";    }
+        then { cpu = elemAt l 0;                      kernel = "windows";  abi = "cygnus";   }
+      else if elemAt l 1 == "gnu"
+        then { cpu = elemAt l 0;                      kernel = "hurd";     abi = "gnu";      }
       else   { cpu = elemAt l 0;                      kernel = elemAt l 1;                   };
     "3" = # Awkwards hacks, beware!
       if elemAt l 1 == "apple"
@@ -150,27 +143,27 @@ rec {
     getKernel = name:  kernels.${name} or (throw "Unknown kernel: ${name}");
     getAbi    = name:     abis.${name} or (throw "Unknown ABI: ${name}");
 
-    system = rec {
+    parsed = rec {
       cpu = getCpu args.cpu;
       vendor =
         /**/ if args ? vendor    then getVendor args.vendor
-        else if isDarwin  system then vendors.apple
-        else if isWindows system then vendors.pc
+        else if isDarwin  parsed then vendors.apple
+        else if isWindows parsed then vendors.pc
         else                     vendors.unknown;
       kernel = getKernel args.kernel;
       abi =
         /**/ if args ? abi       then getAbi args.abi
-        else if isLinux   system then abis.gnu
-        else if isWindows system then abis.gnu
+        else if isLinux   parsed then abis.gnu
+        else if isWindows parsed then abis.gnu
         else                     abis.unknown;
     };
 
-  in mkSystem system;
+  in mkSystem parsed;
 
   mkSystemFromString = s: mkSystemFromSkeleton (mkSkeletonFromList (lib.splitString "-" s));
 
   doubleFromSystem = { cpu, vendor, kernel, abi, ... }:
-    if vendor == kernels.windows && abi == abis.cygnus
+    if abi == abis.cygnus
     then "${cpu.name}-cygwin"
     else "${cpu.name}-${kernel.name}";
 
diff --git a/lib/systems/platforms.nix b/lib/systems/platforms.nix
index 4322c8e2ff687..54ed8f3c12c1c 100644
--- a/lib/systems/platforms.nix
+++ b/lib/systems/platforms.nix
@@ -21,6 +21,39 @@ rec {
     kernelAutoModules = false;
   };
 
+  pogoplug4 = {
+    name = "pogoplug4";
+
+    gcc = {
+      arch = "armv5te";
+      float = "soft";
+    };
+
+    kernelMajor = "2.6";
+    kernelHeadersBaseConfig = "multi_v5_defconfig";
+    kernelBaseConfig = "multi_v5_defconfig";
+    kernelArch = "arm";
+    kernelAutoModules = false;
+    kernelExtraConfig =
+      ''
+        # Ubi for the mtd
+        MTD_UBI y
+        UBIFS_FS y
+        UBIFS_FS_XATTR y
+        UBIFS_FS_ADVANCED_COMPR y
+        UBIFS_FS_LZO y
+        UBIFS_FS_ZLIB y
+        UBIFS_FS_DEBUG n
+      '';
+    kernelMakeFlags = [ "LOADADDR=0x8000" ];
+    kernelTarget = "uImage";
+    # TODO reenable once manual-config's config actually builds a .dtb and this is checked to be working
+    #kernelDTB = true;
+
+    # XXX can be anything non-null, pkgs actually only cares if it is set or not
+    uboot = "pogoplug4";
+  };
+
   sheevaplug = {
     name = "sheevaplug";
     kernelMajor = "2.6";
@@ -222,6 +255,10 @@ rec {
       arch = "armv6";
       fpu = "vfp";
       float = "hard";
+      # TODO(@Ericson2314) what is this and is it a good idea? It was
+      # used in some cross compilation examples but not others.
+      #
+      # abi = "aapcs-linux";
     };
   };
 
@@ -307,6 +344,43 @@ rec {
     uboot = null;
   };
 
+  scaleway-c1 = armv7l-hf-multiplatform // {
+    gcc = {
+      cpu = "cortex-a9";
+      fpu = "vfpv3";
+      float = "hard";
+    };
+  };
+
+  utilite = {
+    name = "utilite";
+    kernelMajor = "2.6";
+    kernelHeadersBaseConfig = "multi_v7_defconfig";
+    kernelBaseConfig = "multi_v7_defconfig";
+    kernelArch = "arm";
+    kernelAutoModules = false;
+    kernelExtraConfig =
+      ''
+        # Ubi for the mtd
+        MTD_UBI y
+        UBIFS_FS y
+        UBIFS_FS_XATTR y
+        UBIFS_FS_ADVANCED_COMPR y
+        UBIFS_FS_LZO y
+        UBIFS_FS_ZLIB y
+        UBIFS_FS_DEBUG n
+      '';
+    kernelMakeFlags = [ "LOADADDR=0x10800000" ];
+    kernelTarget = "uImage";
+    kernelDTB = true;
+    uboot = true; #XXX: any non-null value here is needed so that mkimage is present to build kernelTarget uImage
+    gcc = {
+      cpu = "cortex-a9";
+      fpu = "neon";
+      float = "hard";
+    };
+  };
+
   guruplug = sheevaplug // {
     # Define `CONFIG_MACH_GURUPLUG' (see
     # <http://kerneltrap.org/mailarchive/git-commits-head/2010/5/19/33618>)
@@ -390,7 +464,10 @@ rec {
     '';
     kernelTarget = "vmlinux";
     uboot = null;
-    gcc.arch = "loongson2f";
+    gcc = {
+      arch = "loongson2f";
+      abi = "n32";
+    };
   };
 
   beaglebone = armv7l-hf-multiplatform // {
@@ -466,6 +543,10 @@ rec {
 
       # Cavium ThunderX stuff.
       PCI_HOST_THUNDER_ECAM y
+
+      # The default (=y) forces us to have the XHCI firmware available in initrd,
+      # which our initrd builder can't currently do easily.
+      USB_XHCI_TEGRA m
     '';
     uboot = null;
     kernelTarget = "Image";
diff --git a/lib/tests.nix b/lib/tests/misc.nix
index 995a743fe43f1..4c4c06fe78dab 100644
--- a/lib/tests.nix
+++ b/lib/tests/misc.nix
@@ -1,11 +1,13 @@
 # to run these tests:
-# nix-instantiate --eval --strict nixpkgs/lib/tests.nix
+# nix-instantiate --eval --strict nixpkgs/lib/tests/misc.nix
 # if the resulting list is empty, all tests passed
-let inherit (builtins) add; in
-with import ./default.nix;
+with import ../default.nix;
 
 runTests {
 
+
+# TRIVIAL
+
   testId = {
     expr = id 1;
     expected = 1;
@@ -33,6 +35,18 @@ runTests {
     expected = {a = "a";};
   };
 
+  testComposeExtensions = {
+    expr = let obj = makeExtensible (self: { foo = self.bar; });
+               f = self: super: { bar = false; baz = true; };
+               g = self: super: { bar = super.baz or false; };
+               f_o_g = composeExtensions f g;
+               composed = obj.extend f_o_g;
+           in composed.foo;
+    expected = true;
+  };
+
+# STRINGS
+
   testConcatMapStrings = {
     expr = concatMapStrings (x: x + ";") ["a" "b" "c"];
     expected = "a;b;c;";
@@ -43,6 +57,69 @@ runTests {
     expected = "a,b,c";
   };
 
+  testSplitStringsSimple = {
+    expr = strings.splitString "." "a.b.c.d";
+    expected = [ "a" "b" "c" "d" ];
+  };
+
+  testSplitStringsEmpty = {
+    expr = strings.splitString "." "a..b";
+    expected = [ "a" "" "b" ];
+  };
+
+  testSplitStringsOne = {
+    expr = strings.splitString ":" "a.b";
+    expected = [ "a.b" ];
+  };
+
+  testSplitStringsNone = {
+    expr = strings.splitString "." "";
+    expected = [ "" ];
+  };
+
+  testSplitStringsFirstEmpty = {
+    expr = strings.splitString "/" "/a/b/c";
+    expected = [ "" "a" "b" "c" ];
+  };
+
+  testSplitStringsLastEmpty = {
+    expr = strings.splitString ":" "2001:db8:0:0042::8a2e:370:";
+    expected = [ "2001" "db8" "0" "0042" "" "8a2e" "370" "" ];
+  };
+
+  testIsStorePath =  {
+    expr =
+      let goodPath =
+            "${builtins.storeDir}/d945ibfx9x185xf04b890y4f9g3cbb63-python-2.7.11";
+      in {
+        storePath = isStorePath goodPath;
+        storePathAppendix = isStorePath
+          "${goodPath}/bin/python";
+        nonAbsolute = isStorePath (concatStrings (tail (stringToCharacters goodPath)));
+        asPath = isStorePath (builtins.toPath goodPath);
+        otherPath = isStorePath "/something/else";
+        otherVals = {
+          attrset = isStorePath {};
+          list = isStorePath [];
+          int = isStorePath 42;
+        };
+      };
+    expected = {
+      storePath = true;
+      storePathAppendix = false;
+      nonAbsolute = false;
+      asPath = true;
+      otherPath = false;
+      otherVals = {
+        attrset = false;
+        list = false;
+        int = false;
+      };
+    };
+  };
+
+# LISTS
+
   testFilter = {
     expr = filter (x: x != "a") ["a" "b" "c" "a"];
     expected = ["b" "c"];
@@ -93,45 +170,6 @@ runTests {
     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];
@@ -158,9 +196,9 @@ runTests {
   };
 
 
-  /* Generator tests */
-  # these tests assume attributes are converted to lists
-  # in alphabetical order
+# GENERATORS
+# these tests assume attributes are converted to lists
+# in alphabetical order
 
   testMkKeyValueDefault = {
     expr = generators.mkKeyValueDefault ":" "f:oo" "bar";
@@ -247,43 +285,76 @@ runTests {
       expected = builtins.toJSON val;
   };
 
-  testSplitStringsSimple = {
-    expr = strings.splitString "." "a.b.c.d";
-    expected = [ "a" "b" "c" "d" ];
+  testToPretty = {
+    expr = mapAttrs (const (generators.toPretty {})) rec {
+      int = 42;
+      bool = true;
+      string = "fnord";
+      null_ = null;
+      function = x: x;
+      functionArgs = { arg ? 4, foo }: arg;
+      list = [ 3 4 function [ false ] ];
+      attrs = { foo = null; "foo bar" = "baz"; };
+      drv = derivation { name = "test"; system = builtins.currentSystem; };
+    };
+    expected = rec {
+      int = "42";
+      bool = "true";
+      string = "\"fnord\"";
+      null_ = "null";
+      function = "<λ>";
+      functionArgs = "<λ:{(arg),foo}>";
+      list = "[ 3 4 ${function} [ false ] ]";
+      attrs = "{ \"foo\" = null; \"foo bar\" = \"baz\"; }";
+      drv = "<δ>";
+    };
   };
 
-  testSplitStringsEmpty = {
-    expr = strings.splitString "." "a..b";
-    expected = [ "a" "" "b" ];
+  testToPrettyAllowPrettyValues = {
+    expr = generators.toPretty { allowPrettyValues = true; }
+             { __pretty = v: "«" + v + "»"; val = "foo"; };
+    expected  = "«foo»";
   };
 
-  testSplitStringsOne = {
-    expr = strings.splitString ":" "a.b";
-    expected = [ "a.b" ];
-  };
 
-  testSplitStringsNone = {
-    expr = strings.splitString "." "";
-    expected = [ "" ];
-  };
+# MISC
 
-  testSplitStringsFirstEmpty = {
-    expr = strings.splitString "/" "/a/b/c";
-    expected = [ "" "a" "b" "c" ];
-  };
+  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 = builtins.add x.a 3; });
+          res6 = let x = defaultOverridableDelayableArgs id { a = 7; mergeAttrBy = { a = builtins.add; }; };
+                     y = x.merge {};
+                in (y.merge) { a = 10; };
 
-  testSplitStringsLastEmpty = {
-    expr = strings.splitString ":" "2001:db8:0:0042::8a2e:370:";
-    expected = [ "2001" "db8" "0" "0042" "" "8a2e" "370" "" ];
-  };
+          resRem7 = res6.replace (a: removeAttrs a ["a"]);
 
-  testComposeExtensions = {
-    expr = let obj = makeExtensible (self: { foo = self.bar; });
-               f = self: super: { bar = false; baz = true; };
-               g = self: super: { bar = super.baz or false; };
-               f_o_g = composeExtensions f g;
-               composed = obj.extend f_o_g;
-           in composed.foo;
+          resReplace6 = let x = defaultOverridableDelayableArgs id { a = 7; mergeAttrBy = { a = builtins.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;
   };
 
diff --git a/lib/tests/release.nix b/lib/tests/release.nix
index dfa4ca2676d1f..a6184041682f0 100644
--- a/lib/tests/release.nix
+++ b/lib/tests/release.nix
@@ -1,40 +1,32 @@
-{ nixpkgs ? { outPath = (import ../.).cleanSource ../..; revCount = 1234; shortRev = "abcdef"; }
-, # The platforms for which we build Nixpkgs.
-  supportedSystems ? [ builtins.currentSystem ]
-, # Strip most of attributes when evaluating to spare memory usage
-  scrubJobs ? true
-}:
+{ pkgs ? import ((import ../.).cleanSource ../..) {} }:
 
-with import ../../pkgs/top-level/release-lib.nix { inherit supportedSystems scrubJobs; };
-with lib;
+pkgs.stdenv.mkDerivation {
+  name = "nixpkgs-lib-tests";
+  buildInputs = [ pkgs.nix ];
+  NIX_PATH="nixpkgs=${pkgs.path}";
 
-{
-  systems = import ./systems.nix { inherit lib assertTrue; };
+  buildCommand = ''
+    datadir="${pkgs.nix}/share"
+    export TEST_ROOT=$(pwd)/test-tmp
+    export NIX_BUILD_HOOK=
+    export NIX_CONF_DIR=$TEST_ROOT/etc
+    export NIX_DB_DIR=$TEST_ROOT/db
+    export NIX_LOCALSTATE_DIR=$TEST_ROOT/var
+    export NIX_LOG_DIR=$TEST_ROOT/var/log/nix
+    export NIX_MANIFESTS_DIR=$TEST_ROOT/var/nix/manifests
+    export NIX_STATE_DIR=$TEST_ROOT/var/nix
+    export NIX_STORE_DIR=$TEST_ROOT/store
+    export PAGER=cat
+    cacheDir=$TEST_ROOT/binary-cache
+    nix-store --init
 
-  moduleSystem = pkgs.stdenv.mkDerivation {
-    name = "nixpkgs-lib-tests";
-    buildInputs = [ pkgs.nix ];
-    NIX_PATH="nixpkgs=${nixpkgs}";
+    cd ${pkgs.path}/lib/tests
+    ./modules.sh
 
-    buildCommand = ''
-      datadir="${pkgs.nix}/share"
-      export TEST_ROOT=$(pwd)/test-tmp
-      export NIX_BUILD_HOOK=
-      export NIX_CONF_DIR=$TEST_ROOT/etc
-      export NIX_DB_DIR=$TEST_ROOT/db
-      export NIX_LOCALSTATE_DIR=$TEST_ROOT/var
-      export NIX_LOG_DIR=$TEST_ROOT/var/log/nix
-      export NIX_MANIFESTS_DIR=$TEST_ROOT/var/nix/manifests
-      export NIX_STATE_DIR=$TEST_ROOT/var/nix
-      export NIX_STORE_DIR=$TEST_ROOT/store
-      export PAGER=cat
-      cacheDir=$TEST_ROOT/binary-cache
-      nix-store --init
+    [[ "$(nix-instantiate --eval --strict misc.nix)" == "[ ]" ]]
 
-      cd ${nixpkgs}/lib/tests
-      ./modules.sh
+    [[ "$(nix-instantiate --eval --strict systems.nix)" == "[ ]" ]]
 
-      touch $out
-    '';
-  };
+    touch $out
+  '';
 }
diff --git a/lib/tests/systems.nix b/lib/tests/systems.nix
index 5eacc0defafb4..523fd3feb3b0c 100644
--- a/lib/tests/systems.nix
+++ b/lib/tests/systems.nix
@@ -5,13 +5,13 @@
 # calculating the lists anyway?". The answer is one can mindlessly update these
 # tests as new platforms become supported, and then just give the diff a quick
 # sanity check before committing :).
-{ lib, assertTrue }:
-
-with lib.systems.doubles;
-
-let mseteq = x: y: lib.sort lib.lessThan x == lib.sort lib.lessThan y; in
-
-{
+let
+  lib = import ../default.nix;
+  mseteq = x: y: {
+    expr     = lib.sort lib.lessThan x;
+    expected = lib.sort lib.lessThan y;
+  };
+in with lib.systems.doubles; lib.runTests {
   all = assertTrue (mseteq all (linux ++ darwin ++ cygwin ++ freebsd ++ openbsd ++ netbsd ++ illumos));
 
   arm = assertTrue (mseteq arm [ "armv5tel-linux" "armv6l-linux" "armv7l-linux" ]);
diff --git a/lib/trivial.nix b/lib/trivial.nix
index cec28b1a22b11..9ee0549fc0fb1 100644
--- a/lib/trivial.nix
+++ b/lib/trivial.nix
@@ -43,82 +43,6 @@ rec {
   */
   mergeAttrs = x: y: x // y;
 
-
-  # Compute the fixed point of the given function `f`, which is usually an
-  # attribute set that expects its final, non-recursive representation as an
-  # argument:
-  #
-  #     f = self: { foo = "foo"; bar = "bar"; foobar = self.foo + self.bar; }
-  #
-  # Nix evaluates this recursion until all references to `self` have been
-  # resolved. At that point, the final result is returned and `f x = x` holds:
-  #
-  #     nix-repl> fix f
-  #     { bar = "bar"; foo = "foo"; foobar = "foobar"; }
-  #
-  # See https://en.wikipedia.org/wiki/Fixed-point_combinator for further
-  # details.
-  fix = f: let x = f x; in x;
-
-  # A variant of `fix` that records the original recursive attribute set in the
-  # result. This is useful in combination with the `extends` function to
-  # implement deep overriding. See pkgs/development/haskell-modules/default.nix
-  # for a concrete example.
-  fix' = f: let x = f x // { __unfix__ = f; }; in x;
-
-  # Modify the contents of an explicitly recursive attribute set in a way that
-  # honors `self`-references. This is accomplished with a function
-  #
-  #     g = self: super: { foo = super.foo + " + "; }
-  #
-  # that has access to the unmodified input (`super`) as well as the final
-  # non-recursive representation of the attribute set (`self`). `extends`
-  # differs from the native `//` operator insofar as that it's applied *before*
-  # references to `self` are resolved:
-  #
-  #     nix-repl> fix (extends g f)
-  #     { bar = "bar"; foo = "foo + "; foobar = "foo + bar"; }
-  #
-  # The name of the function is inspired by object-oriented inheritance, i.e.
-  # think of it as an infix operator `g extends f` that mimics the syntax from
-  # Java. It may seem counter-intuitive to have the "base class" as the second
-  # argument, but it's nice this way if several uses of `extends` are cascaded.
-  extends = f: rattrs: self: let super = rattrs self; in super // f self super;
-
-  # Compose two extending functions of the type expected by 'extends'
-  # into one where changes made in the first are available in the
-  # 'super' of the second
-  composeExtensions =
-    f: g: self: super:
-      let fApplied = f self super;
-          super' = super // fApplied;
-      in fApplied // g self super';
-
-  # Create an overridable, recursive attribute set. For example:
-  #
-  #     nix-repl> obj = makeExtensible (self: { })
-  #
-  #     nix-repl> obj
-  #     { __unfix__ = «lambda»; extend = «lambda»; }
-  #
-  #     nix-repl> obj = obj.extend (self: super: { foo = "foo"; })
-  #
-  #     nix-repl> obj
-  #     { __unfix__ = «lambda»; extend = «lambda»; foo = "foo"; }
-  #
-  #     nix-repl> obj = obj.extend (self: super: { foo = super.foo + " + "; bar = "bar"; foobar = self.foo + self.bar; })
-  #
-  #     nix-repl> obj
-  #     { __unfix__ = «lambda»; bar = "bar"; extend = «lambda»; foo = "foo + "; foobar = "foo + bar"; }
-  makeExtensible = makeExtensibleWithCustomName "extend";
-
-  # Same as `makeExtensible` but the name of the extending attribute is
-  # customized.
-  makeExtensibleWithCustomName = extenderName: rattrs:
-    fix' rattrs // {
-      ${extenderName} = f: makeExtensibleWithCustomName extenderName (extends f rattrs);
-   };
-
   # Flip the order of the arguments of a binary function.
   flip = f: a: b: f b a;
 
diff --git a/lib/types.nix b/lib/types.nix
index 45122759bfcaf..a7dcd3f1e1c77 100644
--- a/lib/types.nix
+++ b/lib/types.nix
@@ -92,6 +92,8 @@ rec {
     };
 
 
+  # When adding new types don't forget to document them in
+  # nixos/doc/manual/development/option-types.xml!
   types = rec {
 
     unspecified = mkOptionType {
@@ -177,9 +179,9 @@ rec {
       description = "list of ${elemType.description}s";
       check = isList;
       merge = loc: defs:
-        map (x: x.value) (filter (x: x ? value) (concatLists (imap (n: def:
+        map (x: x.value) (filter (x: x ? value) (concatLists (imap1 (n: def:
           if isList def.value then
-            imap (m: def':
+            imap1 (m: def':
               (mergeDefinitions
                 (loc ++ ["[definition ${toString n}-entry ${toString m}]"])
                 elemType
@@ -218,7 +220,7 @@ rec {
           if isList def.value then
             { inherit (def) file;
               value = listToAttrs (
-                imap (elemIdx: elem:
+                imap1 (elemIdx: elem:
                   { name = elem.name or "unnamed-${toString defIdx}.${toString elemIdx}";
                     value = elem;
                   }) def.value);
@@ -231,7 +233,7 @@ rec {
         name = "loaOf";
         description = "list or attribute set of ${elemType.description}s";
         check = x: isList x || isAttrs x;
-        merge = loc: defs: attrOnly.merge loc (imap convertIfList defs);
+        merge = loc: defs: attrOnly.merge loc (imap1 convertIfList defs);
         getSubOptions = prefix: elemType.getSubOptions (prefix ++ ["<name?>"]);
         getSubModules = elemType.getSubModules;
         substSubModules = m: loaOf (elemType.substSubModules m);
@@ -257,6 +259,7 @@ rec {
       functor = (defaultFunctor name) // { wrapped = elemType; };
     };
 
+    # Value of given type but with no merging (i.e. `uniq list`s are not concatenated).
     uniq = elemType: mkOptionType rec {
       name = "uniq";
       inherit (elemType) description check;
@@ -267,6 +270,7 @@ rec {
       functor = (defaultFunctor name) // { wrapped = elemType; };
     };
 
+    # Null or value of ...
     nullOr = elemType: mkOptionType rec {
       name = "nullOr";
       description = "null or ${elemType.description}";
@@ -283,6 +287,7 @@ rec {
       functor = (defaultFunctor name) // { wrapped = elemType; };
     };
 
+    # A submodule (like typed attribute set). See NixOS manual.
     submodule = opts:
       let
         opts' = toList opts;
@@ -314,6 +319,7 @@ rec {
         };
       };
 
+    # A value from a set of allowed ones.
     enum = values:
       let
         show = v:
@@ -329,6 +335,7 @@ rec {
         functor = (defaultFunctor name) // { payload = values; binOp = a: b: unique (a ++ b); };
       };
 
+    # Either value of type `t1` or `t2`.
     either = t1: t2: mkOptionType rec {
       name = "either";
       description = "${t1.description} or ${t2.description}";
@@ -352,6 +359,8 @@ rec {
       functor = (defaultFunctor name) // { wrapped = [ t1 t2 ]; };
     };
 
+    # Either value of type `finalType` or `coercedType`, the latter is
+    # converted to `finalType` using `coerceFunc`.
     coercedTo = coercedType: coerceFunc: finalType:
       assert coercedType.getSubModules == null;
       mkOptionType rec {