about summary refs log tree commit diff
path: root/nixpkgs/lib
diff options
context:
space:
mode:
authorAlyssa Ross <hi@alyssa.is>2022-02-22 10:43:06 +0000
committerAlyssa Ross <hi@alyssa.is>2022-03-11 16:17:56 +0000
commitca1aada113c0ebda1ab8667199f6453f8e01c4fc (patch)
tree55e402280096f62eb0bc8bcad5ce6050c5a0aec7 /nixpkgs/lib
parente4df5a52a6a6531f32626f57205356a773ac2975 (diff)
parent93883402a445ad467320925a0a5dbe43a949f25b (diff)
downloadnixlib-ca1aada113c0ebda1ab8667199f6453f8e01c4fc.tar
nixlib-ca1aada113c0ebda1ab8667199f6453f8e01c4fc.tar.gz
nixlib-ca1aada113c0ebda1ab8667199f6453f8e01c4fc.tar.bz2
nixlib-ca1aada113c0ebda1ab8667199f6453f8e01c4fc.tar.lz
nixlib-ca1aada113c0ebda1ab8667199f6453f8e01c4fc.tar.xz
nixlib-ca1aada113c0ebda1ab8667199f6453f8e01c4fc.tar.zst
nixlib-ca1aada113c0ebda1ab8667199f6453f8e01c4fc.zip
Merge commit '93883402a445ad467320925a0a5dbe43a949f25b'
Conflicts:
	nixpkgs/nixos/modules/programs/ssh.nix
	nixpkgs/pkgs/applications/networking/browsers/firefox/packages.nix
	nixpkgs/pkgs/data/fonts/noto-fonts/default.nix
	nixpkgs/pkgs/development/go-modules/generic/default.nix
	nixpkgs/pkgs/development/interpreters/ruby/default.nix
	nixpkgs/pkgs/development/libraries/mesa/default.nix
