about summary refs log tree commit diff
path: root/nixpkgs/lib
diff options
context:
space:
mode:
authorAlyssa Ross <hi@alyssa.is>2021-02-16 18:32:21 +0000
committerAlyssa Ross <hi@alyssa.is>2021-02-16 18:32:21 +0000
commit9becdcc5df71b47a5da84ad670e9a7eae9e0c65a (patch)
tree2ddf0335eb393f89501e3753b50c3f7ab0552d12 /nixpkgs/lib
parent49f2a77ac9abc88c253f68952eda26557fc3b555 (diff)
parentff96a0fa5635770390b184ae74debea75c3fd534 (diff)
downloadnixlib-9becdcc5df71b47a5da84ad670e9a7eae9e0c65a.tar
nixlib-9becdcc5df71b47a5da84ad670e9a7eae9e0c65a.tar.gz
nixlib-9becdcc5df71b47a5da84ad670e9a7eae9e0c65a.tar.bz2
nixlib-9becdcc5df71b47a5da84ad670e9a7eae9e0c65a.tar.lz
nixlib-9becdcc5df71b47a5da84ad670e9a7eae9e0c65a.tar.xz
nixlib-9becdcc5df71b47a5da84ad670e9a7eae9e0c65a.tar.zst
nixlib-9becdcc5df71b47a5da84ad670e9a7eae9e0c65a.zip
nixpkgs: merge nixos-unstable
Diffstat (limited to 'nixpkgs/lib')
-rw-r--r--nixpkgs/lib/attrsets.nix19
-rw-r--r--nixpkgs/lib/debug.nix22
-rw-r--r--nixpkgs/lib/default.nix4
-rw-r--r--nixpkgs/lib/licenses.nix10
-rw-r--r--nixpkgs/lib/lists.nix6
-rw-r--r--nixpkgs/lib/systems/default.nix2
-rw-r--r--nixpkgs/lib/systems/doubles.nix5
-rw-r--r--nixpkgs/lib/systems/examples.nix19
-rw-r--r--nixpkgs/lib/systems/parse.nix9
-rw-r--r--nixpkgs/lib/systems/platforms.nix2
-rw-r--r--nixpkgs/lib/tests/misc.nix67
-rwxr-xr-xnixpkgs/lib/tests/modules.sh7
-rw-r--r--nixpkgs/lib/tests/modules/functionTo/list-order.nix25
-rw-r--r--nixpkgs/lib/tests/modules/functionTo/merging-attrs.nix27
-rw-r--r--nixpkgs/lib/tests/modules/functionTo/merging-list.nix24
-rw-r--r--nixpkgs/lib/tests/modules/functionTo/trivial.nix17
-rw-r--r--nixpkgs/lib/tests/modules/functionTo/wrong-type.nix18
-rw-r--r--nixpkgs/lib/tests/systems.nix2
-rw-r--r--nixpkgs/lib/types.nix11
19 files changed, 287 insertions, 9 deletions
diff --git a/nixpkgs/lib/attrsets.nix b/nixpkgs/lib/attrsets.nix
index d91d7a0cd47e..0ce3aaeca452 100644
--- a/nixpkgs/lib/attrsets.nix
+++ b/nixpkgs/lib/attrsets.nix
@@ -183,6 +183,24 @@ rec {
     else
       [];
 
+  /* Return the cartesian product of attribute set value combinations.
+
+    Example:
+      cartesianProductOfSets { a = [ 1 2 ]; b = [ 10 20 ]; }
+      => [
+           { a = 1; b = 10; }
+           { a = 1; b = 20; }
+           { a = 2; b = 10; }
+           { a = 2; b = 20; }
+         ]
+  */
+  cartesianProductOfSets = attrsOfLists:
+    lib.foldl' (listOfAttrs: attrName:
+      concatMap (attrs:
+        map (listValue: attrs // { ${attrName} = listValue; }) attrsOfLists.${attrName}
+      ) listOfAttrs
+    ) [{}] (attrNames attrsOfLists);
+
 
   /* Utility function that creates a {name, value} pair as expected by
      builtins.listToAttrs.
@@ -493,5 +511,4 @@ rec {
   zipWithNames = zipAttrsWithNames;
   zip = builtins.trace
     "lib.zip is deprecated, use lib.zipAttrsWith instead" zipAttrsWith;
-
 }
diff --git a/nixpkgs/lib/debug.nix b/nixpkgs/lib/debug.nix
index ea6aed60ab43..e3ca3352397e 100644
--- a/nixpkgs/lib/debug.nix
+++ b/nixpkgs/lib/debug.nix
@@ -148,6 +148,28 @@ rec {
   /* A combination of `traceVal` and `traceSeqN`. */
   traceValSeqN = traceValSeqNFn id;
 
+  /* Trace the input and output of a function `f` named `name`,
+  both down to `depth`.
+
+  This is useful for adding around a function call,
+  to see the before/after of values as they are transformed.
+
+     Example:
+       traceFnSeqN 2 "id" (x: x) { a.b.c = 3; }
+       trace: { fn = "id"; from = { a.b = {…}; }; to = { a.b = {…}; }; }
+       => { a.b.c = 3; }
+  */
+  traceFnSeqN = depth: name: f: v:
+    let res = f v;
+    in lib.traceSeqN
+        (depth + 1)
+        {
+          fn = name;
+          from = v;
+          to = res;
+        }
+        res;
+
 
   # -- TESTING --
 
diff --git a/nixpkgs/lib/default.nix b/nixpkgs/lib/default.nix
index f985266ed938..50320669e280 100644
--- a/nixpkgs/lib/default.nix
+++ b/nixpkgs/lib/default.nix
@@ -78,7 +78,7 @@ let
       zipAttrsWithNames zipAttrsWith zipAttrs recursiveUpdateUntil
       recursiveUpdate matchAttrs overrideExisting getOutput getBin
       getLib getDev getMan chooseDevOutputs zipWithNames zip
-      recurseIntoAttrs dontRecurseIntoAttrs;
+      recurseIntoAttrs dontRecurseIntoAttrs cartesianProductOfSets;
     inherit (self.lists) singleton forEach foldr fold foldl foldl' imap0 imap1
       concatMap flatten remove findSingle findFirst any all count
       optional optionals toList range partition zipListsWith zipLists
@@ -130,7 +130,7 @@ let
       assertMsg assertOneOf;
     inherit (self.debug) addErrorContextToAttrs traceIf traceVal traceValFn
       traceXMLVal traceXMLValMarked traceSeq traceSeqN traceValSeq
-      traceValSeqFn traceValSeqN traceValSeqNFn traceShowVal
+      traceValSeqFn traceValSeqN traceValSeqNFn traceFnSeqN traceShowVal
       traceShowValMarked showVal traceCall traceCall2 traceCall3
       traceValIfNot runTests testAllTrue traceCallXml attrNamesToStr;
     inherit (self.misc) maybeEnv defaultMergeArg defaultMerge foldArgs
diff --git a/nixpkgs/lib/licenses.nix b/nixpkgs/lib/licenses.nix
index 190eeefc1bf8..ee136c7337c1 100644
--- a/nixpkgs/lib/licenses.nix
+++ b/nixpkgs/lib/licenses.nix
@@ -100,6 +100,11 @@ lib.mapAttrs (n: v: v // { shortName = n; }) {
     fullName = "BSD Zero Clause License";
   };
 
+  bsd1 = spdx {
+    spdxId = "BSD-1-Clause";
+    fullName = "BSD 1-Clause License";
+  };
+
   bsd2 = spdx {
     spdxId = "BSD-2-Clause";
     fullName = ''BSD 2-clause "Simplified" License'';
@@ -301,6 +306,11 @@ lib.mapAttrs (n: v: v // { shortName = n; }) {
     fullName = "GNU Free Documentation License v1.1 only";
   };
 
+  fdl11Plus = spdx {
+    spdxId = "GFDL-1.1-or-later";
+    fullName = "GNU Free Documentation License v1.1 or later";
+  };
+
   fdl12Only = spdx {
     spdxId = "GFDL-1.2-only";
     fullName = "GNU Free Documentation License v1.2 only";
diff --git a/nixpkgs/lib/lists.nix b/nixpkgs/lib/lists.nix
index 06cee2eb112a..e469f3ef2652 100644
--- a/nixpkgs/lib/lists.nix
+++ b/nixpkgs/lib/lists.nix
@@ -241,7 +241,7 @@ rec {
 
   /* 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').
+     (e.g. `++ optional (system == "i686-linux") firefox').
 
      Type: optional :: bool -> a -> [a]
 
@@ -629,7 +629,9 @@ rec {
       crossLists (x:y: "${toString x}${toString y}") [[1 2] [3 4]]
       => [ "13" "14" "23" "24" ]
   */
-  crossLists = f: foldl (fs: args: concatMap (f: map f args) fs) [f];
+  crossLists = builtins.trace
+    "lib.crossLists is deprecated, use lib.cartesianProductOfSets instead"
+    (f: foldl (fs: args: concatMap (f: map f args) fs) [f]);
 
 
   /* Remove duplicate elements from the list. O(n^2) complexity.
diff --git a/nixpkgs/lib/systems/default.nix b/nixpkgs/lib/systems/default.nix
index 1a89120e2bf6..1bbe976c4d21 100644
--- a/nixpkgs/lib/systems/default.nix
+++ b/nixpkgs/lib/systems/default.nix
@@ -92,6 +92,8 @@ rec {
         else if final.isx86_32 then "i386"
         else if final.isx86_64 then "x86_64"
         else if final.isMips then "mips"
+        else if final.isPower then "powerpc"
+        else if final.isRiscV then "riscv"
         else final.parsed.cpu.name;
 
       qemuArch =
diff --git a/nixpkgs/lib/systems/doubles.nix b/nixpkgs/lib/systems/doubles.nix
index b0bc7dd1188a..07327fa22736 100644
--- a/nixpkgs/lib/systems/doubles.nix
+++ b/nixpkgs/lib/systems/doubles.nix
@@ -24,6 +24,7 @@ let
 
     "x86_64-redox"
 
+    "powerpc64-linux"
     "powerpc64le-linux"
 
     "riscv32-linux" "riscv64-linux"
@@ -72,7 +73,7 @@ in {
   darwin        = filterDoubles predicates.isDarwin;
   freebsd       = filterDoubles predicates.isFreeBSD;
   # Should be better, but MinGW is unclear.
-  gnu           = filterDoubles (matchAttrs { kernel = parse.kernels.linux; abi = parse.abis.gnu; }) ++ filterDoubles (matchAttrs { kernel = parse.kernels.linux; abi = parse.abis.gnueabi; }) ++ filterDoubles (matchAttrs { kernel = parse.kernels.linux; abi = parse.abis.gnueabihf; });
+  gnu           = filterDoubles (matchAttrs { kernel = parse.kernels.linux; abi = parse.abis.gnu; }) ++ filterDoubles (matchAttrs { kernel = parse.kernels.linux; abi = parse.abis.gnueabi; }) ++ filterDoubles (matchAttrs { kernel = parse.kernels.linux; abi = parse.abis.gnueabihf; }) ++ filterDoubles (matchAttrs { kernel = parse.kernels.linux; abi = parse.abis.elfv1; }) ++ filterDoubles (matchAttrs { kernel = parse.kernels.linux; abi = parse.abis.elfv2; });
   illumos       = filterDoubles predicates.isSunOS;
   linux         = filterDoubles predicates.isLinux;
   netbsd        = filterDoubles predicates.isNetBSD;
@@ -85,5 +86,5 @@ in {
 
   embedded      = filterDoubles predicates.isNone;
 
-  mesaPlatforms = ["i686-linux" "x86_64-linux" "x86_64-darwin" "armv5tel-linux" "armv6l-linux" "armv7l-linux" "armv7a-linux" "aarch64-linux" "powerpc64le-linux"];
+  mesaPlatforms = ["i686-linux" "x86_64-linux" "x86_64-darwin" "armv5tel-linux" "armv6l-linux" "armv7l-linux" "armv7a-linux" "aarch64-linux" "powerpc64-linux" "powerpc64le-linux"];
 }
diff --git a/nixpkgs/lib/systems/examples.nix b/nixpkgs/lib/systems/examples.nix
index e8cf15479c05..8a43b86db701 100644
--- a/nixpkgs/lib/systems/examples.nix
+++ b/nixpkgs/lib/systems/examples.nix
@@ -21,6 +21,19 @@ rec {
     config = "powerpc64le-unknown-linux-musl";
   };
 
+  ppc64-elfv1 = {
+    config = "powerpc64-unknown-linux-elfv1";
+  };
+  ppc64-elfv2 = {
+    config = "powerpc64-unknown-linux-elfv2";
+  };
+  ppc64 = ppc64-elfv2; # default to modern elfv2
+
+  ppc64-musl = {
+    config = "powerpc64-unknown-linux-musl";
+    gcc = { abi = "elfv2"; }; # for gcc configuration
+  };
+
   sheevaplug = {
     config = "armv5tel-unknown-linux-gnueabi";
   } // platforms.sheevaplug;
@@ -131,6 +144,12 @@ rec {
   armhf-embedded = {
     config = "arm-none-eabihf";
     libc = "newlib";
+    # GCC8+ does not build without this
+    # (https://www.mail-archive.com/gcc-bugs@gcc.gnu.org/msg552339.html):
+    gcc = {
+      arch = "armv5t";
+      fpu = "vfp";
+    };
   };
 
   aarch64-embedded = {
diff --git a/nixpkgs/lib/systems/parse.nix b/nixpkgs/lib/systems/parse.nix
index a06ac0d11f74..8e012622ccd0 100644
--- a/nixpkgs/lib/systems/parse.nix
+++ b/nixpkgs/lib/systems/parse.nix
@@ -337,10 +337,18 @@ rec {
             The "gnu" ABI is ambiguous on 32-bit ARM. Use "gnueabi" or "gnueabihf" instead.
           '';
         }
+        { assertion = platform: platform.system != "powerpc64-linux";
+          message = ''
+            The "gnu" ABI is ambiguous on big-endian 64-bit PPC. Use "elfv1" or "elfv2" instead.
+          '';
+        }
       ];
     };
     gnuabi64     = { abi = "64"; };
 
+    elfv1        = { abi = "elfv1"; };
+    elfv2        = { abi = "elfv2"; };
+
     musleabi     = { float = "soft"; };
     musleabihf   = { float = "hard"; };
     musl         = {};
@@ -444,6 +452,7 @@ rec {
             if lib.versionAtLeast (parsed.cpu.version or "0") "6"
             then abis.gnueabihf
             else abis.gnueabi
+          else if cpu == "powerpc64" then abis.elfv2
           else abis.gnu
         else                     abis.unknown;
     };
diff --git a/nixpkgs/lib/systems/platforms.nix b/nixpkgs/lib/systems/platforms.nix
index f399c1873f5e..3bf90cd0cafb 100644
--- a/nixpkgs/lib/systems/platforms.nix
+++ b/nixpkgs/lib/systems/platforms.nix
@@ -300,7 +300,7 @@ rec {
       baseConfig = "multi_v7_defconfig";
       DTB = true;
       autoModules = true;
-      PreferBuiltin = true;
+      preferBuiltin = true;
       target = "zImage";
       extraConfig = ''
         # Serial port for Raspberry Pi 3. Upstream forgot to add it to the ARMv7 defconfig.
diff --git a/nixpkgs/lib/tests/misc.nix b/nixpkgs/lib/tests/misc.nix
index 35a5801c724f..0d249968402d 100644
--- a/nixpkgs/lib/tests/misc.nix
+++ b/nixpkgs/lib/tests/misc.nix
@@ -660,4 +660,71 @@ runTests {
     expected = [ [ "foo" ] [ "foo" "<name>" "bar" ] [ "foo" "bar" ] ];
   };
 
+  testCartesianProductOfEmptySet = {
+    expr = cartesianProductOfSets {};
+    expected = [ {} ];
+  };
+
+  testCartesianProductOfOneSet = {
+    expr = cartesianProductOfSets { a = [ 1 2 3 ]; };
+    expected = [ { a = 1; } { a = 2; } { a = 3; } ];
+  };
+
+  testCartesianProductOfTwoSets = {
+    expr = cartesianProductOfSets { a = [ 1 ]; b = [ 10 20 ]; };
+    expected = [
+      { a = 1; b = 10; }
+      { a = 1; b = 20; }
+    ];
+  };
+
+  testCartesianProductOfTwoSetsWithOneEmpty = {
+    expr = cartesianProductOfSets { a = [ ]; b = [ 10 20 ]; };
+    expected = [ ];
+  };
+
+  testCartesianProductOfThreeSets = {
+    expr = cartesianProductOfSets {
+      a = [   1   2   3 ];
+      b = [  10  20  30 ];
+      c = [ 100 200 300 ];
+    };
+    expected = [
+      { a = 1; b = 10; c = 100; }
+      { a = 1; b = 10; c = 200; }
+      { a = 1; b = 10; c = 300; }
+
+      { a = 1; b = 20; c = 100; }
+      { a = 1; b = 20; c = 200; }
+      { a = 1; b = 20; c = 300; }
+
+      { a = 1; b = 30; c = 100; }
+      { a = 1; b = 30; c = 200; }
+      { a = 1; b = 30; c = 300; }
+
+      { a = 2; b = 10; c = 100; }
+      { a = 2; b = 10; c = 200; }
+      { a = 2; b = 10; c = 300; }
+
+      { a = 2; b = 20; c = 100; }
+      { a = 2; b = 20; c = 200; }
+      { a = 2; b = 20; c = 300; }
+
+      { a = 2; b = 30; c = 100; }
+      { a = 2; b = 30; c = 200; }
+      { a = 2; b = 30; c = 300; }
+
+      { a = 3; b = 10; c = 100; }
+      { a = 3; b = 10; c = 200; }
+      { a = 3; b = 10; c = 300; }
+
+      { a = 3; b = 20; c = 100; }
+      { a = 3; b = 20; c = 200; }
+      { a = 3; b = 20; c = 300; }
+
+      { a = 3; b = 30; c = 100; }
+      { a = 3; b = 30; c = 200; }
+      { a = 3; b = 30; c = 300; }
+    ];
+  };
 }
diff --git a/nixpkgs/lib/tests/modules.sh b/nixpkgs/lib/tests/modules.sh
index 309c5311361c..f843d303e440 100755
--- a/nixpkgs/lib/tests/modules.sh
+++ b/nixpkgs/lib/tests/modules.sh
@@ -262,6 +262,13 @@ checkConfigOutput true config.value.mkbefore ./types-anything/mk-mods.nix
 checkConfigOutput 1 config.value.nested.foo ./types-anything/mk-mods.nix
 checkConfigOutput baz config.value.nested.bar.baz ./types-anything/mk-mods.nix
 
+## types.functionTo
+checkConfigOutput "input is input" config.result ./functionTo/trivial.nix
+checkConfigOutput "a b" config.result ./functionTo/merging-list.nix
+checkConfigError 'A definition for option .fun.\[function body\]. is not of type .string.. Definition values:\n- In .*wrong-type.nix' config.result ./functionTo/wrong-type.nix
+checkConfigOutput "b a" config.result ./functionTo/list-order.nix
+checkConfigOutput "a c" config.result ./functionTo/merging-attrs.nix
+
 cat <<EOF
 ====== module tests ======
 $pass Pass
diff --git a/nixpkgs/lib/tests/modules/functionTo/list-order.nix b/nixpkgs/lib/tests/modules/functionTo/list-order.nix
new file mode 100644
index 000000000000..77a1a43a84f0
--- /dev/null
+++ b/nixpkgs/lib/tests/modules/functionTo/list-order.nix
@@ -0,0 +1,25 @@
+
+{ lib, config, ... }:
+let
+  inherit (lib) types;
+in {
+  options = {
+    fun = lib.mkOption {
+      type = types.functionTo (types.listOf types.str);
+    };
+
+    result = lib.mkOption {
+      type = types.str;
+      default = toString (config.fun {
+        a = "a";
+        b = "b";
+        c = "c";
+      });
+    };
+  };
+
+  config.fun = lib.mkMerge [
+    (input: lib.mkAfter [ input.a ])
+    (input: [ input.b ])
+  ];
+}
diff --git a/nixpkgs/lib/tests/modules/functionTo/merging-attrs.nix b/nixpkgs/lib/tests/modules/functionTo/merging-attrs.nix
new file mode 100644
index 000000000000..97c015f928ab
--- /dev/null
+++ b/nixpkgs/lib/tests/modules/functionTo/merging-attrs.nix
@@ -0,0 +1,27 @@
+{ lib, config, ... }:
+let
+  inherit (lib) types;
+in {
+  options = {
+    fun = lib.mkOption {
+      type = types.functionTo (types.attrsOf types.str);
+    };
+
+    result = lib.mkOption {
+      type = types.str;
+      default = toString (lib.attrValues (config.fun {
+        a = "a";
+        b = "b";
+        c = "c";
+      }));
+    };
+  };
+
+  config.fun = lib.mkMerge [
+    (input: { inherit (input) a; })
+    (input: { inherit (input) b; })
+    (input: {
+      b = lib.mkForce input.c;
+    })
+  ];
+}
diff --git a/nixpkgs/lib/tests/modules/functionTo/merging-list.nix b/nixpkgs/lib/tests/modules/functionTo/merging-list.nix
new file mode 100644
index 000000000000..15fcd2bdcc42
--- /dev/null
+++ b/nixpkgs/lib/tests/modules/functionTo/merging-list.nix
@@ -0,0 +1,24 @@
+{ lib, config, ... }:
+let
+  inherit (lib) types;
+in {
+  options = {
+    fun = lib.mkOption {
+      type = types.functionTo (types.listOf types.str);
+    };
+
+    result = lib.mkOption {
+      type = types.str;
+      default = toString (config.fun {
+        a = "a";
+        b = "b";
+        c = "c";
+      });
+    };
+  };
+
+  config.fun = lib.mkMerge [
+    (input: [ input.a ])
+    (input: [ input.b ])
+  ];
+}
diff --git a/nixpkgs/lib/tests/modules/functionTo/trivial.nix b/nixpkgs/lib/tests/modules/functionTo/trivial.nix
new file mode 100644
index 000000000000..0962a0cf893d
--- /dev/null
+++ b/nixpkgs/lib/tests/modules/functionTo/trivial.nix
@@ -0,0 +1,17 @@
+{ lib, config, ... }:
+let
+  inherit (lib) types;
+in {
+  options = {
+    fun = lib.mkOption {
+      type = types.functionTo types.str;
+    };
+
+    result = lib.mkOption {
+      type = types.str;
+      default = config.fun "input";
+    };
+  };
+
+  config.fun = input: "input is ${input}";
+}
diff --git a/nixpkgs/lib/tests/modules/functionTo/wrong-type.nix b/nixpkgs/lib/tests/modules/functionTo/wrong-type.nix
new file mode 100644
index 000000000000..fd65b75088da
--- /dev/null
+++ b/nixpkgs/lib/tests/modules/functionTo/wrong-type.nix
@@ -0,0 +1,18 @@
+
+{ lib, config, ... }:
+let
+  inherit (lib) types;
+in {
+  options = {
+    fun = lib.mkOption {
+      type = types.functionTo types.str;
+    };
+
+    result = lib.mkOption {
+      type = types.str;
+      default = config.fun 0;
+    };
+  };
+
+  config.fun = input: input + 1;
+}
diff --git a/nixpkgs/lib/tests/systems.nix b/nixpkgs/lib/tests/systems.nix
index eed7ee725bc4..c0800df25ed7 100644
--- a/nixpkgs/lib/tests/systems.nix
+++ b/nixpkgs/lib/tests/systems.nix
@@ -28,7 +28,7 @@ with lib.systems.doubles; lib.runTests {
   testredox = mseteq redox [ "x86_64-redox" ];
   testgnu = mseteq gnu (linux /* ++ kfreebsd ++ ... */);
   testillumos = mseteq illumos [ "x86_64-solaris" ];
-  testlinux = mseteq linux [ "aarch64-linux" "armv5tel-linux" "armv6l-linux" "armv7a-linux" "armv7l-linux" "i686-linux" "mipsel-linux" "riscv32-linux" "riscv64-linux" "x86_64-linux" "powerpc64le-linux" ];
+  testlinux = mseteq linux [ "aarch64-linux" "armv5tel-linux" "armv6l-linux" "armv7a-linux" "armv7l-linux" "i686-linux" "mipsel-linux" "riscv32-linux" "riscv64-linux" "x86_64-linux" "powerpc64-linux" "powerpc64le-linux" ];
   testnetbsd = mseteq netbsd [ "i686-netbsd" "x86_64-netbsd" ];
   testopenbsd = mseteq openbsd [ "i686-openbsd" "x86_64-openbsd" ];
   testwindows = mseteq windows [ "i686-cygwin" "x86_64-cygwin" "i686-windows" "x86_64-windows" ];
diff --git a/nixpkgs/lib/types.nix b/nixpkgs/lib/types.nix
index ee891f8231b6..d0a8e96149d7 100644
--- a/nixpkgs/lib/types.nix
+++ b/nixpkgs/lib/types.nix
@@ -453,6 +453,17 @@ rec {
       functor = (defaultFunctor name) // { wrapped = elemType; };
     };
 
+    functionTo = elemType: mkOptionType {
+      name = "functionTo";
+      description = "function that evaluates to a(n) ${elemType.name}";
+      check = isFunction;
+      merge = loc: defs:
+        fnArgs: (mergeDefinitions (loc ++ [ "[function body]" ]) elemType (map (fn: { inherit (fn) file; value = fn.value fnArgs; }) defs)).mergedValue;
+      getSubOptions = elemType.getSubOptions;
+      getSubModules = elemType.getSubModules;
+      substSubModules = m: functionTo (elemType.substSubModules m);
+    };
+
     # A submodule (like typed attribute set). See NixOS manual.
     submodule = modules: submoduleWith {
       shorthandOnlyDefinesConfig = true;