about summary refs log tree commit diff
path: root/nixpkgs/lib
diff options
context:
space:
mode:
authorAlyssa Ross <hi@alyssa.is>2023-12-15 19:32:38 +0100
committerAlyssa Ross <hi@alyssa.is>2023-12-15 19:32:38 +0100
commit6b8e2555ef013b579cda57025b17d662e0f1fe1f (patch)
tree5a83c673af26c9976acd5a5dfa20e09e06898047 /nixpkgs/lib
parent66ca7a150b5c051f0728f13134e6265cc46f370c (diff)
parent02357adddd0889782362d999628de9d309d202dc (diff)
downloadnixlib-6b8e2555ef013b579cda57025b17d662e0f1fe1f.tar
nixlib-6b8e2555ef013b579cda57025b17d662e0f1fe1f.tar.gz
nixlib-6b8e2555ef013b579cda57025b17d662e0f1fe1f.tar.bz2
nixlib-6b8e2555ef013b579cda57025b17d662e0f1fe1f.tar.lz
nixlib-6b8e2555ef013b579cda57025b17d662e0f1fe1f.tar.xz
nixlib-6b8e2555ef013b579cda57025b17d662e0f1fe1f.tar.zst
nixlib-6b8e2555ef013b579cda57025b17d662e0f1fe1f.zip
Merge branch 'nixos-unstable-small' of https://github.com/NixOS/nixpkgs
Diffstat (limited to 'nixpkgs/lib')
-rw-r--r--nixpkgs/lib/README.md2
-rw-r--r--nixpkgs/lib/attrsets.nix96
-rw-r--r--nixpkgs/lib/customisation.nix4
-rw-r--r--nixpkgs/lib/default.nix2
-rw-r--r--nixpkgs/lib/fileset/default.nix58
-rw-r--r--nixpkgs/lib/fileset/internal.nix33
-rwxr-xr-xnixpkgs/lib/fileset/tests.sh6
-rw-r--r--nixpkgs/lib/flake-version-info.nix20
-rw-r--r--nixpkgs/lib/flake.nix7
-rw-r--r--nixpkgs/lib/lists.nix45
-rw-r--r--nixpkgs/lib/meta.nix34
-rw-r--r--nixpkgs/lib/modules.nix2
-rw-r--r--nixpkgs/lib/path/default.nix81
-rw-r--r--nixpkgs/lib/path/tests/unit.nix30
-rw-r--r--nixpkgs/lib/strings.nix43
-rw-r--r--nixpkgs/lib/systems/default.nix7
-rw-r--r--nixpkgs/lib/tests/misc.nix67
-rwxr-xr-xnixpkgs/lib/tests/modules.sh14
-rw-r--r--nixpkgs/lib/tests/modules/boolByOr.nix14
-rw-r--r--nixpkgs/lib/tests/modules/error-mkOption-in-config.nix14
-rw-r--r--nixpkgs/lib/tests/modules/error-mkOption-in-submodule-config.nix12
-rw-r--r--nixpkgs/lib/trivial.nix50
-rw-r--r--nixpkgs/lib/types.nix16
23 files changed, 549 insertions, 108 deletions
diff --git a/nixpkgs/lib/README.md b/nixpkgs/lib/README.md
index 220940bc2122..a886cf5bfb55 100644
--- a/nixpkgs/lib/README.md
+++ b/nixpkgs/lib/README.md
@@ -42,7 +42,7 @@ Reference documentation for library functions is written above each function as
 These comments are processed using [nixdoc](https://github.com/nix-community/nixdoc) and [rendered in the Nixpkgs manual](https://nixos.org/manual/nixpkgs/stable/#chap-functions).
 The nixdoc README describes the [comment format](https://github.com/nix-community/nixdoc#comment-format).
 
-See the [chapter on contributing to the Nixpkgs manual](https://nixos.org/manual/nixpkgs/#chap-contributing) for how to build the manual.
+See [doc/README.md](../doc/README.md) for how to build the manual.
 
 ## Running tests
 
diff --git a/nixpkgs/lib/attrsets.nix b/nixpkgs/lib/attrsets.nix
index 3d4366ce1814..99b686918453 100644
--- a/nixpkgs/lib/attrsets.nix
+++ b/nixpkgs/lib/attrsets.nix
@@ -14,6 +14,14 @@ rec {
 
   /* Return an attribute from nested attribute sets.
 
+     Nix has an [attribute selection operator `. or`](https://nixos.org/manual/nix/stable/language/operators#attribute-selection) which is sufficient for such queries, as long as the number of attributes is static. For example:
+
+     ```nix
+     (x.a.b or 6) == attrByPath ["a" "b"] 6 x
+     # and
+     (x.${f p}."example.com" or 6) == attrByPath [ (f p) "example.com" ] 6 x
+     ```
+
      Example:
        x = { a = { b = 3; }; }
        # ["a" "b"] is equivalent to x.a.b
@@ -51,12 +59,27 @@ rec {
 
   /* Return if an attribute from nested attribute set exists.
 
+     Nix has a [has attribute operator `?`](https://nixos.org/manual/nix/stable/language/operators#has-attribute), which is sufficient for such queries, as long as the number of attributes is static. For example:
+
+     ```nix
+     (x?a.b) == hasAttryByPath ["a" "b"] x
+     # and
+     (x?${f p}."example.com") == hasAttryByPath [ (f p) "example.com" ] x
+     ```
+
+     **Laws**:
+      1.  ```nix
+          hasAttrByPath [] x == true
+          ```
+
      Example:
        x = { a = { b = 3; }; }
        hasAttrByPath ["a" "b"] x
        => true
        hasAttrByPath ["z" "z"] x
        => false
+       hasAttrByPath [] (throw "no need")
+       => true
 
     Type:
       hasAttrByPath :: [String] -> AttrSet -> Bool
@@ -80,6 +103,71 @@ rec {
     in
       hasAttrByPath' 0 e;
 
+  /*
+    Return the longest prefix of an attribute path that refers to an existing attribute in a nesting of attribute sets.
+
+    Can be used after [`mapAttrsRecursiveCond`](#function-library-lib.attrsets.mapAttrsRecursiveCond) to apply a condition,
+    although this will evaluate the predicate function on sibling attributes as well.
+
+    Note that the empty attribute path is valid for all values, so this function only throws an exception if any of its inputs does.
+
+    **Laws**:
+    1.  ```nix
+        attrsets.longestValidPathPrefix [] x == []
+        ```
+
+    2.  ```nix
+        hasAttrByPath (attrsets.longestValidPathPrefix p x) x == true
+        ```
+
+    Example:
+      x = { a = { b = 3; }; }
+      attrsets.longestValidPathPrefix ["a" "b" "c"] x
+      => ["a" "b"]
+      attrsets.longestValidPathPrefix ["a"] x
+      => ["a"]
+      attrsets.longestValidPathPrefix ["z" "z"] x
+      => []
+      attrsets.longestValidPathPrefix ["z" "z"] (throw "no need")
+      => []
+
+    Type:
+      attrsets.longestValidPathPrefix :: [String] -> Value -> [String]
+  */
+  longestValidPathPrefix =
+    # A list of strings representing the longest possible path that may be returned.
+    attrPath:
+    # The nested attribute set to check.
+    v:
+    let
+      lenAttrPath = length attrPath;
+      getPrefixForSetAtIndex =
+        # The nested attribute set to check, if it is an attribute set, which
+        # is not a given.
+        remainingSet:
+        # The index of the attribute we're about to check, as well as
+        # the length of the prefix we've already checked.
+        remainingPathIndex:
+
+          if remainingPathIndex == lenAttrPath then
+            # All previously checked attributes exist, and no attr names left,
+            # so we return the whole path.
+            attrPath
+          else
+            let
+              attr = elemAt attrPath remainingPathIndex;
+            in
+            if remainingSet ? ${attr} then
+              getPrefixForSetAtIndex
+                remainingSet.${attr}      # advance from the set to the attribute value
+                (remainingPathIndex + 1)  # advance the path
+            else
+              # The attribute doesn't exist, so we return the prefix up to the
+              # previously checked length.
+              take remainingPathIndex attrPath;
+    in
+      getPrefixForSetAtIndex v 0;
+
   /* Create a new attribute set with `value` set at the nested attribute location specified in `attrPath`.
 
      Example:
@@ -105,6 +193,14 @@ rec {
   /* Like `attrByPath`, but without a default value. If it doesn't find the
      path it will throw an error.
 
+     Nix has an [attribute selection operator](https://nixos.org/manual/nix/stable/language/operators#attribute-selection) which is sufficient for such queries, as long as the number of attributes is static. For example:
+
+    ```nix
+     x.a.b == getAttrByPath ["a" "b"] x
+     # and
+     x.${f p}."example.com" == getAttrByPath [ (f p) "example.com" ] x
+     ```
+
      Example:
        x = { a = { b = 3; }; }
        getAttrFromPath ["a" "b"] x
diff --git a/nixpkgs/lib/customisation.nix b/nixpkgs/lib/customisation.nix
index 4de6f58a6aed..c233744e07ca 100644
--- a/nixpkgs/lib/customisation.nix
+++ b/nixpkgs/lib/customisation.nix
@@ -5,7 +5,7 @@ let
     intersectAttrs;
   inherit (lib)
     functionArgs isFunction mirrorFunctionArgs isAttrs setFunctionArgs
-    optionalAttrs attrNames filter elemAt concatStringsSep sort take length
+    optionalAttrs attrNames filter elemAt concatStringsSep sortOn take length
     filterAttrs optionalString flip pathIsDirectory head pipe isDerivation listToAttrs
     mapAttrs seq flatten deepSeq warnIf isInOldestRelease extends
     ;
@@ -174,7 +174,7 @@ rec {
         # levenshteinAtMost is only fast for 2 or less.
         (filter (levenshteinAtMost 2 arg))
         # Put strings with shorter distance first
-        (sort (x: y: levenshtein x arg < levenshtein y arg))
+        (sortOn (levenshtein arg))
         # Only take the first couple results
         (take 3)
         # Quote all entries
diff --git a/nixpkgs/lib/default.nix b/nixpkgs/lib/default.nix
index a2958e561cf3..35e31af364d8 100644
--- a/nixpkgs/lib/default.nix
+++ b/nixpkgs/lib/default.nix
@@ -91,7 +91,7 @@ let
     inherit (self.lists) singleton forEach foldr fold foldl foldl' imap0 imap1
       concatMap flatten remove findSingle findFirst any all count
       optional optionals toList range replicate partition zipListsWith zipLists
-      reverseList listDfs toposort sort naturalSort compareLists take
+      reverseList listDfs toposort sort sortOn naturalSort compareLists take
       drop sublist last init crossLists unique allUnique intersectLists
       subtractLists mutuallyExclusive groupBy groupBy';
     inherit (self.strings) concatStrings concatMapStrings concatImapStrings
diff --git a/nixpkgs/lib/fileset/default.nix b/nixpkgs/lib/fileset/default.nix
index 75e609a072e7..18acb9a980ca 100644
--- a/nixpkgs/lib/fileset/default.nix
+++ b/nixpkgs/lib/fileset/default.nix
@@ -107,7 +107,7 @@ let
     _printFileset
     _intersection
     _difference
-    _mirrorStorePath
+    _fromFetchGit
     _fetchGitSubmodulesMinver
     _emptyWithoutBase
     ;
@@ -148,7 +148,6 @@ let
   inherit (lib.trivial)
     isFunction
     pipe
-    inPureEvalMode
     ;
 
 in {
@@ -754,18 +753,11 @@ in {
       This directory must contain a `.git` file or subdirectory.
     */
     path:
-    # See the gitTrackedWith implementation for more explanatory comments
-    let
-      fetchResult = builtins.fetchGit path;
-    in
-    if inPureEvalMode then
-      throw "lib.fileset.gitTracked: This function is currently not supported in pure evaluation mode, since it currently relies on `builtins.fetchGit`. See https://github.com/NixOS/nix/issues/9292."
-    else if ! isPath path then
-      throw "lib.fileset.gitTracked: Expected the argument to be a path, but it's a ${typeOf path} instead."
-    else if ! pathExists (path + "/.git") then
-      throw "lib.fileset.gitTracked: Expected the argument (${toString path}) to point to a local working tree of a Git repository, but it's not."
-    else
-      _mirrorStorePath path fetchResult.outPath;
+    _fromFetchGit
+      "gitTracked"
+      "argument"
+      path
+      {};
 
   /*
     Create a file set containing all [Git-tracked files](https://git-scm.com/book/en/v2/Git-Basics-Recording-Changes-to-the-Repository) in a repository.
@@ -807,35 +799,19 @@ in {
       This directory must contain a `.git` file or subdirectory.
     */
     path:
-    let
-      # This imports the files unnecessarily, which currently can't be avoided
-      # because `builtins.fetchGit` is the only function exposing which files are tracked by Git.
-      # With the [lazy trees PR](https://github.com/NixOS/nix/pull/6530),
-      # the unnecessarily import could be avoided.
-      # However a simpler alternative still would be [a builtins.gitLsFiles](https://github.com/NixOS/nix/issues/2944).
-      fetchResult = builtins.fetchGit {
-        url = path;
-
-        # This is the only `fetchGit` parameter that makes sense in this context.
-        # We can't just pass `submodules = recurseSubmodules` here because
-        # this would fail for Nix versions that don't support `submodules`.
-        ${if recurseSubmodules then "submodules" else null} = true;
-      };
-    in
-    if inPureEvalMode then
-      throw "lib.fileset.gitTrackedWith: This function is currently not supported in pure evaluation mode, since it currently relies on `builtins.fetchGit`. See https://github.com/NixOS/nix/issues/9292."
-    else if ! isBool recurseSubmodules then
+    if ! isBool recurseSubmodules then
       throw "lib.fileset.gitTrackedWith: Expected the attribute `recurseSubmodules` of the first argument to be a boolean, but it's a ${typeOf recurseSubmodules} instead."
     else if recurseSubmodules && versionOlder nixVersion _fetchGitSubmodulesMinver then
       throw "lib.fileset.gitTrackedWith: Setting the attribute `recurseSubmodules` to `true` is only supported for Nix version ${_fetchGitSubmodulesMinver} and after, but Nix version ${nixVersion} is used."
-    else if ! isPath path then
-      throw "lib.fileset.gitTrackedWith: Expected the second argument to be a path, but it's a ${typeOf path} instead."
-    # We can identify local working directories by checking for .git,
-    # see https://git-scm.com/docs/gitrepository-layout#_description.
-    # Note that `builtins.fetchGit` _does_ work for bare repositories (where there's no `.git`),
-    # even though `git ls-files` wouldn't return any files in that case.
-    else if ! pathExists (path + "/.git") then
-      throw "lib.fileset.gitTrackedWith: Expected the second argument (${toString path}) to point to a local working tree of a Git repository, but it's not."
     else
-      _mirrorStorePath path fetchResult.outPath;
+      _fromFetchGit
+        "gitTrackedWith"
+        "second argument"
+        path
+        # This is the only `fetchGit` parameter that makes sense in this context.
+        # We can't just pass `submodules = recurseSubmodules` here because
+        # this would fail for Nix versions that don't support `submodules`.
+        (lib.optionalAttrs recurseSubmodules {
+          submodules = true;
+        });
 }
diff --git a/nixpkgs/lib/fileset/internal.nix b/nixpkgs/lib/fileset/internal.nix
index 35d556e78391..9de5590d3eff 100644
--- a/nixpkgs/lib/fileset/internal.nix
+++ b/nixpkgs/lib/fileset/internal.nix
@@ -10,6 +10,7 @@ let
     split
     trace
     typeOf
+    fetchGit
     ;
 
   inherit (lib.attrsets)
@@ -55,6 +56,9 @@ let
     hasSuffix
     ;
 
+  inherit (lib.trivial)
+    inPureEvalMode
+    ;
 in
 # Rare case of justified usage of rec:
 # - This file is internal, so the return value doesn't matter, no need to make things overridable
@@ -852,4 +856,33 @@ rec {
     in
     _create localPath
       (recurse storePath);
+
+  # Create a file set from the files included in the result of a fetchGit call
+  # Type: String -> String -> Path -> Attrs -> FileSet
+  _fromFetchGit = function: argument: path: extraFetchGitAttrs:
+    let
+      # This imports the files unnecessarily, which currently can't be avoided
+      # because `builtins.fetchGit` is the only function exposing which files are tracked by Git.
+      # With the [lazy trees PR](https://github.com/NixOS/nix/pull/6530),
+      # the unnecessarily import could be avoided.
+      # However a simpler alternative still would be [a builtins.gitLsFiles](https://github.com/NixOS/nix/issues/2944).
+      fetchResult = fetchGit ({
+        url = path;
+      } // extraFetchGitAttrs);
+    in
+    if inPureEvalMode then
+      throw "lib.fileset.${function}: This function is currently not supported in pure evaluation mode, since it currently relies on `builtins.fetchGit`. See https://github.com/NixOS/nix/issues/9292."
+    else if ! isPath path then
+      throw "lib.fileset.${function}: Expected the ${argument} to be a path, but it's a ${typeOf path} instead."
+    else if pathType path != "directory" then
+      throw "lib.fileset.${function}: Expected the ${argument} (${toString path}) to be a directory, but it's a file instead."
+    # We can identify local working directories by checking for .git,
+    # see https://git-scm.com/docs/gitrepository-layout#_description.
+    # Note that `builtins.fetchGit` _does_ work for bare repositories (where there's no `.git`),
+    # even though `git ls-files` wouldn't return any files in that case.
+    else if ! pathExists (path + "/.git") then
+      throw "lib.fileset.${function}: Expected the ${argument} (${toString path}) to point to a local working tree of a Git repository, but it's not."
+    else
+      _mirrorStorePath path fetchResult.outPath;
+
 }
diff --git a/nixpkgs/lib/fileset/tests.sh b/nixpkgs/lib/fileset/tests.sh
index 077aefe371c3..d55c4fbfdbeb 100755
--- a/nixpkgs/lib/fileset/tests.sh
+++ b/nixpkgs/lib/fileset/tests.sh
@@ -1317,6 +1317,12 @@ rm -rf -- *
 expectFailure 'gitTracked null' 'lib.fileset.gitTracked: Expected the argument to be a path, but it'\''s a null instead.'
 expectFailure 'gitTrackedWith {} null' 'lib.fileset.gitTrackedWith: Expected the second argument to be a path, but it'\''s a null instead.'
 
+# The path must be a directory
+touch a
+expectFailure 'gitTracked ./a' 'lib.fileset.gitTracked: Expected the argument \('"$work"'/a\) to be a directory, but it'\''s a file instead'
+expectFailure 'gitTrackedWith {} ./a' 'lib.fileset.gitTrackedWith: Expected the second argument \('"$work"'/a\) to be a directory, but it'\''s a file instead'
+rm -rf -- *
+
 # The path has to contain a .git directory
 expectFailure 'gitTracked ./.' 'lib.fileset.gitTracked: Expected the argument \('"$work"'\) to point to a local working tree of a Git repository, but it'\''s not.'
 expectFailure 'gitTrackedWith {} ./.' 'lib.fileset.gitTrackedWith: Expected the second argument \('"$work"'\) to point to a local working tree of a Git repository, but it'\''s not.'
diff --git a/nixpkgs/lib/flake-version-info.nix b/nixpkgs/lib/flake-version-info.nix
new file mode 100644
index 000000000000..de15be94bee8
--- /dev/null
+++ b/nixpkgs/lib/flake-version-info.nix
@@ -0,0 +1,20 @@
+# This function produces a lib overlay to be used by the nixpkgs
+# & nixpkgs/lib flakes to provide meaningful values for
+# `lib.trivial.version` et al..
+#
+# Internal and subject to change, don't use this anywhere else!
+# Instead, consider using a public interface, such as this flake here
+# in this directory, `lib/`, or use the nixpkgs flake, which applies
+# this logic for you in its `lib` output attribute.
+
+self: # from the flake
+
+finalLib: prevLib: # lib overlay
+
+{
+  trivial = prevLib.trivial // {
+    versionSuffix =
+      ".${finalLib.substring 0 8 (self.lastModifiedDate or "19700101")}.${self.shortRev or "dirty"}";
+    revisionWithDefault = default: self.rev or default;
+  };
+}
diff --git a/nixpkgs/lib/flake.nix b/nixpkgs/lib/flake.nix
index 0b5e54d547c5..ca09ed5f4a42 100644
--- a/nixpkgs/lib/flake.nix
+++ b/nixpkgs/lib/flake.nix
@@ -1,5 +1,10 @@
 {
   description = "Library of low-level helper functions for nix expressions.";
 
-  outputs = { self }: { lib = import ./.; };
+  outputs = { self }:
+    let
+      lib0 = import ./.;
+    in {
+      lib = lib0.extend (import ./flake-version-info.nix self);
+    };
 }
diff --git a/nixpkgs/lib/lists.nix b/nixpkgs/lib/lists.nix
index a56667ec9c38..9397acf148fc 100644
--- a/nixpkgs/lib/lists.nix
+++ b/nixpkgs/lib/lists.nix
@@ -4,6 +4,7 @@ let
   inherit (lib.strings) toInt;
   inherit (lib.trivial) compare min id;
   inherit (lib.attrsets) mapAttrs;
+  inherit (lib.lists) sort;
 in
 rec {
 
@@ -591,9 +592,15 @@ rec {
      the second argument.  The returned list is sorted in an increasing
      order.  The implementation does a quick-sort.
 
+     See also [`sortOn`](#function-library-lib.lists.sortOn), which applies the
+     default comparison on a function-derived property, and may be more efficient.
+
      Example:
-       sort (a: b: a < b) [ 5 3 7 ]
+       sort (p: q: p < q) [ 5 3 7 ]
        => [ 3 5 7 ]
+
+     Type:
+       sort :: (a -> a -> Bool) -> [a] -> [a]
   */
   sort = builtins.sort or (
     strictLess: list:
@@ -612,6 +619,42 @@ rec {
       if len < 2 then list
       else (sort strictLess pivot.left) ++  [ first ] ++  (sort strictLess pivot.right));
 
+  /*
+    Sort a list based on the default comparison of a derived property `b`.
+
+    The items are returned in `b`-increasing order.
+
+    **Performance**:
+
+    The passed function `f` is only evaluated once per item,
+    unlike an unprepared [`sort`](#function-library-lib.lists.sort) using
+    `f p < f q`.
+
+    **Laws**:
+    ```nix
+    sortOn f == sort (p: q: f p < f q)
+    ```
+
+    Example:
+      sortOn stringLength [ "aa" "b" "cccc" ]
+      => [ "b" "aa" "cccc" ]
+
+    Type:
+      sortOn :: (a -> b) -> [a] -> [a], for comparable b
+  */
+  sortOn = f: list:
+    let
+      # Heterogenous list as pair may be ugly, but requires minimal allocations.
+      pairs = map (x: [(f x) x]) list;
+    in
+      map
+        (x: builtins.elemAt x 1)
+        (sort
+          # Compare the first element of the pairs
+          # Do not factor out the `<`, to avoid calls in hot code; duplicate instead.
+          (a: b: head a < head b)
+          pairs);
+
   /* Compare two lists element-by-element.
 
      Example:
diff --git a/nixpkgs/lib/meta.nix b/nixpkgs/lib/meta.nix
index 1a43016d27c4..5d5f71d6c3cb 100644
--- a/nixpkgs/lib/meta.nix
+++ b/nixpkgs/lib/meta.nix
@@ -4,8 +4,8 @@
 { lib }:
 
 let
-  inherit (lib) matchAttrs any all;
-  inherit (builtins) isString;
+  inherit (lib) matchAttrs any all isDerivation getBin assertMsg;
+  inherit (builtins) isString match typeOf;
 
 in
 rec {
@@ -154,16 +154,12 @@ rec {
        getExe pkgs.mustache-go
        => "/nix/store/am9ml4f4ywvivxnkiaqwr0hyxka1xjsf-mustache-go-1.3.0/bin/mustache"
   */
-  getExe = x:
-    let
-      y = x.meta.mainProgram or (
-        # This could be turned into an error when 23.05 is at end of life
-        lib.warn "getExe: Package ${lib.strings.escapeNixIdentifier x.meta.name or x.pname or x.name} does not have the meta.mainProgram attribute. We'll assume that the main program has the same name for now, but this behavior is deprecated, because it leads to surprising errors when the assumption does not hold. If the package has a main program, please set `meta.mainProgram` in its definition to make this warning go away. Otherwise, if the package does not have a main program, or if you don't control its definition, use getExe' to specify the name to the program, such as lib.getExe' foo \"bar\"."
-          lib.getName
-          x
-      );
-    in
-    getExe' x y;
+  getExe = x: getExe' x (x.meta.mainProgram or (
+    # This could be turned into an error when 23.05 is at end of life
+    lib.warn "getExe: Package ${lib.strings.escapeNixIdentifier x.meta.name or x.pname or x.name} does not have the meta.mainProgram attribute. We'll assume that the main program has the same name for now, but this behavior is deprecated, because it leads to surprising errors when the assumption does not hold. If the package has a main program, please set `meta.mainProgram` in its definition to make this warning go away. Otherwise, if the package does not have a main program, or if you don't control its definition, use getExe' to specify the name to the program, such as lib.getExe' foo \"bar\"."
+    lib.getName
+    x
+  ));
 
   /* Get the path of a program of a derivation.
 
@@ -175,11 +171,11 @@ rec {
        => "/nix/store/5rs48jamq7k6sal98ymj9l4k2bnwq515-imagemagick-7.1.1-15/bin/convert"
   */
   getExe' = x: y:
-    assert lib.assertMsg (lib.isDerivation x)
-      "lib.meta.getExe': The first argument is of type ${builtins.typeOf x}, but it should be a derivation instead.";
-    assert lib.assertMsg (lib.isString y)
-     "lib.meta.getExe': The second argument is of type ${builtins.typeOf y}, but it should be a string instead.";
-    assert lib.assertMsg (builtins.length (lib.splitString "/" y) == 1)
-     "lib.meta.getExe': The second argument \"${y}\" is a nested path with a \"/\" character, but it should just be the name of the executable instead.";
-    "${lib.getBin x}/bin/${y}";
+    assert assertMsg (isDerivation x)
+      "lib.meta.getExe': The first argument is of type ${typeOf x}, but it should be a derivation instead.";
+    assert assertMsg (isString y)
+      "lib.meta.getExe': The second argument is of type ${typeOf y}, but it should be a string instead.";
+    assert assertMsg (match ".*\/.*" y == null)
+      "lib.meta.getExe': The second argument \"${y}\" is a nested path with a \"/\" character, but it should just be the name of the executable instead.";
+    "${getBin x}/bin/${y}";
 }
diff --git a/nixpkgs/lib/modules.nix b/nixpkgs/lib/modules.nix
index 4acbce39e94d..64939a1eae81 100644
--- a/nixpkgs/lib/modules.nix
+++ b/nixpkgs/lib/modules.nix
@@ -275,6 +275,8 @@ let
                 "The option `${optText}' does not exist. Definition values:${defText}";
           in
             if attrNames options == [ "_module" ]
+              # No options were declared at all (`_module` is built in)
+              # but we do have unmatched definitions, and no freeformType (earlier conditions)
               then
                 let
                   optionName = showOption prefix;
diff --git a/nixpkgs/lib/path/default.nix b/nixpkgs/lib/path/default.nix
index ab8a9e2202e9..e6b385c0aee0 100644
--- a/nixpkgs/lib/path/default.nix
+++ b/nixpkgs/lib/path/default.nix
@@ -9,6 +9,7 @@ let
     split
     match
     typeOf
+    storeDir
     ;
 
   inherit (lib.lists)
@@ -24,6 +25,8 @@ let
     drop
     ;
 
+  listHasPrefix = lib.lists.hasPrefix;
+
   inherit (lib.strings)
     concatStringsSep
     substring
@@ -120,6 +123,28 @@ let
         else recurse ([ (baseNameOf base) ] ++ components) (dirOf base);
     in recurse [];
 
+  # The components of the store directory, typically [ "nix" "store" ]
+  storeDirComponents = splitRelPath ("./" + storeDir);
+  # The number of store directory components, typically 2
+  storeDirLength = length storeDirComponents;
+
+  # Type: [ String ] -> Bool
+  #
+  # Whether path components have a store path as a prefix, according to
+  # https://nixos.org/manual/nix/stable/store/store-path.html#store-path.
+  componentsHaveStorePathPrefix = components:
+    # path starts with the store directory (typically /nix/store)
+    listHasPrefix storeDirComponents components
+    # is not the store directory itself, meaning there's at least one extra component
+    && storeDirComponents != components
+    # and the first component after the store directory has the expected format.
+    # NOTE: We could change the hash regex to be [0-9a-df-np-sv-z],
+    # because these are the actual ASCII characters used by Nix's base32 implementation,
+    # but this is not fully specified, so let's tie this too much to the currently implemented concept of store paths.
+    # Similar reasoning applies to the validity of the name part.
+    # We care more about discerning store path-ness on realistic values. Making it airtight would be fragile and slow.
+    && match ".{32}-.+" (elemAt components storeDirLength) != null;
+
 in /* No rec! Add dependencies on this file at the top. */ {
 
   /*
@@ -322,6 +347,62 @@ in /* No rec! Add dependencies on this file at the top. */ {
     };
 
   /*
+    Whether a [path](https://nixos.org/manual/nix/stable/language/values.html#type-path)
+    has a [store path](https://nixos.org/manual/nix/stable/store/store-path.html#store-path)
+    as a prefix.
+
+    :::{.note}
+    As with all functions of this `lib.path` library, it does not work on paths in strings,
+    which is how you'd typically get store paths.
+
+    Instead, this function only handles path values themselves,
+    which occur when Nix files in the store use relative path expressions.
+    :::
+
+    Type:
+      hasStorePathPrefix :: Path -> Bool
+
+    Example:
+      # Subpaths of derivation outputs have a store path as a prefix
+      hasStorePathPrefix /nix/store/nvl9ic0pj1fpyln3zaqrf4cclbqdfn1j-foo/bar/baz
+      => true
+
+      # The store directory itself is not a store path
+      hasStorePathPrefix /nix/store
+      => false
+
+      # Derivation outputs are store paths themselves
+      hasStorePathPrefix /nix/store/nvl9ic0pj1fpyln3zaqrf4cclbqdfn1j-foo
+      => true
+
+      # Paths outside the Nix store don't have a store path prefix
+      hasStorePathPrefix /home/user
+      => false
+
+      # Not all paths under the Nix store are store paths
+      hasStorePathPrefix /nix/store/.links/10gg8k3rmbw8p7gszarbk7qyd9jwxhcfq9i6s5i0qikx8alkk4hq
+      => false
+
+      # Store derivations are also store paths themselves
+      hasStorePathPrefix /nix/store/nvl9ic0pj1fpyln3zaqrf4cclbqdfn1j-foo.drv
+      => true
+  */
+  hasStorePathPrefix = path:
+    let
+      deconstructed = deconstructPath path;
+    in
+    assert assertMsg
+      (isPath path)
+      "lib.path.hasStorePathPrefix: Argument is of type ${typeOf path}, but a path was expected";
+    assert assertMsg
+      # This function likely breaks or needs adjustment if used with other filesystem roots, if they ever get implemented.
+      # Let's try to error nicely in such a case, though it's unclear how an implementation would work even and whether this could be detected.
+      # See also https://github.com/NixOS/nix/pull/6530#discussion_r1422843117
+      (deconstructed.root == /. && toString deconstructed.root == "/")
+      "lib.path.hasStorePathPrefix: Argument has a filesystem root (${toString deconstructed.root}) that's not /, which is currently not supported.";
+    componentsHaveStorePathPrefix deconstructed.components;
+
+  /*
     Whether a value is a valid subpath string.
 
     A subpath string points to a specific file or directory within an absolute base directory.
diff --git a/nixpkgs/lib/path/tests/unit.nix b/nixpkgs/lib/path/tests/unit.nix
index bad6560f13a9..9b0a0b2714aa 100644
--- a/nixpkgs/lib/path/tests/unit.nix
+++ b/nixpkgs/lib/path/tests/unit.nix
@@ -3,7 +3,10 @@
 { libpath }:
 let
   lib = import libpath;
-  inherit (lib.path) hasPrefix removePrefix append splitRoot subpath;
+  inherit (lib.path) hasPrefix removePrefix append splitRoot hasStorePathPrefix subpath;
+
+  # This is not allowed generally, but we're in the tests here, so we'll allow ourselves.
+  storeDirPath = /. + builtins.storeDir;
 
   cases = lib.runTests {
     # Test examples from the lib.path.append documentation
@@ -91,6 +94,31 @@ let
       expected = false;
     };
 
+    testHasStorePathPrefixExample1 = {
+      expr = hasStorePathPrefix (storeDirPath + "/nvl9ic0pj1fpyln3zaqrf4cclbqdfn1j-foo/bar/baz");
+      expected = true;
+    };
+    testHasStorePathPrefixExample2 = {
+      expr = hasStorePathPrefix storeDirPath;
+      expected = false;
+    };
+    testHasStorePathPrefixExample3 = {
+      expr = hasStorePathPrefix (storeDirPath + "/nvl9ic0pj1fpyln3zaqrf4cclbqdfn1j-foo");
+      expected = true;
+    };
+    testHasStorePathPrefixExample4 = {
+      expr = hasStorePathPrefix /home/user;
+      expected = false;
+    };
+    testHasStorePathPrefixExample5 = {
+      expr = hasStorePathPrefix (storeDirPath + "/.links/10gg8k3rmbw8p7gszarbk7qyd9jwxhcfq9i6s5i0qikx8alkk4hq");
+      expected = false;
+    };
+    testHasStorePathPrefixExample6 = {
+      expr = hasStorePathPrefix (storeDirPath + "/nvl9ic0pj1fpyln3zaqrf4cclbqdfn1j-foo.drv");
+      expected = true;
+    };
+
     # Test examples from the lib.path.subpath.isValid documentation
     testSubpathIsValidExample1 = {
       expr = subpath.isValid null;
diff --git a/nixpkgs/lib/strings.nix b/nixpkgs/lib/strings.nix
index 695aaaacd348..49654d8abaa7 100644
--- a/nixpkgs/lib/strings.nix
+++ b/nixpkgs/lib/strings.nix
@@ -715,12 +715,12 @@ rec {
        getName pkgs.youtube-dl
        => "youtube-dl"
   */
-  getName = x:
-   let
-     parse = drv: (parseDrvName drv).name;
-   in if isString x
-      then parse x
-      else x.pname or (parse x.name);
+  getName = let
+    parse = drv: (parseDrvName drv).name;
+  in x:
+    if isString x
+    then parse x
+    else x.pname or (parse x.name);
 
   /* This function takes an argument that's either a derivation or a
      derivation's "name" attribute and extracts the version part from that
@@ -732,12 +732,12 @@ rec {
        getVersion pkgs.youtube-dl
        => "2016.01.01"
   */
-  getVersion = x:
-   let
-     parse = drv: (parseDrvName drv).version;
-   in if isString x
-      then parse x
-      else x.version or (parse x.name);
+  getVersion = let
+    parse = drv: (parseDrvName drv).version;
+  in x:
+    if isString x
+    then parse x
+    else x.version or (parse x.name);
 
   /* Extract name with version from URL. Ask for separator which is
      supposed to start extension.
@@ -771,12 +771,13 @@ rec {
        cmakeOptionType "string" "ENGINE" "sdl2"
        => "-DENGINE:STRING=sdl2"
   */
-  cmakeOptionType = type: feature: value:
-    assert (lib.elem (lib.toUpper type)
-      [ "BOOL" "FILEPATH" "PATH" "STRING" "INTERNAL" ]);
-    assert (lib.isString feature);
-    assert (lib.isString value);
-    "-D${feature}:${lib.toUpper type}=${value}";
+  cmakeOptionType = let
+    types = [ "BOOL" "FILEPATH" "PATH" "STRING" "INTERNAL" ];
+  in type: feature: value:
+    assert (elem (toUpper type) types);
+    assert (isString feature);
+    assert (isString value);
+    "-D${feature}:${toUpper type}=${value}";
 
   /* Create a -D<condition>={TRUE,FALSE} string that can be passed to typical
      CMake invocations.
@@ -977,9 +978,11 @@ rec {
      Many types of value are coercible to string this way, including int, float,
      null, bool, list of similarly coercible values.
   */
-  isConvertibleWithToString = x:
+  isConvertibleWithToString = let
+    types = [ "null" "int" "float" "bool" ];
+  in x:
     isStringLike x ||
-    elem (typeOf x) [ "null" "int" "float" "bool" ] ||
+    elem (typeOf x) types ||
     (isList x && lib.all isConvertibleWithToString x);
 
   /* Check whether a value can be coerced to a string.
diff --git a/nixpkgs/lib/systems/default.nix b/nixpkgs/lib/systems/default.nix
index 9eec21cbf21b..6137d47e91a2 100644
--- a/nixpkgs/lib/systems/default.nix
+++ b/nixpkgs/lib/systems/default.nix
@@ -89,6 +89,13 @@ rec {
         # is why we use the more obscure "bfd" and not "binutils" for this
         # choice.
         else                                     "bfd";
+      # The standard lib directory name that non-nixpkgs binaries distributed
+      # for this platform normally assume.
+      libDir = if final.isLinux then
+        if final.isx86_64 || final.isMips64 || final.isPower64
+        then "lib64"
+        else "lib"
+      else null;
       extensions = lib.optionalAttrs final.hasSharedLibraries {
         sharedLibrary =
           if      final.isDarwin  then ".dylib"
diff --git a/nixpkgs/lib/tests/misc.nix b/nixpkgs/lib/tests/misc.nix
index 9f1fee2ba234..2884e880e13a 100644
--- a/nixpkgs/lib/tests/misc.nix
+++ b/nixpkgs/lib/tests/misc.nix
@@ -650,6 +650,28 @@ runTests {
     expected = [2 30 40 42];
   };
 
+  testSortOn = {
+    expr = sortOn stringLength [ "aa" "b" "cccc" ];
+    expected = [ "b" "aa" "cccc" ];
+  };
+
+  testSortOnEmpty = {
+    expr = sortOn (throw "nope") [ ];
+    expected = [ ];
+  };
+
+  testSortOnIncomparable = {
+    expr =
+      map
+        (x: x.f x.ok)
+        (sortOn (x: x.ok) [
+          { ok = 1; f = x: x; }
+          { ok = 3; f = x: x + 3; }
+          { ok = 2; f = x: x; }
+        ]);
+    expected = [ 1 2 6 ];
+  };
+
   testReplicate = {
     expr = replicate 3 "a";
     expected = ["a" "a" "a"];
@@ -675,6 +697,51 @@ runTests {
     expected = false;
   };
 
+  testHasAttrByPathNonStrict = {
+    expr = hasAttrByPath [] (throw "do not use");
+    expected = true;
+  };
+
+  testLongestValidPathPrefix_empty_empty = {
+    expr = attrsets.longestValidPathPrefix [ ] { };
+    expected = [ ];
+  };
+
+  testLongestValidPathPrefix_empty_nonStrict = {
+    expr = attrsets.longestValidPathPrefix [ ] (throw "do not use");
+    expected = [ ];
+  };
+
+  testLongestValidPathPrefix_zero = {
+    expr = attrsets.longestValidPathPrefix [ "a" (throw "do not use") ] { d = null; };
+    expected = [ ];
+  };
+
+  testLongestValidPathPrefix_zero_b = {
+    expr = attrsets.longestValidPathPrefix [ "z" "z" ] "remarkably harmonious";
+    expected = [ ];
+  };
+
+  testLongestValidPathPrefix_one = {
+    expr = attrsets.longestValidPathPrefix [ "a" "b" "c" ] { a = null; };
+    expected = [ "a" ];
+  };
+
+  testLongestValidPathPrefix_two = {
+    expr = attrsets.longestValidPathPrefix [ "a" "b" "c" ] { a.b = null; };
+    expected = [ "a" "b" ];
+  };
+
+  testLongestValidPathPrefix_three = {
+    expr = attrsets.longestValidPathPrefix [ "a" "b" "c" ] { a.b.c = null; };
+    expected = [ "a" "b" "c" ];
+  };
+
+  testLongestValidPathPrefix_three_extra = {
+    expr = attrsets.longestValidPathPrefix [ "a" "b" "c" ] { a.b.c.d = throw "nope"; };
+    expected = [ "a" "b" "c" ];
+  };
+
   testFindFirstIndexExample1 = {
     expr = lists.findFirstIndex (x: x > 3) (abort "index found, so a default must not be evaluated") [ 1 6 4 ];
     expected = 1;
diff --git a/nixpkgs/lib/tests/modules.sh b/nixpkgs/lib/tests/modules.sh
index 21d4978a1160..4cf9821a3063 100755
--- a/nixpkgs/lib/tests/modules.sh
+++ b/nixpkgs/lib/tests/modules.sh
@@ -94,6 +94,14 @@ checkConfigOutput '^true$' config.result ./module-argument-default.nix
 # gvariant
 checkConfigOutput '^true$' config.assertion ./gvariant.nix
 
+# https://github.com/NixOS/nixpkgs/pull/131205
+# We currently throw this error already in `config`, but throwing in `config.wrong1` would be acceptable.
+checkConfigError 'It seems as if you.re trying to declare an option by placing it into .config. rather than .options.' config.wrong1 ./error-mkOption-in-config.nix
+# We currently throw this error already in `config`, but throwing in `config.nest.wrong2` would be acceptable.
+checkConfigError 'It seems as if you.re trying to declare an option by placing it into .config. rather than .options.' config.nest.wrong2 ./error-mkOption-in-config.nix
+checkConfigError 'The option .sub.wrong2. does not exist. Definition values:' config.sub ./error-mkOption-in-submodule-config.nix
+checkConfigError '.*This can happen if you e.g. declared your options in .types.submodule.' config.sub ./error-mkOption-in-submodule-config.nix
+
 # types.pathInStore
 checkConfigOutput '".*/store/0lz9p8xhf89kb1c1kk6jxrzskaiygnlh-bash-5.2-p15.drv"' config.pathInStore.ok1 ./types.nix
 checkConfigOutput '".*/store/0fb3ykw9r5hpayd05sr0cizwadzq1d8q-bash-5.2-p15"' config.pathInStore.ok2 ./types.nix
@@ -111,6 +119,12 @@ checkConfigError 'The option .* does not exist. Definition values:\n\s*- In .*'
 checkConfigError 'while evaluating a definition from `.*/define-enable-abort.nix' config.enable ./define-enable-abort.nix
 checkConfigError 'while evaluating the error message for definitions for .enable., which is an option that does not exist' config.enable ./define-enable-abort.nix
 
+# Check boolByOr type.
+checkConfigOutput '^false$' config.value.falseFalse ./boolByOr.nix
+checkConfigOutput '^true$' config.value.trueFalse ./boolByOr.nix
+checkConfigOutput '^true$' config.value.falseTrue ./boolByOr.nix
+checkConfigOutput '^true$' config.value.trueTrue ./boolByOr.nix
+
 checkConfigOutput '^1$' config.bare-submodule.nested ./declare-bare-submodule.nix ./declare-bare-submodule-nested-option.nix
 checkConfigOutput '^2$' config.bare-submodule.deep ./declare-bare-submodule.nix ./declare-bare-submodule-deep-option.nix
 checkConfigOutput '^42$' config.bare-submodule.nested ./declare-bare-submodule.nix ./declare-bare-submodule-nested-option.nix ./declare-bare-submodule-deep-option.nix ./define-bare-submodule-values.nix
diff --git a/nixpkgs/lib/tests/modules/boolByOr.nix b/nixpkgs/lib/tests/modules/boolByOr.nix
new file mode 100644
index 000000000000..ff86e2dfc859
--- /dev/null
+++ b/nixpkgs/lib/tests/modules/boolByOr.nix
@@ -0,0 +1,14 @@
+{ lib, ... }: {
+
+  options.value = lib.mkOption {
+    type = lib.types.lazyAttrsOf lib.types.boolByOr;
+  };
+
+  config.value = {
+    falseFalse = lib.mkMerge [ false false ];
+    trueFalse = lib.mkMerge [ true false ];
+    falseTrue = lib.mkMerge [ false true ];
+    trueTrue = lib.mkMerge [ true true ];
+  };
+}
+
diff --git a/nixpkgs/lib/tests/modules/error-mkOption-in-config.nix b/nixpkgs/lib/tests/modules/error-mkOption-in-config.nix
new file mode 100644
index 000000000000..2d78cd8db8ba
--- /dev/null
+++ b/nixpkgs/lib/tests/modules/error-mkOption-in-config.nix
@@ -0,0 +1,14 @@
+{ lib, ... }:
+let
+  inherit (lib) mkOption;
+in
+{
+  wrong1 = mkOption {
+  };
+  # This is not actually reported separately, so could be omitted from the test
+  # but it makes the example more realistic.
+  # Making it parse this _config_ as options would too risky. What if it's not
+  # options but other values, that abort, throw, diverge, etc?
+  nest.wrong2 = mkOption {
+  };
+}
diff --git a/nixpkgs/lib/tests/modules/error-mkOption-in-submodule-config.nix b/nixpkgs/lib/tests/modules/error-mkOption-in-submodule-config.nix
new file mode 100644
index 000000000000..91ac3d65780a
--- /dev/null
+++ b/nixpkgs/lib/tests/modules/error-mkOption-in-submodule-config.nix
@@ -0,0 +1,12 @@
+{ lib, ... }:
+let
+  inherit (lib) mkOption;
+in
+{
+  options.sub = lib.mkOption {
+    type = lib.types.submodule {
+      wrong2 = mkOption {};
+    };
+    default = {};
+  };
+}
diff --git a/nixpkgs/lib/trivial.nix b/nixpkgs/lib/trivial.nix
index caff77190fde..b2796096e8bc 100644
--- a/nixpkgs/lib/trivial.nix
+++ b/nixpkgs/lib/trivial.nix
@@ -1,6 +1,18 @@
 { lib }:
 
-rec {
+let
+  inherit (lib.trivial)
+    isFunction
+    isInt
+    functionArgs
+    pathExists
+    release
+    setFunctionArgs
+    toBaseDigits
+    version
+    versionSuffix
+    warn;
+in {
 
   ## Simple (higher order) functions
 
@@ -58,9 +70,7 @@ rec {
      of the next function, and the last function returns the
      final value.
   */
-  pipe = val: functions:
-    let reverseApply = x: f: f x;
-    in builtins.foldl' reverseApply val functions;
+  pipe = builtins.foldl' (x: f: f x);
 
   # note please don’t add a function like `compose = flip pipe`.
   # This would confuse users, because the order of the functions
@@ -439,7 +449,7 @@ rec {
   */
   functionArgs = f:
     if f ? __functor
-    then f.__functionArgs or (lib.functionArgs (f.__functor f))
+    then f.__functionArgs or (functionArgs (f.__functor f))
     else builtins.functionArgs f;
 
   /* Check whether something is a function or something
@@ -510,22 +520,20 @@ rec {
 
      toHexString 250 => "FA"
   */
-  toHexString = i:
-    let
-      toHexDigit = d:
-        if d < 10
-        then toString d
-        else
-          {
-            "10" = "A";
-            "11" = "B";
-            "12" = "C";
-            "13" = "D";
-            "14" = "E";
-            "15" = "F";
-          }.${toString d};
-    in
-      lib.concatMapStrings toHexDigit (toBaseDigits 16 i);
+  toHexString = let
+    hexDigits = {
+      "10" = "A";
+      "11" = "B";
+      "12" = "C";
+      "13" = "D";
+      "14" = "E";
+      "15" = "F";
+    };
+    toHexDigit = d:
+      if d < 10
+      then toString d
+      else hexDigits.${toString d};
+  in i: lib.concatMapStrings toHexDigit (toBaseDigits 16 i);
 
   /* `toBaseDigits base i` converts the positive integer i to a list of its
      digits in the given base. For example:
diff --git a/nixpkgs/lib/types.nix b/nixpkgs/lib/types.nix
index 5ffbecda5db3..51e58eaa8ab5 100644
--- a/nixpkgs/lib/types.nix
+++ b/nixpkgs/lib/types.nix
@@ -275,6 +275,22 @@ rec {
       merge = mergeEqualOption;
     };
 
+    boolByOr = mkOptionType {
+      name = "boolByOr";
+      description = "boolean (merged using or)";
+      descriptionClass = "noun";
+      check = isBool;
+      merge = loc: defs:
+        foldl'
+          (result: def:
+            # Under the assumption that .check always runs before merge, we can assume that all defs.*.value
+            # have been forced, and therefore we assume we don't introduce order-dependent strictness here
+            result || def.value
+          )
+          false
+          defs;
+    };
+
     int = mkOptionType {
       name = "int";
       description = "signed integer";