Diffstat (limited to 'nixpkgs/lib')
-rw-r--r--nixpkgs/lib/asserts.nix22
-rw-r--r--nixpkgs/lib/attrsets.nix36
-rw-r--r--nixpkgs/lib/default.nix12
-rw-r--r--nixpkgs/lib/licenses.nix20
-rw-r--r--nixpkgs/lib/meta.nix2
-rw-r--r--nixpkgs/lib/modules.nix27
-rw-r--r--nixpkgs/lib/options.nix58
-rw-r--r--nixpkgs/lib/sources.nix115
-rw-r--r--nixpkgs/lib/trivial.nix49
-rw-r--r--nixpkgs/lib/types.nix28
10 files changed, 259 insertions, 110 deletions
diff --git a/nixpkgs/lib/asserts.nix b/nixpkgs/lib/asserts.nix
index 8a5f1fb3feb7..9ae357cbc935 100644
--- a/nixpkgs/lib/asserts.nix
+++ b/nixpkgs/lib/asserts.nix
@@ -2,35 +2,33 @@
 
 rec {
 
-  /* Print a trace message if pred is false.
+  /* Throw if pred is false, else return pred.
      Intended to be used to augment asserts with helpful error messages.
 
      Example:
        assertMsg false "nope"
-       => false
-       stderr> trace: nope
+       stderr> error: nope
 
-       assert (assertMsg ("foo" == "bar") "foo is not bar, silly"); ""
-       stderr> trace: foo is not bar, silly
-       stderr> assert failed at …
+       assert assertMsg ("foo" == "bar") "foo is not bar, silly"; ""
+       stderr> error: foo is not bar, silly
 
      Type:
        assertMsg :: Bool -> String -> Bool
   */
   # TODO(Profpatsch): add tests that check stderr
   assertMsg = pred: msg:
-    if pred
-    then true
-    else builtins.trace msg false;
+    pred || builtins.throw msg;
 
   /* Specialized `assertMsg` for checking if val is one of the elements
      of a list. Useful for checking enums.
 
      Example:
-       let sslLibrary = "libressl"
+       let sslLibrary = "libressl";
        in assertOneOf "sslLibrary" sslLibrary [ "openssl" "bearssl" ]
-       => false
-       stderr> trace: sslLibrary must be one of "openssl", "bearssl", but is: "libressl"
+       stderr> error: sslLibrary must be one of [
+       stderr>   "openssl"
+       stderr>   "bearssl"
+       stderr> ], but is: "libressl"
 
      Type:
        assertOneOf :: String -> ComparableVal -> List ComparableVal -> Bool
diff --git a/nixpkgs/lib/attrsets.nix b/nixpkgs/lib/attrsets.nix
index 812521ce6d1c..a88947b45858 100644
--- a/nixpkgs/lib/attrsets.nix
+++ b/nixpkgs/lib/attrsets.nix
@@ -3,9 +3,9 @@
 
 let
   inherit (builtins) head tail length;
-  inherit (lib.trivial) and;
+  inherit (lib.trivial) id;
   inherit (lib.strings) concatStringsSep sanitizeDerivationName;
-  inherit (lib.lists) foldr foldl' concatMap concatLists elemAt;
+  inherit (lib.lists) foldr foldl' concatMap concatLists elemAt all;
 in
 
 rec {
@@ -73,9 +73,9 @@ rec {
        getAttrFromPath ["z" "z"] x
        => error: cannot find attribute `z.z'
   */
-  getAttrFromPath = attrPath: set:
+  getAttrFromPath = attrPath:
     let errorMsg = "cannot find attribute `" + concatStringsSep "." attrPath + "'";
-    in attrByPath attrPath (abort errorMsg) set;
+    in attrByPath attrPath (abort errorMsg);
 
 
   /* Return the specified attributes from a set.
@@ -154,12 +154,12 @@ rec {
        foldAttrs (n: a: [n] ++ a) [] [{ a = 2; } { a = 3; }]
        => { a = [ 2 3 ]; }
   */
-  foldAttrs = op: nul: list_of_attrs:
+  foldAttrs = op: nul:
     foldr (n: a:
         foldr (name: o:
           o // { ${name} = op n.${name} (a.${name} or nul); }
         ) a (attrNames n)
-    ) {} list_of_attrs;
+    ) {};
 
 
   /* Recursively collect sets that verify a given predicate named `pred'
@@ -276,7 +276,7 @@ rec {
 
 
   /* Like `mapAttrsRecursive', but it takes an additional predicate
-     function that tells it whether to recursive into an attribute
+     function that tells it whether to recurse into an attribute
      set.  If it returns false, `mapAttrsRecursiveCond' does not
      recurse, but does apply the map function.  If it returns true, it
      does recurse, and does not apply the map function.
@@ -295,14 +295,14 @@ rec {
   */
   mapAttrsRecursiveCond = cond: f: set:
     let
-      recurse = path: set:
+      recurse = path:
         let
           g =
             name: value:
             if isAttrs value && cond value
               then recurse (path ++ [name]) value
               else f (path ++ [name]) value;
-        in mapAttrs g set;
+        in mapAttrs g;
     in recurse [] set;
 
 
@@ -369,7 +369,7 @@ rec {
       value = f name (catAttrs name sets);
     }) names);
 
-  /* Implementation note: Common names  appear multiple times in the list of
+  /* Implementation 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.
@@ -378,7 +378,8 @@ rec {
        zipAttrsWith (name: values: values) [{a = "x";} {a = "y"; b = "z";}]
        => { a = ["x" "y"]; b = ["z"] }
   */
-  zipAttrsWith = f: sets: zipAttrsWithNames (concatMap attrNames sets) f sets;
+  zipAttrsWith =
+    builtins.zipAttrsWith or (f: sets: zipAttrsWithNames (concatMap attrNames sets) f sets);
   /* Like `zipAttrsWith' with `(name: values: values)' as the function.
 
     Example:
@@ -419,8 +420,8 @@ rec {
     let f = attrPath:
       zipAttrsWith (n: values:
         let here = attrPath ++ [n]; in
-        if tail values == []
-        || pred here (head (tail values)) (head values) then
+        if length values == 1
+        || pred here (elemAt values 1) (head values) then
           head values
         else
           f here values
@@ -446,10 +447,7 @@ rec {
        }
 
      */
