about summary refs log tree commit diff
path: root/lib
diff options
context:
space:
mode:
Diffstat (limited to 'lib')
-rw-r--r--lib/customisation.nix6
-rw-r--r--lib/fixed-points.nix159
-rw-r--r--lib/licenses.nix9
-rw-r--r--lib/systems/inspect.nix6
-rw-r--r--lib/tests/misc.nix12
-rw-r--r--lib/types.nix22
6 files changed, 185 insertions, 29 deletions
diff --git a/lib/customisation.nix b/lib/customisation.nix
index c233744e07ca..0b5cad71fddf 100644
--- a/lib/customisation.nix
+++ b/lib/customisation.nix
@@ -203,7 +203,11 @@ rec {
 
     in if missingArgs == {}
        then makeOverridable f allArgs
-       else throw "lib.customisation.callPackageWith: ${error}";
+       # This needs to be an abort so it can't be caught with `builtins.tryEval`,
+       # which is used by nix-env and ofborg to filter out packages that don't evaluate.
+       # This way we're forced to fix such errors in Nixpkgs,
+       # which is especially relevant with allowAliases = false
+       else abort "lib.customisation.callPackageWith: ${error}";
 
 
   /* Like callPackage, but for a function that returns an attribute
diff --git a/lib/fixed-points.nix b/lib/fixed-points.nix
index 3b5fdc9e8ea1..3370b55a4ab9 100644
--- a/lib/fixed-points.nix
+++ b/lib/fixed-points.nix
@@ -103,42 +103,155 @@ rec {
       else converge f x';
 
   /*
-    Modify the contents of an explicitly recursive attribute set in a way that
-    honors `self`-references. This is accomplished with a function
+    Extend a function using an overlay.
+
+    Overlays allow modifying and extending fixed-point functions, specifically ones returning attribute sets.
+    A fixed-point function is a function which is intended to be evaluated by passing the result of itself as the argument.
+    This is possible due to Nix's lazy evaluation.
+
+
+    A fixed-point function returning an attribute set has the form
+
+    ```nix
+    final: { # attributes }
+    ```
+
+    where `final` refers to the lazily evaluated attribute set returned by the fixed-point function.
+
+    An overlay to such a fixed-point function has the form
 
     ```nix
-    g = self: super: { foo = super.foo + " + "; }
+    final: prev: { # attributes }
+    ```
+
+    where `prev` refers to the result of the original function to `final`, and `final` is the result of the composition of the overlay and the original function.
+
+    Applying an overlay is done with `extends`:
+
+    ```nix
+    let
+      f = final: { # attributes };
+      overlay = final: prev: { # attributes };
+    in extends overlay f;
+    ```
+
+    To get the value of `final`, use `lib.fix`:
+
+    ```nix
+    let
+      f = final: { # attributes };
+      overlay = final: prev: { # attributes };
+      g = extends overlay f;
+    in fix g
+    ```
+
+    :::{.example}
+
+    # Extend a fixed-point function with an overlay
+
+    Define a fixed-point function `f` that expects its own output as the argument `final`:
+
+    ```nix-repl
+    f = final: {
+      # Constant value a
+      a = 1;
+
+      # b depends on the final value of a, available as final.a
+      b = final.a + 2;
+    }
+    ```
+
+    Evaluate this using [`lib.fix`](#function-library-lib.fixedPoints.fix) to get the final result:
+
+    ```nix-repl
+    fix f
+    => { a = 1; b = 3; }
     ```
 
-    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:
+    An overlay represents a modification or extension of such a fixed-point function.
+    Here's an example of an overlay:
 
+    ```nix-repl
+    overlay = final: prev: {
+      # Modify the previous value of a, available as prev.a
+      a = prev.a + 10;
+
+      # Extend the attribute set with c, letting it depend on the final values of a and b
+      c = final.a + final.b;
+    }
     ```
-    nix-repl> fix (extends g f)
-    { bar = "bar"; foo = "foo + "; foobar = "foo + bar"; }
+
+    Use `extends overlay f` to apply the overlay to the fixed-point function `f`.
+    This produces a new fixed-point function `g` with the combined behavior of `f` and `overlay`:
+
+    ```nix-repl
+    g = extends overlay f
     ```
 
-    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.
+    The result is a function, so we can't print it directly, but it's the same as:
+
+    ```nix-repl
+    g' = final: {
+      # The constant from f, but changed with the overlay
+      a = 1 + 10;
 
-    To get a better understanding how `extends` turns a function with a fix
-    point (the package set we start with) into a new function with a different fix
-    point (the desired packages set) lets just see, how `extends g f`
-    unfolds with `g` and `f` defined above:
+      # Unchanged from f
+      b = final.a + 2;
 
+      # Extended in the overlay
+      c = final.a + final.b;
+    }
     ```
-    extends g f = self: let super = f self; in super // g self super;
-                = self: let super = { foo = "foo"; bar = "bar"; foobar = self.foo + self.bar; }; in super // g self super
-                = self: { foo = "foo"; bar = "bar"; foobar = self.foo + self.bar; } // g self { foo = "foo"; bar = "bar"; foobar = self.foo + self.bar; }
-                = self: { foo = "foo"; bar = "bar"; foobar = self.foo + self.bar; } // { foo = "foo" + " + "; }
-                = self: { foo = "foo + "; bar = "bar"; foobar = self.foo + self.bar; }
+
+    Evaluate this using [`lib.fix`](#function-library-lib.fixedPoints.fix) again to get the final result:
+
+    ```nix-repl
+    fix g
+    => { a = 11; b = 13; c = 24; }
     ```
+    :::
+
+    Type:
+      extends :: (Attrs -> Attrs -> Attrs) # The overlay to apply to the fixed-point function
+              -> (Attrs -> Attrs) # A fixed-point function
+              -> (Attrs -> Attrs) # The resulting fixed-point function
+
+    Example:
+      f = final: { a = 1; b = final.a + 2; }
+
+      fix f
+      => { a = 1; b = 3; }
+
+      fix (extends (final: prev: { a = prev.a + 10; }) f)
+      => { a = 11; b = 13; }
+
+      fix (extends (final: prev: { b = final.a + 5; }) f)
+      => { a = 1; b = 6; }
+
+      fix (extends (final: prev: { c = final.a + final.b; }) f)
+      => { a = 1; b = 3; c = 4; }
+
+    :::{.note}
+    The argument to the given fixed-point function after applying an overlay will *not* refer to its own return value, but rather to the value after evaluating the overlay function.
+
+    The given fixed-point function is called with a separate argument than if it was evaluated with `lib.fix`.
+    The new argument
+    :::
   */
-  extends = f: rattrs: self: let super = rattrs self; in super // f self super;
+  extends =
+    # The overlay to apply to the fixed-point function
+    overlay:
+    # The fixed-point function
+    f:
+    # Wrap with parenthesis to prevent nixdoc from rendering the `final` argument in the documentation
+    # The result should be thought of as a function, the argument of that function is not an argument to `extends` itself
+    (
+      final:
+      let
+        prev = f final;
+      in
+      prev // overlay final prev
+    );
 
   /*
     Compose two extending functions of the type expected by 'extends'
diff --git a/lib/licenses.nix b/lib/licenses.nix
index baf92007123d..8dc01e560746 100644
--- a/lib/licenses.nix
+++ b/lib/licenses.nix
@@ -104,6 +104,7 @@ in mkLicense lset) ({
   };
 
   arphicpl = {
+    spdxId = "Arphic-1999";
     fullName = "Arphic Public License";
     url = "https://www.freedesktop.org/wiki/Arphic_Public_License/";
   };
@@ -236,6 +237,7 @@ in mkLicense lset) ({
   };
 
   cal10 = {
+    spdxId = "CAL-1.0";
     fullName = "Cryptographic Autonomy License version 1.0 (CAL-1.0)";
     url = "https://opensource.org/licenses/CAL-1.0";
   };
@@ -429,6 +431,7 @@ in mkLicense lset) ({
   };
 
   elastic20 = {
+    spdxId = "Elastic-2.0";
     fullName = "Elastic License 2.0";
     url = "https://github.com/elastic/elasticsearch/blob/main/licenses/ELASTIC-LICENSE-2.0.txt";
     free = false;
@@ -598,6 +601,7 @@ in mkLicense lset) ({
 
   # Intel's license, seems free
   iasl = {
+    spdxId = "Intel-ACPI";
     fullName = "iASL";
     url = "https://old.calculate-linux.org/packages/licenses/iASL";
   };
@@ -609,7 +613,7 @@ in mkLicense lset) ({
 
   imagemagick = {
     fullName = "ImageMagick License";
-    spdxId = "imagemagick";
+    spdxId = "ImageMagick";
   };
 
   imlib2 = {
@@ -803,6 +807,7 @@ in mkLicense lset) ({
   };
 
   miros = {
+    spdxId = "MirOS";
     fullName = "MirOS License";
     url = "https://opensource.org/licenses/MirOS";
   };
@@ -1138,6 +1143,7 @@ in mkLicense lset) ({
   };
 
   upl = {
+    spdxId = "UPL-1.0";
     fullName = "Universal Permissive License";
     url = "https://oss.oracle.com/licenses/upl/";
   };
@@ -1194,6 +1200,7 @@ in mkLicense lset) ({
   };
 
   xfig = {
+    spdxId = "Xfig";
     fullName = "xfig";
     url = "https://mcj.sourceforge.net/authors.html#xfig";
   };
diff --git a/lib/systems/inspect.nix b/lib/systems/inspect.nix
index 073df78797c7..38ca9967cdde 100644
--- a/lib/systems/inspect.nix
+++ b/lib/systems/inspect.nix
@@ -62,7 +62,8 @@ rec {
 
     is32bit        = { cpu = { bits = 32; }; };
     is64bit        = { cpu = { bits = 64; }; };
-    isILP32        = map (a: { abi = { abi = a; }; }) [ "n32" "ilp32" "x32" ];
+    isILP32        = [ { cpu = { family = "wasm"; bits = 32; }; } ] ++
+                     map (a: { abi = { abi = a; }; }) [ "n32" "ilp32" "x32" ];
     isBigEndian    = { cpu = { significantByte = significantBytes.bigEndian; }; };
     isLittleEndian = { cpu = { significantByte = significantBytes.littleEndian; }; };
 
@@ -98,6 +99,9 @@ rec {
       { cpu = { family = "riscv"; }; }
       { cpu = { family = "x86"; }; }
     ];
+
+    isElf          = { kernel.execFormat = execFormats.elf; };
+    isMacho        = { kernel.execFormat = execFormats.macho; };
   };
 
   # given two patterns, return a pattern which is their logical AND.
diff --git a/lib/tests/misc.nix b/lib/tests/misc.nix
index cf7fa9f2e284..3059878ba069 100644
--- a/lib/tests/misc.nix
+++ b/lib/tests/misc.nix
@@ -1959,6 +1959,18 @@ runTests {
     expr = (with types; int).description;
     expected = "signed integer";
   };
+  testTypeDescriptionIntsPositive = {
+    expr = (with types; ints.positive).description;
+    expected = "positive integer, meaning >0";
+  };
+  testTypeDescriptionIntsPositiveOrEnumAuto = {
+    expr = (with types; either ints.positive (enum ["auto"])).description;
+    expected = ''positive integer, meaning >0, or value "auto" (singular enum)'';
+  };
+  testTypeDescriptionListOfPositive = {
+    expr = (with types; listOf ints.positive).description;
+    expected = "list of (positive integer, meaning >0)";
+  };
   testTypeDescriptionListOfInt = {
     expr = (with types; listOf int).description;
     expected = "list of signed integer";
diff --git a/lib/types.nix b/lib/types.nix
index 4378568c141f..cea63c598321 100644
--- a/lib/types.nix
+++ b/lib/types.nix
@@ -113,9 +113,14 @@ rec {
     , # Description of the type, defined recursively by embedding the wrapped type if any.
       description ? null
       # A hint for whether or not this description needs parentheses. Possible values:
-      #  - "noun": a simple noun phrase such as "positive integer"
-      #  - "conjunction": a phrase with a potentially ambiguous "or" connective.
+      #  - "noun": a noun phrase
+      #    Example description: "positive integer",
+      #  - "conjunction": a phrase with a potentially ambiguous "or" connective
+      #    Example description: "int or string"
       #  - "composite": a phrase with an "of" connective
+      #    Example description: "list of string"
+      #  - "nonRestrictiveClause": a noun followed by a comma and a clause
+      #    Example description: "positive integer, meaning >0"
       # See the `optionDescriptionPhrase` function.
     , descriptionClass ? null
     , # DO NOT USE WITHOUT KNOWING WHAT YOU ARE DOING!
@@ -338,10 +343,12 @@ rec {
         unsigned = addCheck types.int (x: x >= 0) // {
           name = "unsignedInt";
           description = "unsigned integer, meaning >=0";
+          descriptionClass = "nonRestrictiveClause";
         };
         positive = addCheck types.int (x: x > 0) // {
           name = "positiveInt";
           description = "positive integer, meaning >0";
+          descriptionClass = "nonRestrictiveClause";
         };
         u8 = unsign 8 256;
         u16 = unsign 16 65536;
@@ -383,10 +390,12 @@ rec {
       nonnegative = addCheck number (x: x >= 0) // {
         name = "numberNonnegative";
         description = "nonnegative integer or floating point number, meaning >=0";
+        descriptionClass = "nonRestrictiveClause";
       };
       positive = addCheck number (x: x > 0) // {
         name = "numberPositive";
         description = "positive integer or floating point number, meaning >0";
+        descriptionClass = "nonRestrictiveClause";
       };
     };
 
@@ -463,6 +472,7 @@ rec {
     passwdEntry = entryType: addCheck entryType (str: !(hasInfix ":" str || hasInfix "\n" str)) // {
       name = "passwdEntry ${entryType.name}";
       description = "${optionDescriptionPhrase (class: class == "noun") entryType}, not containing newlines or colons";
+      descriptionClass = "nonRestrictiveClause";
     };
 
     attrs = mkOptionType {
@@ -870,7 +880,13 @@ rec {
     # Either value of type `t1` or `t2`.
     either = t1: t2: mkOptionType rec {
       name = "either";
-      description = "${optionDescriptionPhrase (class: class == "noun" || class == "conjunction") t1} or ${optionDescriptionPhrase (class: class == "noun" || class == "conjunction" || class == "composite") t2}";
+      description =
+        if t1.descriptionClass or null == "nonRestrictiveClause"
+        then
+          # Plain, but add comma
+          "${t1.description}, or ${optionDescriptionPhrase (class: class == "noun" || class == "conjunction") t2}"
+        else
+          "${optionDescriptionPhrase (class: class == "noun" || class == "conjunction") t1} or ${optionDescriptionPhrase (class: class == "noun" || class == "conjunction" || class == "composite") t2}";
       descriptionClass = "conjunction";
       check = x: t1.check x || t2.check x;
       merge = loc: defs: