summary refs log tree commit diff
path: root/lib
diff options
context:
space:
mode:
Diffstat (limited to 'lib')
-rw-r--r--lib/asserts.nix22
-rw-r--r--lib/attrsets.nix2
-rw-r--r--lib/default.nix9
-rw-r--r--lib/licenses.nix15
-rw-r--r--lib/meta.nix2
-rw-r--r--lib/modules.nix12
-rw-r--r--lib/options.nix56
-rw-r--r--lib/sources.nix115
-rw-r--r--lib/trivial.nix10
-rw-r--r--lib/types.nix28
10 files changed, 189 insertions, 82 deletions
diff --git a/lib/asserts.nix b/lib/asserts.nix
index 8a5f1fb3feb..9ae357cbc93 100644
--- a/lib/asserts.nix
+++ b/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/lib/attrsets.nix b/lib/attrsets.nix
index dafdd98f498..a88947b4585 100644
--- a/lib/attrsets.nix
+++ b/lib/attrsets.nix
@@ -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.
diff --git a/lib/default.nix b/lib/default.nix
index 2dfe62e82a8..3e43733ad20 100644
--- a/lib/default.nix
+++ b/lib/default.nix
@@ -111,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
@@ -122,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/lib/licenses.nix b/lib/licenses.nix
index ddd5f8daac4..b9310ef6c5b 100644
--- a/lib/licenses.nix
+++ b/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";
@@ -592,6 +602,11 @@ in mkLicense lset) ({
     fullName = "feh License";
   };
 
+  mitAdvertising = {
+    spdxId = "MIT-advertising";
+    fullName = "Enlightenment License (e16)";
+  };
+
   mpl10 = {
     spdxId = "MPL-1.0";
     fullName = "Mozilla Public License 1.0";
diff --git a/lib/meta.nix b/lib/meta.nix
index bc3387646f2..5b1f7ee5ff2 100644
--- a/lib/meta.nix
+++ b/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/lib/modules.nix b/lib/modules.nix
index c68bbfcaa3e..f1125aca0a3 100644
--- a/lib/modules.nix
+++ b/lib/modules.nix
@@ -334,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:
@@ -534,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;
@@ -568,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/lib/options.nix b/lib/options.nix
index 53001a3113f..627aac24d2f 100644
--- a/lib/options.nix
+++ b/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:
diff --git a/lib/sources.nix b/lib/sources.nix
index ae2df723521..343449d9a60 100644
--- a/lib/sources.nix
+++ b/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/lib/trivial.nix b/lib/trivial.nix
index 575aaf6a7ad..c68bac902e9 100644
--- a/lib/trivial.nix
+++ b/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.
 
diff --git a/lib/types.nix b/lib/types.nix
index 244cbb6b535..f2f9b2bca98 100644
--- a/lib/types.nix
+++ b/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