-  recursiveUpdate = lhs: rhs:
-    recursiveUpdateUntil (path: lhs: rhs:
-      !(isAttrs lhs && isAttrs rhs)
-    ) lhs rhs;
+  recursiveUpdate = recursiveUpdateUntil (path: lhs: rhs: !(isAttrs lhs && isAttrs rhs));
 
   /* Returns true if the pattern is contained in the set. False otherwise.
 
@@ -458,8 +456,8 @@ rec {
        => true
    */
   matchAttrs = pattern: attrs: assert isAttrs pattern;
-    foldr and true (attrValues (zipAttrsWithNames (attrNames pattern) (n: values:
-      let pat = head values; val = head (tail values); in
+    all id (attrValues (zipAttrsWithNames (attrNames pattern) (n: values:
+      let pat = head values; val = elemAt values 1; in
       if length values == 1 then false
       else if isAttrs pat then isAttrs val && matchAttrs pat val
       else pat == val
diff --git a/nixpkgs/lib/default.nix b/nixpkgs/lib/default.nix
index 626a751cb10a..3e43733ad20f 100644
--- a/nixpkgs/lib/default.nix
+++ b/nixpkgs/lib/default.nix
@@ -66,7 +66,8 @@ let
       stringLength sub substring tail trace;
     inherit (self.trivial) id const pipe concat or and bitAnd bitOr bitXor
       bitNot boolToString mergeAttrs flip mapNullable inNixShell isFloat min max
-      importJSON importTOML warn warnIf info showWarnings nixpkgsVersion version
+      importJSON importTOML warn warnIf throwIfNot checkListOfEnum
+      info showWarnings nixpkgsVersion version
       mod compare splitByAndCompare functionArgs setFunctionArgs isFunction
       toHexString toBaseDigits;
     inherit (self.fixedPoints) fix fix' converge extends composeExtensions
@@ -110,8 +111,8 @@ let
       cleanSource sourceByRegex sourceFilesBySuffices
       commitIdFromGitRepo cleanSourceWith pathHasContext
       canCleanSource pathIsRegularFile pathIsGitRepo;
-    inherit (self.modules) evalModules unifyModuleSyntax
-      applyIfFunction mergeModules
+    inherit (self.modules) evalModules setDefaultModuleLocation
+      unifyModuleSyntax applyIfFunction mergeModules
       mergeModules' mergeOptionDecls evalOptionValue mergeDefinitions
       pushDownProperties dischargeProperties filterOverrides
       sortProperties fixupOptionType mkIf mkAssert mkMerge mkOverride
@@ -121,8 +122,9 @@ let
       mkRenamedOptionModule mkMergedOptionModule mkChangedOptionModule
       mkAliasOptionModule mkDerivedConfig doRename;
     inherit (self.options) isOption mkEnableOption mkSinkUndeclaredOptions
-      mergeDefaultOption mergeOneOption mergeEqualOption getValues
-      getFiles optionAttrSetToDocList optionAttrSetToDocList'
+      mergeDefaultOption mergeOneOption mergeEqualOption mergeUniqueOption
+      getValues getFiles
+      optionAttrSetToDocList optionAttrSetToDocList'
       scrubOptionValue literalExpression literalExample literalDocBook
       showOption showFiles unknownModule mkOption;
     inherit (self.types) isType setType defaultTypeMerge defaultFunctor
diff --git a/nixpkgs/lib/licenses.nix b/nixpkgs/lib/licenses.nix
index d305001a5c19..b9310ef6c5b8 100644
--- a/nixpkgs/lib/licenses.nix
+++ b/nixpkgs/lib/licenses.nix
@@ -67,6 +67,11 @@ in mkLicense lset) ({
     free = false;
   };
 
+  aom = {
+    fullName = "Alliance for Open Media Patent License 1.0";
+    url = "https://aomedia.org/license/patent-license/";
+  };
+
   apsl20 = {
     spdxId = "APSL-2.0";
     fullName = "Apple Public Source License 2.0";
@@ -460,6 +465,11 @@ in mkLicense lset) ({
     spdxId = "imagemagick";
   };
 
+  imlib2 = {
+    spdxId = "Imlib2";
+    fullName = "Imlib2 License";
+  };
+
   inria-compcert = {
     fullName  = "INRIA Non-Commercial License Agreement for the CompCert verified compiler";
     url       = "https://compcert.org/doc/LICENSE.txt";
@@ -586,6 +596,16 @@ in mkLicense lset) ({
     spdxId = "MIT";
     fullName = "MIT License";
   };
+  # https://spdx.org/licenses/MIT-feh.html
+  mit-feh = {
+    spdxId = "MIT-feh";
+    fullName = "feh License";
+  };
+
+  mitAdvertising = {
+    spdxId = "MIT-advertising";
+    fullName = "Enlightenment License (e16)";
+  };
 
   mpl10 = {
     spdxId = "MPL-1.0";
diff --git a/nixpkgs/lib/meta.nix b/nixpkgs/lib/meta.nix
index bc3387646f26..5b1f7ee5ff2d 100644
--- a/nixpkgs/lib/meta.nix
+++ b/nixpkgs/lib/meta.nix
@@ -78,7 +78,7 @@ rec {
 
        2. (modern) a pattern for the platform `parsed` field.
 
-     We can inject these into a patten for the whole of a structured platform,
+     We can inject these into a pattern for the whole of a structured platform,
      and then match that.
   */
   platformMatch = platform: elem: let
diff --git a/nixpkgs/lib/modules.nix b/nixpkgs/lib/modules.nix
index 573bf40e4b34..f1125aca0a35 100644
--- a/nixpkgs/lib/modules.nix
+++ b/nixpkgs/lib/modules.nix
@@ -37,6 +37,7 @@ let
     toList
     types
     warnIf
+    zipAttrsWith
     ;
   inherit (lib.options)
     isOption
@@ -333,6 +334,10 @@ rec {
     in modulesPath: initialModules: args:
       filterModules modulesPath (collectStructuredModules unknownModule "" initialModules args);
 
+  /* Wrap a module with a default location for reporting errors. */
+  setDefaultModuleLocation = file: m:
+    { _file = file; imports = [ m ]; };
+
   /* Massage a module into canonical form, that is, a set consisting
      of ‘options’, ‘config’ and ‘imports’ attributes. */
   unifyModuleSyntax = file: key: m:
@@ -442,10 +447,11 @@ rec {
         }
       */
       byName = attr: f: modules:
-        foldl' (acc: module:
-              if !(builtins.isAttrs module.${attr}) then
+        zipAttrsWith (n: concatLists)
+          (map (module: let subtree = module.${attr}; in
+              if !(builtins.isAttrs subtree) then
                 throw ''
-                  You're trying to declare a value of type `${builtins.typeOf module.${attr}}'
+                  You're trying to declare a value of type `${builtins.typeOf subtree}'
                   rather than an attribute-set for the option
                   `${builtins.concatStringsSep "." prefix}'!
 
@@ -454,11 +460,8 @@ rec {
                   this option by e.g. referring to `man 5 configuration.nix'!
                 ''
               else
-                acc // (mapAttrs (n: v:
-                                   (acc.${n} or []) ++ f module v
-                                 ) module.${attr}
-                       )
-               ) {} modules;
+                mapAttrs (n: f module) subtree
+              ) modules);
       # an attrset 'name' => list of submodules that declare ‘name’.
       declsByName = byName "options" (module: option:
           [{ inherit (module) _file; options = option; }]
@@ -535,11 +538,9 @@ rec {
      correspond to the definition of 'loc' in 'opt.file'. */
   mergeOptionDecls =
    let
-    packSubmodule = file: m:
-      { _file = file; imports = [ m ]; };
     coerceOption = file: opt:
-      if isFunction opt then packSubmodule file opt
-      else packSubmodule file { options = opt; };
+      if isFunction opt then setDefaultModuleLocation file opt
+      else setDefaultModuleLocation file { options = opt; };
    in loc: opts:
     foldl' (res: opt:
       let t  = res.type;
@@ -569,7 +570,7 @@ rec {
 
           getSubModules = opt.options.type.getSubModules or null;
           submodules =
-            if getSubModules != null then map (packSubmodule opt._file) getSubModules ++ res.options
+            if getSubModules != null then map (setDefaultModuleLocation opt._file) getSubModules ++ res.options
             else if opt.options ? options then map (coerceOption opt._file) options' ++ res.options
             else res.options;
         in opt.options // res //
diff --git a/nixpkgs/lib/options.nix b/nixpkgs/lib/options.nix
index 5d52f065af08..627aac24d2fb 100644
--- a/nixpkgs/lib/options.nix
+++ b/nixpkgs/lib/options.nix
@@ -26,6 +26,7 @@ let
     take
     ;
   inherit (lib.attrsets)
+    attrByPath
     optionalAttrs
     ;
   inherit (lib.strings)
@@ -99,6 +100,49 @@ rec {
     type = lib.types.bool;
   };
 
+  /* Creates an Option attribute set for an option that specifies the
+     package a module should use for some purpose.
+
+     Type: mkPackageOption :: pkgs -> string -> { default :: [string], example :: null | string | [string] } -> option
+
+     The package is specified as a list of strings representing its attribute path in nixpkgs.
+
+     Because of this, you need to pass nixpkgs itself as the first argument.
+
+     The second argument is the name of the option, used in the description "The <name> package to use.".
+
+     You can also pass an example value, either a literal string or a package's attribute path.
+
+     You can omit the default path if the name of the option is also attribute path in nixpkgs.
+
+     Example:
+       mkPackageOption pkgs "hello" { }
+       => { _type = "option"; default = «derivation /nix/store/3r2vg51hlxj3cx5vscp0vkv60bqxkaq0-hello-2.10.drv»; defaultText = { ... }; description = "The hello package to use."; type = { ... }; }
+
+     Example:
+       mkPackageOption pkgs "GHC" {
+         default = [ "ghc" ];
+         example = "pkgs.haskell.package.ghc921.ghc.withPackages (hkgs: [ hkgs.primes ])";
+       }
+       => { _type = "option"; default = «derivation /nix/store/jxx55cxsjrf8kyh3fp2ya17q99w7541r-ghc-8.10.7.drv»; defaultText = { ... }; description = "The GHC package to use."; example = { ... }; type = { ... }; }
+  */
+  mkPackageOption =
+    # Package set (a specific version of nixpkgs)
+    pkgs:
+      # Name for the package, shown in option description
+      name:
+      { default ? [ name ], example ? null }:
+      let default' = if !isList default then [ default ] else default;
+      in mkOption {
+        type = lib.types.package;
+        description = "The ${name} package to use.";
+        default = attrByPath default'
+          (throw "${concatStringsSep "." default'} cannot be found in pkgs") pkgs;
+        defaultText = literalExpression ("pkgs." + concatStringsSep "." default');
+        ${if example != null then "example" else null} = literalExpression
+          (if isList example then "pkgs." + concatStringsSep "." example else example);
+      };
+
   /* This option accepts anything, but it does not produce any result.
 
      This is useful for sharing a module across different module sets
@@ -128,11 +172,13 @@ rec {
     else if all isInt list && all (x: x == head list) list then head list
     else throw "Cannot merge definitions of `${showOption loc}'. Definition values:${showDefs defs}";
 
-  mergeOneOption = loc: defs:
-    if defs == [] then abort "This case should never happen."
-    else if length defs != 1 then
-      throw "The unique option `${showOption loc}' is defined multiple times. Definition values:${showDefs defs}"
-    else (head defs).value;
+  mergeOneOption = mergeUniqueOption { message = ""; };
+
+  mergeUniqueOption = { message }: loc: defs:
+    if length defs == 1
+    then (head defs).value
+    else assert length defs > 1;
+      throw "The option `${showOption loc}' is defined multiple times.\n${message}\nDefinition values:${showDefs defs}";
 
   /* "Merge" option definitions by checking that they all have the same value. */
   mergeEqualOption = loc: defs:
@@ -177,7 +223,7 @@ rec {
         docOption = rec {
           loc = opt.loc;
           name = showOption opt.loc;
-          description = opt.description or (lib.warn "Option `${name}' has no description." "This option has no description.");
+          description = opt.description or null;
           declarations = filter (x: x != unknownModule) opt.declarations;
           internal = opt.internal or false;
           visible =
diff --git a/nixpkgs/lib/sources.nix b/nixpkgs/lib/sources.nix
index ae2df7235213..343449d9a603 100644
--- a/nixpkgs/lib/sources.nix
+++ b/nixpkgs/lib/sources.nix
@@ -20,17 +20,26 @@ let
     readFile
     ;
 
-  # Returns the type of a path: regular (for file), symlink, or directory
-  pathType = p: getAttr (baseNameOf p) (readDir (dirOf p));
+  /*
+    Returns the type of a path: regular (for file), symlink, or directory.
+  */
+  pathType = path: getAttr (baseNameOf path) (readDir (dirOf path));
 
-  # Returns true if the path exists and is a directory, false otherwise
-  pathIsDirectory = p: if pathExists p then (pathType p) == "directory" else false;
+  /*
+    Returns true if the path exists and is a directory, false otherwise.
+  */
+  pathIsDirectory = path: if pathExists path then (pathType path) == "directory" else false;
 
-  # Returns true if the path exists and is a regular file, false otherwise
-  pathIsRegularFile = p: if pathExists p then (pathType p) == "regular" else false;
+  /*
+    Returns true if the path exists and is a regular file, false otherwise.
+  */
+  pathIsRegularFile = path: if pathExists path then (pathType path) == "regular" else false;
 
-  # Bring in a path as a source, filtering out all Subversion and CVS
-  # directories, as well as backup files (*~).
+  /*
+    A basic filter for `cleanSourceWith` that removes
+    directories of version control system, backup files (*~)
+    and some generated files.
+  */
   cleanSourceFilter = name: type: let baseName = baseNameOf (toString name); in ! (
     # Filter out version control software files/directories
     (baseName == ".git" || type == "directory" && (baseName == ".svn" || baseName == "CVS" || baseName == ".hg")) ||
@@ -48,43 +57,48 @@ let
     (type == "unknown")
   );
 
-  # Filters a source tree removing version control files and directories using cleanSourceWith
-  #
-  # Example:
-  #          cleanSource ./.
+  /*
+    Filters a source tree removing version control files and directories using cleanSourceFilter.
+
+    Example:
+             cleanSource ./.
+  */
   cleanSource = src: cleanSourceWith { filter = cleanSourceFilter; inherit src; };
 
-  # Like `builtins.filterSource`, except it will compose with itself,
-  # allowing you to chain multiple calls together without any
-  # intermediate copies being put in the nix store.
-  #
-  #     lib.cleanSourceWith {
-  #       filter = f;
-  #       src = lib.cleanSourceWith {
-  #         filter = g;
-  #         src = ./.;
-  #       };
-  #     }
-  #     # Succeeds!
-  #
-  #     builtins.filterSource f (builtins.filterSource g ./.)
-  #     # Fails!
-  #
-  # Parameters:
-  #
-  #   src:      A path or cleanSourceWith result to filter and/or rename.
-  #
-  #   filter:   A function (path -> type -> bool)
-  #             Optional with default value: constant true (include everything)
-  #             The function will be combined with the && operator such
-  #             that src.filter is called lazily.
-  #             For implementing a filter, see
-  #             https://nixos.org/nix/manual/#builtin-filterSource
-  #
-  #   name:     Optional name to use as part of the store path.
-  #             This defaults to `src.name` or otherwise `"source"`.
-  #
-  cleanSourceWith = { filter ? _path: _type: true, src, name ? null }:
+  /*
+    Like `builtins.filterSource`, except it will compose with itself,
+    allowing you to chain multiple calls together without any
+    intermediate copies being put in the nix store.
+
+    Example:
+        lib.cleanSourceWith {
+          filter = f;
+          src = lib.cleanSourceWith {
+            filter = g;
+            src = ./.;
+          };
+        }
+        # Succeeds!
+
+        builtins.filterSource f (builtins.filterSource g ./.)
+        # Fails!
+
+  */
+  cleanSourceWith =
+    {
+      # A path or cleanSourceWith result to filter and/or rename.
+      src,
+      # Optional with default value: constant true (include everything)
+      # The function will be combined with the && operator such
+      # that src.filter is called lazily.
+      # For implementing a filter, see
+      # https://nixos.org/nix/manual/#builtin-filterSource
+      # Type: A function (path -> type -> bool)
+      filter ? _path: _type: true,
+      # Optional name to use as part of the store path.
+      # This defaults to `src.name` or otherwise `"source"`.
+      name ? null
+    }:
     let
       orig = toSourceAttributes src;
     in fromSourceAttributes {
@@ -116,9 +130,11 @@ let
         satisfiesSubpathInvariant = src ? satisfiesSubpathInvariant && src.satisfiesSubpathInvariant;
       };
 
-  # Filter sources by a list of regular expressions.
-  #
-  # E.g. `src = sourceByRegex ./my-subproject [".*\.py$" "^database.sql$"]`
+  /*
+    Filter sources by a list of regular expressions.
+
+    Example: src = sourceByRegex ./my-subproject [".*\.py$" "^database.sql$"]
+  */
   sourceByRegex = src: regexes:
     let
       isFiltered = src ? _isLibCleanSourceWith;
@@ -153,8 +169,11 @@ let
 
   pathIsGitRepo = path: (tryEval (commitIdFromGitRepo path)).success;
 
-  # Get the commit id of a git repo
-  # Example: commitIdFromGitRepo <nixpkgs/.git>
+  /*
+    Get the commit id of a git repo.
+
+    Example: commitIdFromGitRepo <nixpkgs/.git>
+  */
   commitIdFromGitRepo =
     let readCommitFromFile = file: path:
         let fileName       = toString path + "/" + file;
diff --git a/nixpkgs/lib/trivial.nix b/nixpkgs/lib/trivial.nix
index 33b553ac4191..c68bac902e91 100644
--- a/nixpkgs/lib/trivial.nix
+++ b/nixpkgs/lib/trivial.nix
@@ -61,11 +61,11 @@ rec {
   pipe = val: functions:
     let reverseApply = x: f: f x;
     in builtins.foldl' reverseApply val functions;
-  /* note please don’t add a function like `compose = flip pipe`.
-     This would confuse users, because the order of the functions
-     in the list is not clear. With pipe, it’s obvious that it
-     goes first-to-last. With `compose`, not so much.
-  */
+
+  # note please don’t add a function like `compose = flip pipe`.
+  # This would confuse users, because the order of the functions
+  # in the list is not clear. With pipe, it’s obvious that it
+  # goes first-to-last. With `compose`, not so much.
 
   ## Named versions corresponding to some builtin operators.
 
@@ -325,6 +325,45 @@ rec {
   */
   warnIf = cond: msg: if cond then warn msg else id;
 
+  /*
+    Like the `assert b; e` expression, but with a custom error message and
+    without the semicolon.
+
+    If true, return the identity function, `r: r`.
+
+    If false, throw the error message.
+
+    Calls can be juxtaposed using function application, as `(r: r) a = a`, so
+    `(r: r) (r: r) a = a`, and so forth.
+
+    Type: bool -> string -> a -> a
+
+    Example:
+
+        throwIfNot (lib.isList overlays) "The overlays argument to nixpkgs must be a list."
+        lib.foldr (x: throwIfNot (lib.isFunction x) "All overlays passed to nixpkgs must be functions.") (r: r) overlays
+        pkgs
+
+  */
+  throwIfNot = cond: msg: if cond then x: x else throw msg;
+
+  /* Check if the elements in a list are valid values from a enum, returning the identity function, or throwing an error message otherwise.
+
+     Example:
+       let colorVariants = ["bright" "dark" "black"]
+       in checkListOfEnum "color variants" [ "standard" "light" "dark" ] colorVariants;
+       =>
+       error: color variants: bright, black unexpected; valid ones: standard, light, dark
+
+     Type: String -> List ComparableVal -> List ComparableVal -> a -> a
+  */
+  checkListOfEnum = msg: valid: given:
+    let
+      unexpected = lib.subtractLists valid given;
+    in
+      lib.throwIfNot (unexpected == [])
+        "${msg}: ${builtins.concatStringsSep ", " (builtins.map builtins.toString unexpected)} unexpected; valid ones: ${builtins.concatStringsSep ", " (builtins.map builtins.toString valid)}";
+
   info = msg: builtins.trace "INFO: ${msg}";
 
   showWarnings = warnings: res: lib.foldr (w: x: warn w x) res warnings;
diff --git a/nixpkgs/lib/types.nix b/nixpkgs/lib/types.nix
index 244cbb6b5354..f2f9b2bca985 100644
--- a/nixpkgs/lib/types.nix
+++ b/nixpkgs/lib/types.nix
@@ -32,7 +32,6 @@ let
     last
     length
     tail
-    unique
     ;
   inherit (lib.attrsets)
     attrNames
@@ -48,6 +47,7 @@ let
     mergeDefaultOption
     mergeEqualOption
     mergeOneOption
+    mergeUniqueOption
     showFiles
     showOption
     ;
@@ -300,6 +300,19 @@ rec {
       inherit (str) merge;
     };
 
+    # Allow a newline character at the end and trim it in the merge function.
+    singleLineStr =
+      let
+        inherit (strMatching "[^\n\r]*\n?") check merge;
+      in
+      mkOptionType {
+        name = "singleLineStr";
+        description = "(optionally newline-terminated) single-line string";
+        inherit check;
+        merge = loc: defs:
+          lib.removeSuffix "\n" (merge loc defs);
+      };
+
     strMatching = pattern: mkOptionType {
       name = "strMatching ${escapeNixString pattern}";
       description = "string matching the pattern ${pattern}";
@@ -457,6 +470,18 @@ rec {
       nestedTypes.elemType = elemType;
     };
 
+    unique = { message }: type: mkOptionType rec {
+      name = "unique";
+      inherit (type) description check;
+      merge = mergeUniqueOption { inherit message; };
+      emptyValue = type.emptyValue;
+      getSubOptions = type.getSubOptions;
+      getSubModules = type.getSubModules;
+      substSubModules = m: uniq (type.substSubModules m);
+      functor = (defaultFunctor name) // { wrapped = type; };
+      nestedTypes.elemType = type;
+    };
+
     # Null or value of ...
     nullOr = elemType: mkOptionType rec {
       name = "nullOr";
@@ -586,6 +611,7 @@ rec {
     # A value from a set of allowed ones.
     enum = values:
       let
+        inherit (lib.lists) unique;
         show = v:
                if builtins.isString v then ''"${v}"''
           else if builtins.isInt v then builtins.toString v