about summary refs log tree commit diff
path: root/nixpkgs/lib
diff options
context:
space:
mode:
authorAlyssa Ross <hi@alyssa.is>2022-12-06 19:57:55 +0000
committerAlyssa Ross <hi@alyssa.is>2023-02-08 13:48:30 +0000
commitbf3aadfdd39aa197e18bade671fab6726349ffa4 (patch)
tree698567af766ed441d757b57a7b21e68d4a342a2b /nixpkgs/lib
parentf4afc5a01d9539ce09e47494e679c51f80723d07 (diff)
parent99665eb45f58d959d2cb9e49ddb960c79d596f33 (diff)
downloadnixlib-bf3aadfdd39aa197e18bade671fab6726349ffa4.tar
nixlib-bf3aadfdd39aa197e18bade671fab6726349ffa4.tar.gz
nixlib-bf3aadfdd39aa197e18bade671fab6726349ffa4.tar.bz2
nixlib-bf3aadfdd39aa197e18bade671fab6726349ffa4.tar.lz
nixlib-bf3aadfdd39aa197e18bade671fab6726349ffa4.tar.xz
nixlib-bf3aadfdd39aa197e18bade671fab6726349ffa4.tar.zst
nixlib-bf3aadfdd39aa197e18bade671fab6726349ffa4.zip
Merge commit '99665eb45f58d959d2cb9e49ddb960c79d596f33'
Diffstat (limited to 'nixpkgs/lib')
-rw-r--r--nixpkgs/lib/attrsets.nix2
-rw-r--r--nixpkgs/lib/customisation.nix51
-rw-r--r--nixpkgs/lib/default.nix12
-rw-r--r--nixpkgs/lib/generators.nix61
-rw-r--r--nixpkgs/lib/licenses.nix18
-rw-r--r--nixpkgs/lib/lists.nix2
-rw-r--r--nixpkgs/lib/meta.nix14
-rw-r--r--nixpkgs/lib/modules.nix106
-rw-r--r--nixpkgs/lib/options.nix25
-rw-r--r--nixpkgs/lib/source-types.nix19
-rw-r--r--nixpkgs/lib/strings.nix202
-rw-r--r--nixpkgs/lib/systems/default.nix18
-rw-r--r--nixpkgs/lib/systems/doubles.nix5
-rw-r--r--nixpkgs/lib/systems/examples.nix45
-rw-r--r--nixpkgs/lib/systems/flake-systems.nix29
-rw-r--r--nixpkgs/lib/systems/inspect.nix6
-rw-r--r--nixpkgs/lib/systems/parse.nix20
-rw-r--r--nixpkgs/lib/systems/platforms.nix17
-rw-r--r--nixpkgs/lib/systems/supported.nix26
-rw-r--r--nixpkgs/lib/tests/maintainer-module.nix31
-rw-r--r--nixpkgs/lib/tests/maintainers.nix37
-rw-r--r--nixpkgs/lib/tests/misc.nix297
-rwxr-xr-xnixpkgs/lib/tests/modules.sh21
-rw-r--r--nixpkgs/lib/tests/modules/deferred-module-error.nix20
-rw-r--r--nixpkgs/lib/tests/modules/deferred-module.nix58
-rw-r--r--nixpkgs/lib/tests/modules/define-settingsDict-a-is-b.nix3
-rw-r--r--nixpkgs/lib/tests/modules/extendModules-168767-imports.nix41
-rw-r--r--nixpkgs/lib/tests/modules/functionTo/submodule-options.nix61
-rw-r--r--nixpkgs/lib/tests/modules/submoduleFiles.nix21
-rw-r--r--nixpkgs/lib/tests/release.nix4
-rw-r--r--nixpkgs/lib/tests/systems.nix3
-rw-r--r--nixpkgs/lib/tests/teams.nix50
-rw-r--r--nixpkgs/lib/trivial.nix11
-rw-r--r--nixpkgs/lib/types.nix86
-rw-r--r--nixpkgs/lib/zip-int-bits.nix2
35 files changed, 1259 insertions, 165 deletions
diff --git a/nixpkgs/lib/attrsets.nix b/nixpkgs/lib/attrsets.nix
index 516fdd8d33fd..5575e9577029 100644
--- a/nixpkgs/lib/attrsets.nix
+++ b/nixpkgs/lib/attrsets.nix
@@ -248,7 +248,7 @@ rec {
   /* Apply fold functions to values grouped by key.
 
      Example:
-       foldAttrs (n: a: [n] ++ a) [] [{ a = 2; } { a = 3; }]
+       foldAttrs (item: acc: [item] ++ acc) [] [{ a = 2; } { a = 3; }]
        => { a = [ 2 3 ]; }
   */
   foldAttrs = op: nul:
diff --git a/nixpkgs/lib/customisation.nix b/nixpkgs/lib/customisation.nix
index 234a528527d3..cc9a9b1c55d0 100644
--- a/nixpkgs/lib/customisation.nix
+++ b/nixpkgs/lib/customisation.nix
@@ -117,8 +117,55 @@ rec {
   callPackageWith = autoArgs: fn: args:
     let
       f = if lib.isFunction fn then fn else import fn;
-      auto = builtins.intersectAttrs (lib.functionArgs f) autoArgs;
-    in makeOverridable f (auto // args);
+      fargs = lib.functionArgs f;
+
+      # All arguments that will be passed to the function
+      # This includes automatic ones and ones passed explicitly
+      allArgs = builtins.intersectAttrs fargs autoArgs // args;
+
+      # A list of argument names that the function requires, but
+      # wouldn't be passed to it
+      missingArgs = lib.attrNames
+        # Filter out arguments that have a default value
+        (lib.filterAttrs (name: value: ! value)
+        # Filter out arguments that would be passed
+        (removeAttrs fargs (lib.attrNames allArgs)));
+
+      # Get a list of suggested argument names for a given missing one
+      getSuggestions = arg: lib.pipe (autoArgs // args) [
+        lib.attrNames
+        # Only use ones that are at most 2 edits away. While mork would work,
+        # levenshteinAtMost is only fast for 2 or less.
+        (lib.filter (lib.strings.levenshteinAtMost 2 arg))
+        # Put strings with shorter distance first
+        (lib.sort (x: y: lib.strings.levenshtein x arg < lib.strings.levenshtein y arg))
+        # Only take the first couple results
+        (lib.take 3)
+        # Quote all entries
+        (map (x: "\"" + x + "\""))
+      ];
+
+      prettySuggestions = suggestions:
+        if suggestions == [] then ""
+        else if lib.length suggestions == 1 then ", did you mean ${lib.elemAt suggestions 0}?"
+        else ", did you mean ${lib.concatStringsSep ", " (lib.init suggestions)} or ${lib.last suggestions}?";
+
+      errorForArg = arg:
+        let
+          loc = builtins.unsafeGetAttrPos arg fargs;
+          # loc' can be removed once lib/minver.nix is >2.3.4, since that includes
+          # https://github.com/NixOS/nix/pull/3468 which makes loc be non-null
+          loc' = if loc != null then loc.file + ":" + toString loc.line
+            else if ! lib.isFunction fn then
+              toString fn + lib.optionalString (lib.sources.pathIsDirectory fn) "/default.nix"
+            else "<unknown location>";
+        in "Function called without required argument \"${arg}\" at "
+        + "${loc'}${prettySuggestions (getSuggestions arg)}";
+
+      # Only show the error for the first missing argument
+      error = errorForArg (lib.head missingArgs);
+
+    in if missingArgs == [] then makeOverridable f allArgs else throw error;
 
 
   /* Like callPackage, but for a function that returns an attribute
diff --git a/nixpkgs/lib/default.nix b/nixpkgs/lib/default.nix
index 1f06283790a8..e2a93e63ac1f 100644
--- a/nixpkgs/lib/default.nix
+++ b/nixpkgs/lib/default.nix
@@ -36,6 +36,7 @@ let
 
     # constants
     licenses = callLibs ./licenses.nix;
+    sourceTypes = callLibs ./source-types.nix;
     systems = callLibs ./systems;
 
     # serialization
@@ -70,7 +71,7 @@ let
       info showWarnings nixpkgsVersion version isInOldestRelease
       mod compare splitByAndCompare
       functionArgs setFunctionArgs isFunction toFunction
-      toHexString toBaseDigits;
+      toHexString toBaseDigits inPureEvalMode;
     inherit (self.fixedPoints) fix fix' converge extends composeExtensions
       composeManyExtensions makeExtensible makeExtensibleWithCustomName;
     inherit (self.attrsets) attrByPath hasAttrByPath setAttrByPath
@@ -94,7 +95,8 @@ let
       concatImapStringsSep makeSearchPath makeSearchPathOutput
       makeLibraryPath makeBinPath optionalString
       hasInfix hasPrefix hasSuffix stringToCharacters stringAsChars escape
-      escapeShellArg escapeShellArgs escapeRegex escapeXML replaceChars lowerChars
+      escapeShellArg escapeShellArgs isValidPosixName toShellVar toShellVars
+      escapeRegex escapeXML replaceChars lowerChars
       upperChars toLower toUpper addContextFrom splitString
       removePrefix removeSuffix versionOlder versionAtLeast
       getName getVersion
@@ -108,7 +110,7 @@ let
       makeScope makeScopeWithSplicing;
     inherit (self.meta) addMetaAttrs dontDistribute setName updateName
       appendToName mapDerivationAttrset setPrio lowPrio lowPrioSet hiPrio
-      hiPrioSet getLicenseFromSpdxId;
+      hiPrioSet getLicenseFromSpdxId getExe;
     inherit (self.sources) pathType pathIsDirectory cleanSourceFilter
       cleanSource sourceByRegex sourceFilesBySuffices
       commitIdFromGitRepo cleanSourceWith pathHasContext
@@ -129,7 +131,9 @@ let
       getValues getFiles
       optionAttrSetToDocList optionAttrSetToDocList'
       scrubOptionValue literalExpression literalExample literalDocBook
-      showOption showFiles unknownModule mkOption mkPackageOption;
+      showOption showOptionWithDefLocs showFiles
+      unknownModule mkOption mkPackageOption
+      mdDoc literalMD;
     inherit (self.types) isType setType defaultTypeMerge defaultFunctor
       isOptionType mkOptionType;
     inherit (self.asserts)
diff --git a/nixpkgs/lib/generators.nix b/nixpkgs/lib/generators.nix
index 79ae9055ce3d..431b93c4ebbc 100644
--- a/nixpkgs/lib/generators.nix
+++ b/nixpkgs/lib/generators.nix
@@ -10,7 +10,7 @@
  * are mostly generators themselves, called with
  * their respective default values; they can be reused.
  *
- * Tests can be found in ./tests.nix
+ * Tests can be found in ./tests/misc.nix
  * Documentation in the manual, #sec-generators
  */
 { lib }:
@@ -108,7 +108,7 @@ rec {
    * The mk* configuration attributes can generically change
    * the way sections and key-value strings are generated.
    *
-   * For more examples see the test cases in ./tests.nix.
+   * For more examples see the test cases in ./tests/misc.nix.
    */
   toINI = {
     # apply transformations (e.g. escapes) to section names
@@ -130,6 +130,51 @@ rec {
       # map input to ini sections
       mapAttrsToStringsSep "\n" mkSection attrsOfAttrs;
 
+  /* Generate an INI-style config file from an attrset
+   * specifying the global section (no header), and an
+   * attrset of sections to an attrset of key-value pairs.
+   *
+   * generators.toINIWithGlobalSection {} {
+   *   globalSection = {
+   *     someGlobalKey = "hi";
+   *   };
+   *   sections = {
+   *     foo = { hi = "${pkgs.hello}"; ciao = "bar"; };
+   *     baz = { "also, integers" = 42; };
+   * }
+   *
+   *> someGlobalKey=hi
+   *>
+   *> [baz]
+   *> also, integers=42
+   *>
+   *> [foo]
+   *> ciao=bar
+   *> hi=/nix/store/y93qql1p5ggfnaqjjqhxcw0vqw95rlz0-hello-2.10
+   *
+   * The mk* configuration attributes can generically change
+   * the way sections and key-value strings are generated.
+   *
+   * For more examples see the test cases in ./tests/misc.nix.
+   *
+   * If you don’t need a global section, you can also use
+   * `generators.toINI` directly, which only takes
+   * the part in `sections`.
+   */
+  toINIWithGlobalSection = {
+    # apply transformations (e.g. escapes) to section names
+    mkSectionName ? (name: libStr.escape [ "[" "]" ] name),
+    # format a setting line from key and value
+    mkKeyValue    ? mkKeyValueDefault {} "=",
+    # allow lists as values for duplicate keys
+    listsAsDuplicateKeys ? false
+  }: { globalSection, sections }:
+    ( if globalSection == {}
+      then ""
+      else (toKeyValue { inherit mkKeyValue listsAsDuplicateKeys; } globalSection)
+           + "\n")
+    + (toINI { inherit mkSectionName mkKeyValue listsAsDuplicateKeys; } sections);
+
   /* Generate a git-config file from an attrset.
    *
    * It has two major differences from the regular INI format:
@@ -206,6 +251,16 @@ rec {
     }:
       assert builtins.isInt depthLimit;
       let
+        specialAttrs = [
+          "__functor"
+          "__functionArgs"
+          "__toString"
+          "__pretty"
+        ];
+        stepIntoAttr = evalNext: name:
+          if builtins.elem name specialAttrs
+            then id
+            else evalNext;
         transform = depth:
           if depthLimit != null && depth > depthLimit then
             if throwOnDepthLimit
@@ -216,7 +271,7 @@ rec {
           let
             evalNext = x: mapAny (depth + 1) (transform (depth + 1) x);
           in
-            if isAttrs v then mapAttrs (const evalNext) v
+            if isAttrs v then mapAttrs (stepIntoAttr evalNext) v
             else if isList v then map evalNext v
             else transform (depth + 1) v;
       in
diff --git a/nixpkgs/lib/licenses.nix b/nixpkgs/lib/licenses.nix
index 2928d11d8b22..56299612a0e6 100644
--- a/nixpkgs/lib/licenses.nix
+++ b/nixpkgs/lib/licenses.nix
@@ -55,6 +55,12 @@ in mkLicense lset) ({
     fullName = "GNU Affero General Public License v3.0 or later";
   };
 
+  aladdin = {
+    spdxId = "Aladdin";
+    fullName = "Aladdin Free Public License";
+    free = false;
+  };
+
   amazonsl = {
     fullName = "Amazon Software License";
     url = "https://aws.amazon.com/asl/";
@@ -285,6 +291,11 @@ in mkLicense lset) ({
     fullName = "DOC License";
   };
 
+  drl10 = {
+    spdxId = "DRL-1.0";
+    fullName = "Detection Rule License 1.0";
+  };
+
   eapl = {
     fullName = "EPSON AVASYS PUBLIC LICENSE";
     url = "https://avasys.jp/hp/menu000000700/hpg000000603.htm";
@@ -509,6 +520,13 @@ in mkLicense lset) ({
     free = false;
   };
 
+  databricks-dbx = {
+    fullName = "DataBricks eXtensions aka dbx License";
+    url = "https://github.com/databrickslabs/dbx/blob/743b579a4ac44531f764c6e522dbe5a81a7dc0e4/LICENSE";
+    free = false;
+    redistributable = false;
+  };
+
   issl = {
     fullName = "Intel Simplified Software License";
     url = "https://software.intel.com/en-us/license/intel-simplified-software-license";
diff --git a/nixpkgs/lib/lists.nix b/nixpkgs/lib/lists.nix
index a030280c8dcc..36056e18065e 100644
--- a/nixpkgs/lib/lists.nix
+++ b/nixpkgs/lib/lists.nix
@@ -507,7 +507,7 @@ rec {
        compareLists compare [ "a" ] []
        => 1
        compareLists compare [ "a" "b" ] [ "a" "c" ]
-       => 1
+       => -1
   */
   compareLists = cmp: a: b:
     if a == []
diff --git a/nixpkgs/lib/meta.nix b/nixpkgs/lib/meta.nix
index 5b1f7ee5ff2d..74b94211552b 100644
--- a/nixpkgs/lib/meta.nix
+++ b/nixpkgs/lib/meta.nix
@@ -126,4 +126,18 @@ rec {
         lib.warn "getLicenseFromSpdxId: No license matches the given SPDX ID: ${licstr}"
         { shortName = licstr; }
       );
+
+  /* Get the path to the main program of a derivation with either
+     meta.mainProgram or pname or name
+
+     Type: getExe :: derivation -> string
+
+     Example:
+       getExe pkgs.hello
+       => "/nix/store/g124820p9hlv4lj8qplzxw1c44dxaw1k-hello-2.12/bin/hello"
+       getExe pkgs.mustache-go
+       => "/nix/store/am9ml4f4ywvivxnkiaqwr0hyxka1xjsf-mustache-go-1.3.0/bin/mustache"
+  */
+  getExe = x:
+    "${lib.getBin x}/bin/${x.meta.mainProgram or (lib.getName x)}";
 }
diff --git a/nixpkgs/lib/modules.nix b/nixpkgs/lib/modules.nix
index 894104cc5790..7f1646e9b8bc 100644
--- a/nixpkgs/lib/modules.nix
+++ b/nixpkgs/lib/modules.nix
@@ -46,6 +46,7 @@ let
     showFiles
     showOption
     unknownModule
+    literalExpression
     ;
 
   showDeclPrefix = loc: decl: prefix:
@@ -140,7 +141,7 @@ rec {
       # this module is used, to avoid conflicts and allow chaining of
       # extendModules.
       internalModule = rec {
-        _file = ./modules.nix;
+        _file = "lib/modules.nix";
 
         key = _file;
 
@@ -153,8 +154,94 @@ rec {
             # a `_module.args.pkgs = import (fetchTarball { ... }) {}` won't
             # start a download when `pkgs` wasn't evaluated.
             type = types.lazyAttrsOf types.raw;
-            internal = true;
-            description = "Arguments passed to each module.";
+            # Only render documentation once at the root of the option tree,
+            # not for all individual submodules.
+            # Allow merging option decls to make this internal regardless.
+            ${if prefix == []
+              then null  # unset => visible
+              else "internal"} = true;
+            # TODO: Change the type of this option to a submodule with a
+            # freeformType, so that individual arguments can be documented
+            # separately
+            description = ''
+              Additional arguments passed to each module in addition to ones
+              like <literal>lib</literal>, <literal>config</literal>,
+              and <literal>pkgs</literal>, <literal>modulesPath</literal>.
+              </para>
+              <para>
+              This option is also available to all submodules. Submodules do not
+              inherit args from their parent module, nor do they provide args to
+              their parent module or sibling submodules. The sole exception to
+              this is the argument <literal>name</literal> which is provided by
+              parent modules to a submodule and contains the attribute name
+              the submodule is bound to, or a unique generated name if it is
+              not bound to an attribute.
+              </para>
+              <para>
+              Some arguments are already passed by default, of which the
+              following <emphasis>cannot</emphasis> be changed with this option:
+              <itemizedlist>
+               <listitem>
+                <para>
+                 <varname>lib</varname>: The nixpkgs library.
+                </para>
+               </listitem>
+               <listitem>
+                <para>
+                 <varname>config</varname>: The results of all options after merging the values from all modules together.
+                </para>
+               </listitem>
+               <listitem>
+                <para>
+                 <varname>options</varname>: The options declared in all modules.
+                </para>
+               </listitem>
+               <listitem>
+                <para>
+                 <varname>specialArgs</varname>: The <literal>specialArgs</literal> argument passed to <literal>evalModules</literal>.
+                </para>
+               </listitem>
+               <listitem>
+                <para>
+                 All attributes of <varname>specialArgs</varname>
+                </para>
+                <para>
+                 Whereas option values can generally depend on other option values
+                 thanks to laziness, this does not apply to <literal>imports</literal>, which
+                 must be computed statically before anything else.
+                </para>
+                <para>
+                 For this reason, callers of the module system can provide <literal>specialArgs</literal>
+                 which are available during import resolution.
+                </para>
+                <para>
+                 For NixOS, <literal>specialArgs</literal> includes
+                 <varname>modulesPath</varname>, which allows you to import
+                 extra modules from the nixpkgs package tree without having to
+                 somehow make the module aware of the location of the
+                 <literal>nixpkgs</literal> or NixOS directories.
+              <programlisting>
+              { modulesPath, ... }: {
+                imports = [
+                  (modulesPath + "/profiles/minimal.nix")
+                ];
+              }
+              </programlisting>
+                </para>
+               </listitem>
+              </itemizedlist>
+              </para>
+              <para>
+              For NixOS, the default value for this option includes at least this argument:
+              <itemizedlist>
+               <listitem>
+                <para>
+                 <varname>pkgs</varname>: The nixpkgs package set according to
+                 the <option>nixpkgs.pkgs</option> option.
+                </para>
+               </listitem>
+              </itemizedlist>
+            '';
           };
 
           _module.check = mkOption {
@@ -179,6 +266,15 @@ rec {
               turned off.
             '';
           };
+
+          _module.specialArgs = mkOption {
+            readOnly = true;
+            internal = true;
+            description = ''
+              Externally provided module arguments that can't be modified from
+              within a configuration, but can be used in module imports.
+            '';
+          };
         };
 
         config = {
@@ -186,6 +282,7 @@ rec {
             inherit extendModules;
             moduleType = type;
           };
+          _module.specialArgs = specialArgs;
         };
       };
 
@@ -258,7 +355,7 @@ rec {
           evalModules (evalModulesArgs // {
             modules = regularModules ++ modules;
             specialArgs = evalModulesArgs.specialArgs or {} // specialArgs;
-            prefix = extendArgs.prefix or evalModulesArgs.prefix;
+            prefix = extendArgs.prefix or evalModulesArgs.prefix or [];
           });
 
       type = lib.types.submoduleWith {
@@ -375,6 +472,7 @@ rec {
           config = addFreeformType (addMeta (m.config or {}));
         }
     else
+      lib.throwIfNot (isAttrs m) "module ${file} (${key}) does not look like a module."
       { _file = toString m._file or file;
         key = toString m.key or key;
         disabledModules = m.disabledModules or [];
diff --git a/nixpkgs/lib/options.nix b/nixpkgs/lib/options.nix
index 8d0801775c46..84d9fd5e15de 100644
--- a/nixpkgs/lib/options.nix
+++ b/nixpkgs/lib/options.nix
@@ -7,6 +7,7 @@ let
     collect
     concatLists
     concatMap
+    concatMapStringsSep
     elemAt
     filter
     foldl'
@@ -120,7 +121,7 @@ rec {
      Example:
        mkPackageOption pkgs "GHC" {
          default = [ "ghc" ];
-         example = "pkgs.haskell.package.ghc921.ghc.withPackages (hkgs: [ hkgs.primes ])";
+         example = "pkgs.haskell.packages.ghc924.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 = { ... }; }
   */
@@ -241,6 +242,8 @@ rec {
           in if ss != {} then optionAttrSetToDocList' opt.loc ss else [];
         subOptionsVisible = docOption.visible && opt.visible or null != "shallow";
       in
+        # To find infinite recursion in NixOS option docs:
+        # builtins.trace opt.loc
         [ docOption ] ++ optionals subOptionsVisible subOptions) (collect isOption options);
 
 
@@ -280,6 +283,21 @@ rec {
     if ! isString text then throw "literalDocBook expects a string."
     else { _type = "literalDocBook"; inherit text; };
 
+  /* Transition marker for documentation that's already migrated to markdown
+     syntax.
+  */
+  mdDoc = text:
+    if ! isString text then throw "mdDoc expects a string."
+    else { _type = "mdDoc"; inherit text; };
+
+  /* For use in the `defaultText` and `example` option attributes. Causes the
+     given MD text to be inserted verbatim in the documentation, for when
+     a `literalExpression` would be too hard to read.
+  */
+  literalMD = text:
+    if ! isString text then throw "literalMD expects a string."
+    else { _type = "literalMD"; inherit text; };
+
   # Helper functions.
 
   /* Convert an option, described as a list of the option parts in to a
@@ -325,6 +343,11 @@ rec {
     in "\n- In `${def.file}'${result}"
   ) defs;
 
+  showOptionWithDefLocs = opt: ''
+      ${showOption opt.loc}, with values defined in:
+      ${concatMapStringsSep "\n" (defFile: "  - ${defFile}") opt.files}
+    '';
+
   unknownModule = "<unknown-file>";
 
 }
diff --git a/nixpkgs/lib/source-types.nix b/nixpkgs/lib/source-types.nix
new file mode 100644
index 000000000000..c4f263dcf464
--- /dev/null
+++ b/nixpkgs/lib/source-types.nix
@@ -0,0 +1,19 @@
+{ lib }:
+
+let
+  defaultSourceType = tname: {
+    shortName = tname;
+    isSource = false;
+  };
+in lib.mapAttrs (tname: tset: defaultSourceType tname // tset) {
+
+  fromSource = {
+    isSource = true;
+  };
+
+  binaryNativeCode = {};
+
+  binaryBytecode = {};
+
+  binaryFirmware = {};
+}
diff --git a/nixpkgs/lib/strings.nix b/nixpkgs/lib/strings.nix
index b2fd495e4c84..295d98900e99 100644
--- a/nixpkgs/lib/strings.nix
+++ b/nixpkgs/lib/strings.nix
@@ -17,6 +17,7 @@ rec {
     head
     isInt
     isList
+    isAttrs
     isString
     match
     parseDrvName
@@ -253,10 +254,7 @@ rec {
       => false
   */
   hasInfix = infix: content:
-    let
-      drop = x: substring 1 (stringLength x) x;
-    in hasPrefix infix content
-      || content != "" && hasInfix infix (drop content);
+    builtins.match ".*${escapeRegex infix}.*" "${content}" != null;
 
   /* Convert a string to a list of characters (i.e. singleton strings).
      This allows you to, e.g., map a function over each character.  However,
@@ -327,6 +325,66 @@ rec {
   */
   escapeShellArgs = concatMapStringsSep " " escapeShellArg;
 
+  /* Test whether the given name is a valid POSIX shell variable name.
+
+     Type: string -> bool
+
+     Example:
+       isValidPosixName "foo_bar000"
+       => true
+       isValidPosixName "0-bad.jpg"
+       => false
+  */
+  isValidPosixName = name: match "[a-zA-Z_][a-zA-Z0-9_]*" name != null;
+
+  /* Translate a Nix value into a shell variable declaration, with proper escaping.
+
+     The value can be a string (mapped to a regular variable), a list of strings
+     (mapped to a Bash-style array) or an attribute set of strings (mapped to a
+     Bash-style associative array). Note that "string" includes string-coercible
+     values like paths or derivations.
+
+     Strings are translated into POSIX sh-compatible code; lists and attribute sets
+     assume a shell that understands Bash syntax (e.g. Bash or ZSH).
+
+     Type: string -> (string | listOf string | attrsOf string) -> string
+
+     Example:
+       ''
+         ${toShellVar "foo" "some string"}
+         [[ "$foo" == "some string" ]]
+       ''
+  */
+  toShellVar = name: value:
+    lib.throwIfNot (isValidPosixName name) "toShellVar: ${name} is not a valid shell variable name" (
+    if isAttrs value && ! isCoercibleToString value then
+      "declare -A ${name}=(${
+        concatStringsSep " " (lib.mapAttrsToList (n: v:
+          "[${escapeShellArg n}]=${escapeShellArg v}"
+        ) value)
+      })"
+    else if isList value then
+      "declare -a ${name}=(${escapeShellArgs value})"
+    else
+      "${name}=${escapeShellArg value}"
+    );
+
+  /* Translate an attribute set into corresponding shell variable declarations
+     using `toShellVar`.
+
+     Type: attrsOf (string | listOf string | attrsOf string) -> string
+
+     Example:
+       let
+         foo = "value";
+         bar = foo;
+       in ''
+         ${toShellVars { inherit foo bar; }}
+         [[ "$foo" == "$bar" ]]
+       ''
+  */
+  toShellVars = vars: concatStringsSep "\n" (lib.mapAttrsToList toShellVar vars);
+
   /* Turn a string into a Nix expression representing that string
 
      Type: string -> string
@@ -756,7 +814,14 @@ rec {
        sanitizeDerivationName pkgs.hello
        => "-nix-store-2g75chlbpxlrqn15zlby2dfh8hr9qwbk-hello-2.10"
   */
-  sanitizeDerivationName = string: lib.pipe string [
+  sanitizeDerivationName =
+  let okRegex = match "[[:alnum:]+_?=-][[:alnum:]+._?=-]*";
+  in
+  string:
+  # First detect the common case of already valid strings, to speed those up
+  if stringLength string <= 207 && okRegex string != null
+  then unsafeDiscardStringContext string
+  else lib.pipe string [
     # Get rid of string context. This is safe under the assumption that the
     # resulting string is only used as a derivation name
     unsafeDiscardStringContext
@@ -774,4 +839,131 @@ rec {
     (x: if stringLength x == 0 then "unknown" else x)
   ];
 
+  /* Computes the Levenshtein distance between two strings.
+     Complexity O(n*m) where n and m are the lengths of the strings.
+     Algorithm adjusted from https://stackoverflow.com/a/9750974/6605742
+
+     Type: levenshtein :: string -> string -> int
+
+     Example:
+       levenshtein "foo" "foo"
+       => 0
+       levenshtein "book" "hook"
+       => 1
+       levenshtein "hello" "Heyo"
+       => 3
+  */
+  levenshtein = a: b: let
+    # Two dimensional array with dimensions (stringLength a + 1, stringLength b + 1)
+    arr = lib.genList (i:
+      lib.genList (j:
+        dist i j
+      ) (stringLength b + 1)
+    ) (stringLength a + 1);
+    d = x: y: lib.elemAt (lib.elemAt arr x) y;
+    dist = i: j:
+      let c = if substring (i - 1) 1 a == substring (j - 1) 1 b
+        then 0 else 1;
+      in
+      if j == 0 then i
+      else if i == 0 then j
+      else lib.min
+        ( lib.min (d (i - 1) j + 1) (d i (j - 1) + 1))
+        ( d (i - 1) (j - 1) + c );
+  in d (stringLength a) (stringLength b);
+
+  /* Returns the length of the prefix common to both strings.
+  */
+  commonPrefixLength = a: b:
+    let
+      m = lib.min (stringLength a) (stringLength b);
+      go = i: if i >= m then m else if substring i 1 a == substring i 1 b then go (i + 1) else i;
+    in go 0;
+
+  /* Returns the length of the suffix common to both strings.
+  */
+  commonSuffixLength = a: b:
+    let
+      m = lib.min (stringLength a) (stringLength b);
+      go = i: if i >= m then m else if substring (stringLength a - i - 1) 1 a == substring (stringLength b - i - 1) 1 b then go (i + 1) else i;
+    in go 0;
+
+  /* Returns whether the levenshtein distance between two strings is at most some value
+     Complexity is O(min(n,m)) for k <= 2 and O(n*m) otherwise
+
+     Type: levenshteinAtMost :: int -> string -> string -> bool
+
+     Example:
+       levenshteinAtMost 0 "foo" "foo"
+       => true
+       levenshteinAtMost 1 "foo" "boa"
+       => false
+       levenshteinAtMost 2 "foo" "boa"
+       => true
+       levenshteinAtMost 2 "This is a sentence" "this is a sentense."
+       => false
+       levenshteinAtMost 3 "This is a sentence" "this is a sentense."
+       => true
+
+  */
+  levenshteinAtMost = let
+    infixDifferAtMost1 = x: y: stringLength x <= 1 && stringLength y <= 1;
+
+    # This function takes two strings stripped by their common pre and suffix,
+    # and returns whether they differ by at most two by Levenshtein distance.
+    # Because of this stripping, if they do indeed differ by at most two edits,
+    # we know that those edits were (if at all) done at the start or the end,
+    # while the middle has to have stayed the same. This fact is used in the
+    # implementation.
+    infixDifferAtMost2 = x: y:
+      let
+        xlen = stringLength x;
+        ylen = stringLength y;
+        # This function is only called with |x| >= |y| and |x| - |y| <= 2, so
+        # diff is one of 0, 1 or 2
+        diff = xlen - ylen;
+
+        # Infix of x and y, stripped by the left and right most character
+        xinfix = substring 1 (xlen - 2) x;
+        yinfix = substring 1 (ylen - 2) y;
+
+        # x and y but a character deleted at the left or right
+        xdelr = substring 0 (xlen - 1) x;
+        xdell = substring 1 (xlen - 1) x;
+        ydelr = substring 0 (ylen - 1) y;
+        ydell = substring 1 (ylen - 1) y;
+      in
+        # A length difference of 2 can only be gotten with 2 delete edits,
+        # which have to have happened at the start and end of x
+        # Example: "abcdef" -> "bcde"
+        if diff == 2 then xinfix == y
+        # A length difference of 1 can only be gotten with a deletion on the
+        # right and a replacement on the left or vice versa.
+        # Example: "abcdef" -> "bcdez" or "zbcde"
+        else if diff == 1 then xinfix == ydelr || xinfix == ydell
+        # No length difference can either happen through replacements on both
+        # sides, or a deletion on the left and an insertion on the right or
+        # vice versa
+        # Example: "abcdef" -> "zbcdez" or "bcdefz" or "zabcde"
+        else xinfix == yinfix || xdelr == ydell || xdell == ydelr;
+
+    in k: if k <= 0 then a: b: a == b else
+      let f = a: b:
+        let
+          alen = stringLength a;
+          blen = stringLength b;
+          prelen = commonPrefixLength a b;
+          suflen = commonSuffixLength a b;
+          presuflen = prelen + suflen;
+          ainfix = substring prelen (alen - presuflen) a;
+          binfix = substring prelen (blen - presuflen) b;
+        in
+        # Make a be the bigger string
+        if alen < blen then f b a
+        # If a has over k more characters than b, even with k deletes on a, b can't be reached
+        else if alen - blen > k then false
+        else if k == 1 then infixDifferAtMost1 ainfix binfix
+        else if k == 2 then infixDifferAtMost2 ainfix binfix
+        else levenshtein ainfix binfix <= k;
+      in f;
 }
diff --git a/nixpkgs/lib/systems/default.nix b/nixpkgs/lib/systems/default.nix
index 7ddd5b8a5812..2990afde3e9a 100644
--- a/nixpkgs/lib/systems/default.nix
+++ b/nixpkgs/lib/systems/default.nix
@@ -8,7 +8,13 @@ rec {
   platforms = import ./platforms.nix { inherit lib; };
   examples = import ./examples.nix { inherit lib; };
   architectures = import ./architectures.nix { inherit lib; };
-  supported = import ./supported.nix { inherit lib; };
+
+  /* List of all Nix system doubles the nixpkgs flake will expose the package set
+     for. All systems listed here must be supported by nixpkgs as `localSystem`.
+
+     **Warning**: This attribute is considered experimental and is subject to change.
+  */
+  flakeExposed = import ./flake-systems.nix { };
 
   # Elaborate a `localSystem` or `crossSystem` so that it contains everything
   # necessary.
@@ -25,8 +31,12 @@ rec {
       # Either of these can be losslessly-extracted from `parsed` iff parsing succeeds.
       system = parse.doubleFromSystem final.parsed;
       config = parse.tripleFromSystem final.parsed;
-      # Determine whether we are compatible with the provided CPU
-      isCompatible = platform: parse.isCompatible final.parsed.cpu platform.parsed.cpu;
+      # Determine whether we can execute binaries built for the provided platform.
+      canExecute = platform:
+        final.isAndroid == platform.isAndroid &&
+        parse.isCompatible final.parsed.cpu platform.parsed.cpu
+        && final.parsed.kernel == platform.parsed.kernel;
+      isCompatible = _: throw "2022-05-23: isCompatible has been removed in favor of canExecute, refer to the 22.11 changelog for details";
       # Derived meta-data
       libc =
         /**/ if final.isDarwin              then "libSystem"
@@ -159,7 +169,7 @@ rec {
         wine = (pkgs.winePackagesFor wine-name).minimal;
       in
         if final.parsed.kernel.name == pkgs.stdenv.hostPlatform.parsed.kernel.name &&
-           pkgs.stdenv.hostPlatform.isCompatible final
+           pkgs.stdenv.hostPlatform.canExecute final
         then "${pkgs.runtimeShell} -c '\"$@\"' --"
         else if final.isWindows
         then "${wine}/bin/${wine-name}"
diff --git a/nixpkgs/lib/systems/doubles.nix b/nixpkgs/lib/systems/doubles.nix
index 27cdaf6a7233..90a6eb9f35c9 100644
--- a/nixpkgs/lib/systems/doubles.nix
+++ b/nixpkgs/lib/systems/doubles.nix
@@ -41,7 +41,7 @@ let
     # none
     "aarch64_be-none" "aarch64-none" "arm-none" "armv6l-none" "avr-none" "i686-none"
     "msp430-none" "or1k-none" "m68k-none" "powerpc-none" "powerpcle-none"
-    "riscv32-none" "riscv64-none" "s390-none" "s390x-none" "vc4-none"
+    "riscv32-none" "riscv64-none" "rx-none" "s390-none" "s390x-none" "vc4-none"
     "x86_64-none"
 
     # OpenBSD
@@ -74,6 +74,9 @@ in {
   mips          = filterDoubles predicates.isMips;
   mmix          = filterDoubles predicates.isMmix;
   riscv         = filterDoubles predicates.isRiscV;
+  riscv32       = filterDoubles predicates.isRiscV32;
+  riscv64       = filterDoubles predicates.isRiscV64;
+  rx            = filterDoubles predicates.isRx;
   vc4           = filterDoubles predicates.isVc4;
   or1k          = filterDoubles predicates.isOr1k;
   m68k          = filterDoubles predicates.isM68k;
diff --git a/nixpkgs/lib/systems/examples.nix b/nixpkgs/lib/systems/examples.nix
index 997a7a8c273a..65dc9c07e346 100644
--- a/nixpkgs/lib/systems/examples.nix
+++ b/nixpkgs/lib/systems/examples.nix
@@ -57,30 +57,28 @@ rec {
   armv7a-android-prebuilt = {
     config = "armv7a-unknown-linux-androideabi";
     rustc.config = "armv7-linux-androideabi";
-    sdkVer = "29";
-    ndkVer = "21";
+    sdkVer = "28";
+    ndkVer = "24";
     useAndroidPrebuilt = true;
   } // platforms.armv7a-android;
 
   aarch64-android-prebuilt = {
     config = "aarch64-unknown-linux-android";
     rustc.config = "aarch64-linux-android";
-    sdkVer = "29";
-    ndkVer = "21";
+    sdkVer = "28";
+    ndkVer = "24";
     useAndroidPrebuilt = true;
   };
 
   aarch64-android = {
     config = "aarch64-unknown-linux-android";
     sdkVer = "30";
-    ndkVer = "21";
+    ndkVer = "24";
     libc = "bionic";
     useAndroidPrebuilt = false;
     useLLVM = true;
   };
 
-  scaleway-c1 = armv7l-hf-multiplatform // platforms.scaleway-c1;
-
   pogoplug4 = {
     config = "armv5tel-unknown-linux-gnueabi";
   } // platforms.pogoplug4;
@@ -93,25 +91,23 @@ rec {
     config = "mipsel-unknown-linux-gnu";
   } // platforms.fuloong2f_n32;
 
-  # MIPS ABI table transcribed from here: https://wiki.debian.org/Multiarch/Tuples
-
   # can execute on 32bit chip
-  mips-linux-gnu                = { config = "mips-linux-gnu";                } // platforms.gcc_mips32r2_o32;
-  mipsel-linux-gnu              = { config = "mipsel-linux-gnu";              } // platforms.gcc_mips32r2_o32;
-  mipsisa32r6-linux-gnu         = { config = "mipsisa32r6-linux-gnu";         } // platforms.gcc_mips32r6_o32;
-  mipsisa32r6el-linux-gnu       = { config = "mipsisa32r6el-linux-gnu";       } // platforms.gcc_mips32r6_o32;
+  mips-linux-gnu                = { config = "mips-unknown-linux-gnu";                } // platforms.gcc_mips32r2_o32;
+  mipsel-linux-gnu              = { config = "mipsel-unknown-linux-gnu";              } // platforms.gcc_mips32r2_o32;
+  mipsisa32r6-linux-gnu         = { config = "mipsisa32r6-unknown-linux-gnu";         } // platforms.gcc_mips32r6_o32;
+  mipsisa32r6el-linux-gnu       = { config = "mipsisa32r6el-unknown-linux-gnu";       } // platforms.gcc_mips32r6_o32;
 
   # require 64bit chip (for more registers, 64-bit floating point, 64-bit "long long") but use 32bit pointers
-  mips64-linux-gnuabin32        = { config = "mips64-linux-gnuabin32";        } // platforms.gcc_mips64r2_n32;
-  mips64el-linux-gnuabin32      = { config = "mips64el-linux-gnuabin32";      } // platforms.gcc_mips64r2_n32;
-  mipsisa64r6-linux-gnuabin32   = { config = "mipsisa64r6-linux-gnuabin32";   } // platforms.gcc_mips64r6_n32;
-  mipsisa64r6el-linux-gnuabin32 = { config = "mipsisa64r6el-linux-gnuabin32"; } // platforms.gcc_mips64r6_n32;
+  mips64-linux-gnuabin32        = { config = "mips64-unknown-linux-gnuabin32";        } // platforms.gcc_mips64r2_n32;
+  mips64el-linux-gnuabin32      = { config = "mips64el-unknown-linux-gnuabin32";      } // platforms.gcc_mips64r2_n32;
+  mipsisa64r6-linux-gnuabin32   = { config = "mipsisa64r6-unknown-linux-gnuabin32";   } // platforms.gcc_mips64r6_n32;
+  mipsisa64r6el-linux-gnuabin32 = { config = "mipsisa64r6el-unknown-linux-gnuabin32"; } // platforms.gcc_mips64r6_n32;
 
   # 64bit pointers
-  mips64-linux-gnuabi64         = { config = "mips64-linux-gnuabi64";         } // platforms.gcc_mips64r2_64;
-  mips64el-linux-gnuabi64       = { config = "mips64el-linux-gnuabi64";       } // platforms.gcc_mips64r2_64;
-  mipsisa64r6-linux-gnuabi64    = { config = "mipsisa64r6-linux-gnuabi64";    } // platforms.gcc_mips64r6_64;
-  mipsisa64r6el-linux-gnuabi64  = { config = "mipsisa64r6el-linux-gnuabi64";  } // platforms.gcc_mips64r6_64;
+  mips64-linux-gnuabi64         = { config = "mips64-unknown-linux-gnuabi64";         } // platforms.gcc_mips64r2_64;
+  mips64el-linux-gnuabi64       = { config = "mips64el-unknown-linux-gnuabi64";       } // platforms.gcc_mips64r2_64;
+  mipsisa64r6-linux-gnuabi64    = { config = "mipsisa64r6-unknown-linux-gnuabi64";    } // platforms.gcc_mips64r6_64;
+  mipsisa64r6el-linux-gnuabi64  = { config = "mipsisa64r6el-unknown-linux-gnuabi64";  } // platforms.gcc_mips64r6_64;
 
   muslpi = raspberryPi // {
     config = "armv6l-unknown-linux-musleabihf";
@@ -145,6 +141,11 @@ rec {
     libc = "newlib";
   };
 
+  rx-embedded = {
+    config = "rx-none-elf";
+    libc = "newlib";
+  };
+
   msp430 = {
     config = "msp430-elf";
     libc = "newlib";
@@ -303,8 +304,6 @@ rec {
 
   # BSDs
 
-  amd64-netbsd = lib.warn "The amd64-netbsd system example is deprecated. Use x86_64-netbsd instead." x86_64-netbsd;
-
   x86_64-netbsd = {
     config = "x86_64-unknown-netbsd";
     libc = "nblibc";
diff --git a/nixpkgs/lib/systems/flake-systems.nix b/nixpkgs/lib/systems/flake-systems.nix
new file mode 100644
index 000000000000..74124c32e836
--- /dev/null
+++ b/nixpkgs/lib/systems/flake-systems.nix
@@ -0,0 +1,29 @@
+# See [RFC 46] for mandated platform support and ../../pkgs/stdenv for
+# implemented platform support. This list is mainly descriptive, i.e. all
+# system doubles for platforms where nixpkgs can do native compiliation
+# reasonably well are included.
+#
+# [RFC 46]: https://github.com/NixOS/rfcs/blob/master/rfcs/0046-platform-support-tiers.md
+{ }:
+
+[
+  # Tier 1
+  "x86_64-linux"
+  # Tier 2
+  "aarch64-linux"
+  "x86_64-darwin"
+  # Tier 3
+  "armv6l-linux"
+  "armv7l-linux"
+  "i686-linux"
+  "mipsel-linux"
+
+  # Other platforms with sufficient support in stdenv which is not formally
+  # mandated by their platform tier.
+  "aarch64-darwin"
+  "armv5tel-linux"
+  "powerpc64le-linux"
+  "riscv64-linux"
+
+  # "x86_64-freebsd" is excluded because it is mostly broken
+]
diff --git a/nixpkgs/lib/systems/inspect.nix b/nixpkgs/lib/systems/inspect.nix
index 89cac575c67d..dbffca0300b5 100644
--- a/nixpkgs/lib/systems/inspect.nix
+++ b/nixpkgs/lib/systems/inspect.nix
@@ -11,11 +11,12 @@ rec {
     isi686         = { cpu = cpuTypes.i686; };
     isx86_32       = { cpu = { family = "x86"; bits = 32; }; };
     isx86_64       = { cpu = { family = "x86"; bits = 64; }; };
-    isPowerPC      = { cpu = cpuTypes.powerpc; };
     isPower        = { cpu = { family = "power"; }; };
+    isPower64      = { cpu = { family = "power"; bits = 64; }; };
     isx86          = { cpu = { family = "x86"; }; };
     isAarch32      = { cpu = { family = "arm"; bits = 32; }; };
     isAarch64      = { cpu = { family = "arm"; bits = 64; }; };
+    isAarch        = { cpu = { family = "arm"; }; };
     isMips         = { cpu = { family = "mips"; }; };
     isMips32       = { cpu = { family = "mips"; bits = 32; }; };
     isMips64       = { cpu = { family = "mips"; bits = 64; }; };
@@ -23,6 +24,9 @@ rec {
     isMips64n64    = { cpu = { family = "mips"; bits = 64; }; abi = { abi = "64";  }; };
     isMmix         = { cpu = { family = "mmix"; }; };
     isRiscV        = { cpu = { family = "riscv"; }; };
+    isRiscV32      = { cpu = { family = "riscv"; bits = 32; }; };
+    isRiscV64      = { cpu = { family = "riscv"; bits = 64; }; };
+    isRx           = { cpu = { family = "rx"; }; };
     isSparc        = { cpu = { family = "sparc"; }; };
     isWasm         = { cpu = { family = "wasm"; }; };
     isMsp430       = { cpu = { family = "msp430"; }; };
diff --git a/nixpkgs/lib/systems/parse.nix b/nixpkgs/lib/systems/parse.nix
index 3ceddbb599b9..9d2571c993a9 100644
--- a/nixpkgs/lib/systems/parse.nix
+++ b/nixpkgs/lib/systems/parse.nix
@@ -116,6 +116,7 @@ rec {
 
     alpha    = { bits = 64; significantByte = littleEndian; family = "alpha"; };
 
+    rx       = { bits = 32; significantByte = littleEndian; family = "rx"; };
     msp430   = { bits = 16; significantByte = littleEndian; family = "msp430"; };
     avr      = { bits = 8; family = "avr"; };
 
@@ -147,8 +148,10 @@ rec {
   #   Every CPU is compatible with itself.
   # - (transitivity)
   #   If A is compatible with B and B is compatible with C then A is compatible with C.
-  # - (compatible under multiple endianness)
-  #   CPUs with multiple modes of endianness are pairwise compatible.
+  #
+  # Note: Since 22.11 the archs of a mode switching CPU are no longer considered
+  # pairwise compatible. Mode switching implies that binaries built for A
+  # and B respectively can't be executed at the same time.
   isCompatible = a: b: with cpuTypes; lib.any lib.id [
     # x86
     (b == i386 && isCompatible a i486)
@@ -190,22 +193,13 @@ rec {
     (b == aarch64 && a == armv8a)
     (b == armv8a && isCompatible a aarch64)
 
-    (b == aarch64 && a == aarch64_be)
-    (b == aarch64_be && isCompatible a aarch64)
-
     # PowerPC
     (b == powerpc && isCompatible a powerpc64)
-    (b == powerpcle && isCompatible a powerpc)
-    (b == powerpc && a == powerpcle)
-    (b == powerpc64le && isCompatible a powerpc64)
-    (b == powerpc64 && a == powerpc64le)
+    (b == powerpcle && isCompatible a powerpc64le)
 
     # MIPS
     (b == mips && isCompatible a mips64)
-    (b == mips && a == mipsel)
-    (b == mipsel && isCompatible a mips)
-    (b == mips64 && a == mips64el)
-    (b == mips64el && isCompatible a mips64)
+    (b == mipsel && isCompatible a mips64el)
 
     # RISCV
     (b == riscv32 && isCompatible a riscv64)
diff --git a/nixpkgs/lib/systems/platforms.nix b/nixpkgs/lib/systems/platforms.nix
index 04d55416242e..41c25484cea0 100644
--- a/nixpkgs/lib/systems/platforms.nix
+++ b/nixpkgs/lib/systems/platforms.nix
@@ -3,7 +3,7 @@
 # targetPlatform, etc) containing at least the minimal set of attrs
 # required (see types.parsedPlatform in lib/systems/parse.nix).  This
 # file takes an already-valid platform and further elaborates it with
-# optional fields such as linux-kernel, gcc, etc.
+# optional fields; currently these are: linux-kernel, gcc, and rustc.
 
 { lib }:
 rec {
@@ -242,13 +242,6 @@ rec {
     };
   };
 
-  scaleway-c1 = armv7l-hf-multiplatform // {
-    gcc = {
-      cpu = "cortex-a9";
-      fpu = "vfpv3";
-    };
-  };
-
   utilite = {
     linux-kernel = {
       name = "utilite";
@@ -490,8 +483,8 @@ rec {
   };
 
   # can execute on 32bit chip
-  gcc_mips32r2_o32 = { gcc = { arch = "mips32r2"; abi = "o32"; }; };
-  gcc_mips32r6_o32 = { gcc = { arch = "mips32r6"; abi = "o32"; }; };
+  gcc_mips32r2_o32 = { gcc = { arch = "mips32r2"; abi =  "32"; }; };
+  gcc_mips32r6_o32 = { gcc = { arch = "mips32r6"; abi =  "32"; }; };
   gcc_mips64r2_n32 = { gcc = { arch = "mips64r2"; abi = "n32"; }; };
   gcc_mips64r6_n32 = { gcc = { arch = "mips64r6"; abi = "n32"; }; };
   gcc_mips64r2_64  = { gcc = { arch = "mips64r2"; abi =  "64"; }; };
@@ -500,7 +493,7 @@ rec {
   # based on:
   #   https://www.mail-archive.com/qemu-discuss@nongnu.org/msg05179.html
   #   https://gmplib.org/~tege/qemu.html#mips64-debian
-  mips64el-qemu-linux-gnuabi64 = (import ./examples).mips64el-linux-gnuabi64 // {
+  mips64el-qemu-linux-gnuabi64 = {
     linux-kernel = {
       name = "mips64el";
       baseConfig = "64r2el_defconfig";
@@ -568,5 +561,5 @@ rec {
 
     else if platform.parsed.cpu == lib.systems.parse.cpuTypes.powerpc64le then powernv
 
-    else pc;
+    else { };
 }
diff --git a/nixpkgs/lib/systems/supported.nix b/nixpkgs/lib/systems/supported.nix
deleted file mode 100644
index a1c038a5c8bc..000000000000
--- a/nixpkgs/lib/systems/supported.nix
+++ /dev/null
@@ -1,26 +0,0 @@
-# Supported systems according to RFC0046's definition.
-#
-# https://github.com/NixOS/rfcs/blob/master/rfcs/0046-platform-support-tiers.md
-{ lib }:
-rec {
-  # List of systems that are built by Hydra.
-  hydra = tier1 ++ tier2 ++ tier3 ++ [
-    "aarch64-darwin"
-  ];
-
-  tier1 = [
-    "x86_64-linux"
-  ];
-
-  tier2 = [
-    "aarch64-linux"
-    "x86_64-darwin"
-  ];
-
-  tier3 = [
-    "armv6l-linux"
-    "armv7l-linux"
-    "i686-linux"
-    "mipsel-linux"
-  ];
-}
diff --git a/nixpkgs/lib/tests/maintainer-module.nix b/nixpkgs/lib/tests/maintainer-module.nix
new file mode 100644
index 000000000000..8cf8411b476a
--- /dev/null
+++ b/nixpkgs/lib/tests/maintainer-module.nix
@@ -0,0 +1,31 @@
+{ lib, ... }:
+let
+  inherit (lib) types;
+in {
+  options = {
+    name = lib.mkOption {
+      type = types.str;
+    };
+    email = lib.mkOption {
+      type = types.str;
+    };
+    matrix = lib.mkOption {
+      type = types.nullOr types.str;
+      default = null;
+    };
+    github = lib.mkOption {
+      type = types.nullOr types.str;
+      default = null;
+    };
+    githubId = lib.mkOption {
+      type = types.nullOr types.ints.unsigned;
+      default = null;
+    };
+    keys = lib.mkOption {
+      type = types.listOf (types.submodule {
+        options.fingerprint = lib.mkOption { type = types.str; };
+      });
+      default = [];
+    };
+  };
+}
diff --git a/nixpkgs/lib/tests/maintainers.nix b/nixpkgs/lib/tests/maintainers.nix
index 3cbfba569481..935d256d218d 100644
--- a/nixpkgs/lib/tests/maintainers.nix
+++ b/nixpkgs/lib/tests/maintainers.nix
@@ -1,50 +1,19 @@
 # to run these tests (and the others)
 # nix-build nixpkgs/lib/tests/release.nix
 { # The pkgs used for dependencies for the testing itself
-  pkgs
-, lib
+  pkgs ? import ../.. {}
+, lib ? pkgs.lib
 }:
 
 let
   inherit (lib) types;
-
-  maintainerModule = { config, ... }: {
-    options = {
-      name = lib.mkOption {
-        type = types.str;
-      };
-      email = lib.mkOption {
-        type = types.str;
-      };
-      matrix = lib.mkOption {
-        type = types.nullOr types.str;
-        default = null;
-      };
-      github = lib.mkOption {
-        type = types.nullOr types.str;
-        default = null;
-      };
-      githubId = lib.mkOption {
-        type = types.nullOr types.ints.unsigned;
-        default = null;
-      };
-      keys = lib.mkOption {
-        type = types.listOf (types.submodule {
-          options.longkeyid = lib.mkOption { type = types.str; };
-          options.fingerprint = lib.mkOption { type = types.str; };
-        });
-        default = [];
-      };
-    };
-  };
-
   checkMaintainer = handle: uncheckedAttrs:
   let
       prefix = [ "lib" "maintainers" handle ];
       checkedAttrs = (lib.modules.evalModules {
         inherit prefix;
         modules = [
-          maintainerModule
+          ./maintainer-module.nix
           {
             _file = toString ../../maintainers/maintainer-list.nix;
             config = uncheckedAttrs;
diff --git a/nixpkgs/lib/tests/misc.nix b/nixpkgs/lib/tests/misc.nix
index 271119031395..584a946e92cc 100644
--- a/nixpkgs/lib/tests/misc.nix
+++ b/nixpkgs/lib/tests/misc.nix
@@ -22,7 +22,6 @@ in
 
 runTests {
 
-
 # TRIVIAL
 
   testId = {
@@ -251,6 +250,68 @@ runTests {
     expected = "&quot;test&quot; &apos;test&apos; &lt; &amp; &gt;";
   };
 
+  testToShellVars = {
+    expr = ''
+      ${toShellVars {
+        STRing01 = "just a 'string'";
+        _array_ = [ "with" "more strings" ];
+        assoc."with some" = ''
+          strings
+          possibly newlines
+        '';
+        drv = {
+          outPath = "/drv";
+          foo = "ignored attribute";
+        };
+        path = /path;
+        stringable = {
+          __toString = _: "hello toString";
+          bar = "ignored attribute";
+        };
+      }}
+    '';
+    expected = ''
+      STRing01='just a '\'''string'\''''
+      declare -a _array_=('with' 'more strings')
+      declare -A assoc=(['with some']='strings
+      possibly newlines
+      ')
+      drv='/drv'
+      path='/path'
+      stringable='hello toString'
+    '';
+  };
+
+  testHasInfixFalse = {
+    expr = hasInfix "c" "abde";
+    expected = false;
+  };
+
+  testHasInfixTrue = {
+    expr = hasInfix "c" "abcde";
+    expected = true;
+  };
+
+  testHasInfixDerivation = {
+    expr = hasInfix "hello" (import ../.. { system = "x86_64-linux"; }).hello;
+    expected = true;
+  };
+
+  testHasInfixPath = {
+    expr = hasInfix "tests" ./.;
+    expected = true;
+  };
+
+  testHasInfixPathStoreDir = {
+    expr = hasInfix builtins.storeDir ./.;
+    expected = true;
+  };
+
+  testHasInfixToString = {
+    expr = hasInfix "a" { __toString = _: "a"; };
+    expected = true;
+  };
+
 # LISTS
 
   testFilter = {
@@ -471,6 +532,66 @@ runTests {
     '';
   };
 
+  testToINIWithGlobalSectionEmpty = {
+    expr = generators.toINIWithGlobalSection {} {
+      globalSection = {
+      };
+      sections = {
+      };
+    };
+    expected = ''
+    '';
+  };
+
+  testToINIWithGlobalSectionGlobalEmptyIsTheSameAsToINI =
+    let
+      sections = {
+        "section 1" = {
+          attribute1 = 5;
+          x = "Me-se JarJar Binx";
+        };
+        "foo" = {
+          "he\\h=he" = "this is okay";
+        };
+      };
+    in {
+      expr =
+        generators.toINIWithGlobalSection {} {
+            globalSection = {};
+            sections = sections;
+        };
+      expected = generators.toINI {} sections;
+  };
+
+  testToINIWithGlobalSectionFull = {
+    expr = generators.toINIWithGlobalSection {} {
+      globalSection = {
+        foo = "bar";
+        test = false;
+      };
+      sections = {
+        "section 1" = {
+          attribute1 = 5;
+          x = "Me-se JarJar Binx";
+        };
+        "foo" = {
+          "he\\h=he" = "this is okay";
+        };
+      };
+    };
+    expected = ''
+      foo=bar
+      test=false
+
+      [foo]
+      he\h\=he=this is okay
+
+      [section 1]
+      attribute1=5
+      x=Me-se JarJar Binx
+    '';
+  };
+
   /* right now only invocation check */
   testToJSONSimple =
     let val = {
@@ -553,6 +674,21 @@ runTests {
       expected = false;
     };
 
+  testWithRecursionDealsWithFunctors =
+    let
+      functor = {
+        __functor = self: { a, b, }: null;
+      };
+      a = {
+        value = "1234";
+        b = functor;
+        c.d = functor;
+      };
+    in {
+      expr = generators.toPretty { } (generators.withRecursion { depthLimit = 1; throwOnDepthLimit = false; } a);
+      expected = "{\n  b = <function, args: {a, b}>;\n  c = {\n    d = \"<unevaluated>\";\n  };\n  value = \"<unevaluated>\";\n}";
+    };
+
   testToPrettyMultiline = {
     expr = mapAttrs (const (generators.toPretty { })) rec {
       list = [ 3 4 [ false ] ];
@@ -649,6 +785,11 @@ runTests {
     expected = "foo";
   };
 
+  testSanitizeDerivationNameUnicode = testSanitizeDerivationName {
+    name = "fö";
+    expected = "f-";
+  };
+
   testSanitizeDerivationNameAscii = testSanitizeDerivationName {
     name = " !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~";
     expected = "-+--.-0123456789-=-?-ABCDEFGHIJKLMNOPQRSTUVWXYZ-_-abcdefghijklmnopqrstuvwxyz-";
@@ -691,7 +832,7 @@ runTests {
 
         locs = filter (o: ! o.internal) (optionAttrSetToDocList options);
       in map (o: o.loc) locs;
-    expected = [ [ "foo" ] [ "foo" "<name>" "bar" ] [ "foo" "bar" ] ];
+    expected = [ [ "_module" "args" ] [ "foo" ] [ "foo" "<name>" "bar" ] [ "foo" "bar" ] ];
   };
 
   testCartesianProductOfEmptySet = {
@@ -913,4 +1054,156 @@ runTests {
     };
   };
 
+  ## Levenshtein distance functions and co.
+  testCommonPrefixLengthEmpty = {
+    expr = strings.commonPrefixLength "" "hello";
+    expected = 0;
+  };
+
+  testCommonPrefixLengthSame = {
+    expr = strings.commonPrefixLength "hello" "hello";
+    expected = 5;
+  };
+
+  testCommonPrefixLengthDiffering = {
+    expr = strings.commonPrefixLength "hello" "hey";
+    expected = 2;
+  };
+
+  testCommonSuffixLengthEmpty = {
+    expr = strings.commonSuffixLength "" "hello";
+    expected = 0;
+  };
+
+  testCommonSuffixLengthSame = {
+    expr = strings.commonSuffixLength "hello" "hello";
+    expected = 5;
+  };
+
+  testCommonSuffixLengthDiffering = {
+    expr = strings.commonSuffixLength "test" "rest";
+    expected = 3;
+  };
+
+  testLevenshteinEmpty = {
+    expr = strings.levenshtein "" "";
+    expected = 0;
+  };
+
+  testLevenshteinOnlyAdd = {
+    expr = strings.levenshtein "" "hello there";
+    expected = 11;
+  };
+
+  testLevenshteinOnlyRemove = {
+    expr = strings.levenshtein "hello there" "";
+    expected = 11;
+  };
+
+  testLevenshteinOnlyTransform = {
+    expr = strings.levenshtein "abcdef" "ghijkl";
+    expected = 6;
+  };
+
+  testLevenshteinMixed = {
+    expr = strings.levenshtein "kitchen" "sitting";
+    expected = 5;
+  };
+
+  testLevenshteinAtMostZeroFalse = {
+    expr = strings.levenshteinAtMost 0 "foo" "boo";
+    expected = false;
+  };
+
+  testLevenshteinAtMostZeroTrue = {
+    expr = strings.levenshteinAtMost 0 "foo" "foo";
+    expected = true;
+  };
+
+  testLevenshteinAtMostOneFalse = {
+    expr = strings.levenshteinAtMost 1 "car" "ct";
+    expected = false;
+  };
+
+  testLevenshteinAtMostOneTrue = {
+    expr = strings.levenshteinAtMost 1 "car" "cr";
+    expected = true;
+  };
+
+  # We test levenshteinAtMost 2 particularly well because it uses a complicated
+  # implementation
+  testLevenshteinAtMostTwoIsEmpty = {
+    expr = strings.levenshteinAtMost 2 "" "";
+    expected = true;
+  };
+
+  testLevenshteinAtMostTwoIsZero = {
+    expr = strings.levenshteinAtMost 2 "abcdef" "abcdef";
+    expected = true;
+  };
+
+  testLevenshteinAtMostTwoIsOne = {
+    expr = strings.levenshteinAtMost 2 "abcdef" "abddef";
+    expected = true;
+  };
+
+  testLevenshteinAtMostTwoDiff0False = {
+    expr = strings.levenshteinAtMost 2 "abcdef" "aczyef";
+    expected = false;
+  };
+
+  testLevenshteinAtMostTwoDiff0Outer = {
+    expr = strings.levenshteinAtMost 2 "abcdef" "zbcdez";
+    expected = true;
+  };
+
+  testLevenshteinAtMostTwoDiff0DelLeft = {
+    expr = strings.levenshteinAtMost 2 "abcdef" "bcdefz";
+    expected = true;
+  };
+
+  testLevenshteinAtMostTwoDiff0DelRight = {
+    expr = strings.levenshteinAtMost 2 "abcdef" "zabcde";
+    expected = true;
+  };
+
+  testLevenshteinAtMostTwoDiff1False = {
+    expr = strings.levenshteinAtMost 2 "abcdef" "bddez";
+    expected = false;
+  };
+
+  testLevenshteinAtMostTwoDiff1DelLeft = {
+    expr = strings.levenshteinAtMost 2 "abcdef" "bcdez";
+    expected = true;
+  };
+
+  testLevenshteinAtMostTwoDiff1DelRight = {
+    expr = strings.levenshteinAtMost 2 "abcdef" "zbcde";
+    expected = true;
+  };
+
+  testLevenshteinAtMostTwoDiff2False = {
+    expr = strings.levenshteinAtMost 2 "hello" "hxo";
+    expected = false;
+  };
+
+  testLevenshteinAtMostTwoDiff2True = {
+    expr = strings.levenshteinAtMost 2 "hello" "heo";
+    expected = true;
+  };
+
+  testLevenshteinAtMostTwoDiff3 = {
+    expr = strings.levenshteinAtMost 2 "hello" "ho";
+    expected = false;
+  };
+
+  testLevenshteinAtMostThreeFalse = {
+    expr = strings.levenshteinAtMost 3 "hello" "Holla!";
+    expected = false;
+  };
+
+  testLevenshteinAtMostThreeTrue = {
+    expr = strings.levenshteinAtMost 3 "hello" "Holla";
+    expected = true;
+  };
 }
diff --git a/nixpkgs/lib/tests/modules.sh b/nixpkgs/lib/tests/modules.sh
index 8050c6539fc2..c92cc62023b5 100755
--- a/nixpkgs/lib/tests/modules.sh
+++ b/nixpkgs/lib/tests/modules.sh
@@ -194,6 +194,17 @@ checkConfigOutput '^"submodule"$' options.submodule.type.description ./declare-s
 ## Paths should be allowed as values and work as expected
 checkConfigOutput '^true$' config.submodule.enable ./declare-submoduleWith-path.nix
 
+## deferredModule
+# default module is merged into nodes.foo
+checkConfigOutput '"beta"' config.nodes.foo.settingsDict.c ./deferred-module.nix
+# errors from the default module are reported with accurate location
+checkConfigError 'In `the-file-that-contains-the-bad-config.nix, via option default'\'': "bogus"' config.nodes.foo.bottom ./deferred-module.nix
+checkConfigError '.*lib/tests/modules/deferred-module-error.nix, via option deferred [(]:anon-1:anon-1:anon-1[)] does not look like a module.' config.result ./deferred-module-error.nix
+
+# Check the file location information is propagated into submodules
+checkConfigOutput the-file.nix config.submodule.internalFiles.0 ./submoduleFiles.nix
+
+
 # Check that disabledModules works recursively and correctly
 checkConfigOutput '^true$' config.enable ./disable-recursive/main.nix
 checkConfigOutput '^true$' config.enable ./disable-recursive/{main.nix,disable-foo.nix}
@@ -290,10 +301,12 @@ checkConfigOutput '^"a b"$' config.result ./functionTo/merging-list.nix
 checkConfigError 'A definition for option .fun.\[function body\]. is not of type .string.. Definition values:\n\s*- In .*wrong-type.nix' config.result ./functionTo/wrong-type.nix
 checkConfigOutput '^"b a"$' config.result ./functionTo/list-order.nix
 checkConfigOutput '^"a c"$' config.result ./functionTo/merging-attrs.nix
+checkConfigOutput '^"a bee"$' config.result ./functionTo/submodule-options.nix
+checkConfigOutput '^"fun.\[function body\].a fun.\[function body\].b"$' config.optionsResult ./functionTo/submodule-options.nix
 
 # moduleType
 checkConfigOutput '^"a b"$' config.resultFoo ./declare-variants.nix ./define-variant.nix
-checkConfigOutput '^"a y z"$' config.resultFooBar ./declare-variants.nix ./define-variant.nix
+checkConfigOutput '^"a b y z"$' config.resultFooBar ./declare-variants.nix ./define-variant.nix
 checkConfigOutput '^"a b c"$' config.resultFooFoo ./declare-variants.nix ./define-variant.nix
 
 ## emptyValue's
@@ -313,7 +326,7 @@ checkConfigOutput "bar" config.priorities ./raw.nix
 
 ## Option collision
 checkConfigError \
-  'The option .set. in module .*/declare-set.nix. would be a parent of the following options, but its type .attribute set of signed integers. does not support nested options.\n\s*- option[(]s[)] with prefix .set.enable. in module .*/declare-enable-nested.nix.' \
+  'The option .set. in module .*/declare-set.nix. would be a parent of the following options, but its type .attribute set of signed integer. does not support nested options.\n\s*- option[(]s[)] with prefix .set.enable. in module .*/declare-enable-nested.nix.' \
   config.set \
   ./declare-set.nix ./declare-enable-nested.nix
 
@@ -327,6 +340,10 @@ checkConfigError 'The option .theOption.nested. in .other.nix. is already declar
 # Test that types.optionType leaves types untouched as long as they don't need to be merged
 checkConfigOutput 'ok' config.freeformItems.foo.bar ./adhoc-freeformType-survives-type-merge.nix
 
+# Anonymous submodules don't get nixed by import resolution/deduplication
+# because of an `extendModules` bug, issue 168767.
+checkConfigOutput '^1$' config.sub.specialisation.value ./extendModules-168767-imports.nix
+
 cat <<EOF
 ====== module tests ======
 $pass Pass
diff --git a/nixpkgs/lib/tests/modules/deferred-module-error.nix b/nixpkgs/lib/tests/modules/deferred-module-error.nix
new file mode 100644
index 000000000000..d48ae092e8fe
--- /dev/null
+++ b/nixpkgs/lib/tests/modules/deferred-module-error.nix
@@ -0,0 +1,20 @@
+{ config, lib, ... }:
+let
+  inherit (lib) types mkOption setDefaultModuleLocation evalModules;
+  inherit (types) deferredModule lazyAttrsOf submodule str raw enum;
+in
+{
+  options = {
+    deferred = mkOption {
+      type = deferredModule;
+    };
+    result = mkOption {
+      default = (evalModules { modules = [ config.deferred ]; }).config.result;
+    };
+  };
+  config = {
+    deferred = { ... }:
+      # this should be an attrset, so this fails
+      true;
+  };
+}
diff --git a/nixpkgs/lib/tests/modules/deferred-module.nix b/nixpkgs/lib/tests/modules/deferred-module.nix
new file mode 100644
index 000000000000..d03c60b029bf
--- /dev/null
+++ b/nixpkgs/lib/tests/modules/deferred-module.nix
@@ -0,0 +1,58 @@
+{ lib, ... }:
+let
+  inherit (lib) types mkOption setDefaultModuleLocation;
+  inherit (types) deferredModule lazyAttrsOf submodule str raw enum;
+in
+{
+  imports = [
+    # generic module, declaring submodules:
+    #   - nodes.<name>
+    #   - default
+    # where all nodes include the default
+    ({ config, ... }: {
+      _file = "generic.nix";
+      options.nodes = mkOption {
+        type = lazyAttrsOf (submodule { imports = [ config.default ]; });
+        default = {};
+      };
+      options.default = mkOption {
+        type = deferredModule;
+        default = { };
+        description = ''
+          Module that is included in all nodes.
+        '';
+      };
+    })
+
+    {
+      _file = "default-1.nix";
+      default = { config, ... }: {
+        options.settingsDict = lib.mkOption { type = lazyAttrsOf str; default = {}; };
+        options.bottom = lib.mkOption { type = enum []; };
+      };
+    }
+
+    {
+      _file = "default-a-is-b.nix";
+      default = ./define-settingsDict-a-is-b.nix;
+    }
+
+    {
+      _file = "nodes-foo.nix";
+      nodes.foo.settingsDict.b = "beta";
+    }
+
+    {
+      _file = "the-file-that-contains-the-bad-config.nix";
+      default.bottom = "bogus";
+    }
+
+    {
+      _file = "nodes-foo-c-is-a.nix";
+      nodes.foo = { config, ... }: {
+        settingsDict.c = config.settingsDict.a;
+      };
+    }
+
+  ];
+}
diff --git a/nixpkgs/lib/tests/modules/define-settingsDict-a-is-b.nix b/nixpkgs/lib/tests/modules/define-settingsDict-a-is-b.nix
new file mode 100644
index 000000000000..42363f45f78d
--- /dev/null
+++ b/nixpkgs/lib/tests/modules/define-settingsDict-a-is-b.nix
@@ -0,0 +1,3 @@
+{ config, ... }: {
+  settingsDict.a = config.settingsDict.b;
+}
diff --git a/nixpkgs/lib/tests/modules/extendModules-168767-imports.nix b/nixpkgs/lib/tests/modules/extendModules-168767-imports.nix
new file mode 100644
index 000000000000..489e6b5a5d83
--- /dev/null
+++ b/nixpkgs/lib/tests/modules/extendModules-168767-imports.nix
@@ -0,0 +1,41 @@
+{ lib
+, extendModules
+, ...
+}:
+with lib;
+{
+  imports = [
+
+    {
+      options.sub = mkOption {
+        default = { };
+        type = types.submodule (
+          { config
+          , extendModules
+          , ...
+          }:
+          {
+            options.value = mkOption {
+              type = types.int;
+            };
+
+            options.specialisation = mkOption {
+              default = { };
+              inherit
+                (extendModules {
+                  modules = [{
+                    specialisation = mkOverride 0 { };
+                  }];
+                })
+                type;
+            };
+          }
+        );
+      };
+    }
+
+    { config.sub.value = 1; }
+
+
+  ];
+}
diff --git a/nixpkgs/lib/tests/modules/functionTo/submodule-options.nix b/nixpkgs/lib/tests/modules/functionTo/submodule-options.nix
new file mode 100644
index 000000000000..b884892efd6a
--- /dev/null
+++ b/nixpkgs/lib/tests/modules/functionTo/submodule-options.nix
@@ -0,0 +1,61 @@
+{ lib, config, options, ... }:
+let
+  inherit (lib) types;
+in
+{
+  imports = [
+
+    # fun.<function-body>.a
+    ({ ... }: {
+      options = {
+        fun = lib.mkOption {
+          type = types.functionTo (types.submodule {
+            options.a = lib.mkOption { default = "a"; };
+          });
+        };
+      };
+    })
+
+    # fun.<function-body>.b
+    ({ ... }: {
+      options = {
+        fun = lib.mkOption {
+          type = types.functionTo (types.submodule {
+            options.b = lib.mkOption { default = "b"; };
+          });
+        };
+      };
+    })
+  ];
+
+  options = {
+    result = lib.mkOption
+      {
+        type = types.str;
+        default = lib.concatStringsSep " " (lib.attrValues (config.fun (throw "shouldn't use input param")));
+      };
+
+    optionsResult = lib.mkOption
+      {
+        type = types.str;
+        default = lib.concatStringsSep " "
+          (lib.concatLists
+            (lib.mapAttrsToList
+              (k: v:
+                if k == "_module"
+                then [ ]
+                else [ (lib.showOption v.loc) ]
+              )
+              (
+                (options.fun.type.getSubOptions [ "fun" ])
+              )
+            )
+          );
+      };
+  };
+
+  config.fun = lib.mkMerge
+    [
+      (input: { b = "bee"; })
+    ];
+}
diff --git a/nixpkgs/lib/tests/modules/submoduleFiles.nix b/nixpkgs/lib/tests/modules/submoduleFiles.nix
new file mode 100644
index 000000000000..c0d9b2cef3e8
--- /dev/null
+++ b/nixpkgs/lib/tests/modules/submoduleFiles.nix
@@ -0,0 +1,21 @@
+{ lib, ... }: {
+  options.submodule = lib.mkOption {
+    default = {};
+    type = lib.types.submoduleWith {
+      modules = [ ({ options, ... }: {
+        options.value = lib.mkOption {};
+
+        options.internalFiles = lib.mkOption {
+          default = options.value.files;
+        };
+      })];
+    };
+  };
+
+  imports = [
+    {
+      _file = "the-file.nix";
+      submodule.value = 10;
+    }
+  ];
+}
diff --git a/nixpkgs/lib/tests/release.nix b/nixpkgs/lib/tests/release.nix
index 815841e0a8f3..b93a4236f91e 100644
--- a/nixpkgs/lib/tests/release.nix
+++ b/nixpkgs/lib/tests/release.nix
@@ -11,6 +11,10 @@ pkgs.runCommand "nixpkgs-lib-tests" {
       inherit pkgs;
       lib = import ../.;
     })
+    (import ./teams.nix {
+      inherit pkgs;
+      lib = import ../.;
+    })
   ];
 } ''
     datadir="${pkgs.nix}/share"
diff --git a/nixpkgs/lib/tests/systems.nix b/nixpkgs/lib/tests/systems.nix
index c88adbf4651a..46e7bd992f1e 100644
--- a/nixpkgs/lib/tests/systems.nix
+++ b/nixpkgs/lib/tests/systems.nix
@@ -19,6 +19,9 @@ with lib.systems.doubles; lib.runTests {
   testi686 = mseteq i686 [ "i686-linux" "i686-freebsd" "i686-genode" "i686-netbsd" "i686-openbsd" "i686-cygwin" "i686-windows" "i686-none" "i686-darwin" ];
   testmips = mseteq mips [ "mips64el-linux" "mipsel-linux" "mipsel-netbsd" ];
   testmmix = mseteq mmix [ "mmix-mmixware" ];
+  testriscv = mseteq riscv [ "riscv32-linux" "riscv64-linux" "riscv32-netbsd" "riscv64-netbsd" "riscv32-none" "riscv64-none" ];
+  testriscv32 = mseteq riscv32 [ "riscv32-linux" "riscv32-netbsd" "riscv32-none" ];
+  testriscv64 = mseteq riscv64 [ "riscv64-linux" "riscv64-netbsd" "riscv64-none" ];
   testx86_64 = mseteq x86_64 [ "x86_64-linux" "x86_64-darwin" "x86_64-freebsd" "x86_64-genode" "x86_64-redox" "x86_64-openbsd" "x86_64-netbsd" "x86_64-cygwin" "x86_64-solaris" "x86_64-windows" "x86_64-none" ];
 
   testcygwin = mseteq cygwin [ "i686-cygwin" "x86_64-cygwin" ];
diff --git a/nixpkgs/lib/tests/teams.nix b/nixpkgs/lib/tests/teams.nix
new file mode 100644
index 000000000000..8a0a5d272634
--- /dev/null
+++ b/nixpkgs/lib/tests/teams.nix
@@ -0,0 +1,50 @@
+# to run these tests:
+# nix-build nixpkgs/lib/tests/teams.nix
+# If it builds, all tests passed
+{ pkgs ? import ../.. {}, lib ? pkgs.lib }:
+
+let
+  inherit (lib) types;
+
+  teamModule = { config, ... }: {
+    options = {
+      shortName = lib.mkOption {
+        type = types.str;
+      };
+      scope = lib.mkOption {
+        type = types.str;
+      };
+      enableFeatureFreezePing = lib.mkOption {
+        type = types.bool;
+        default = false;
+      };
+      members = lib.mkOption {
+        type = types.listOf (types.submodule
+          (import ./maintainer-module.nix { inherit lib; })
+        );
+        default = [];
+      };
+      githubTeams = lib.mkOption {
+        type = types.listOf types.str;
+        default = [];
+      };
+    };
+  };
+
+  checkTeam = team: uncheckedAttrs:
+  let
+      prefix = [ "lib" "maintainer-team" team ];
+      checkedAttrs = (lib.modules.evalModules {
+        inherit prefix;
+        modules = [
+          teamModule
+          {
+            _file = toString ../../maintainers/team-list.nix;
+            config = uncheckedAttrs;
+          }
+        ];
+      }).config;
+  in checkedAttrs;
+
+  checkedTeams = lib.mapAttrs checkTeam lib.teams;
+in pkgs.writeTextDir "maintainer-teams.json" (builtins.toJSON checkedTeams)
diff --git a/nixpkgs/lib/trivial.nix b/nixpkgs/lib/trivial.nix
index 18616a189c26..5d4fad8266bc 100644
--- a/nixpkgs/lib/trivial.nix
+++ b/nixpkgs/lib/trivial.nix
@@ -179,7 +179,7 @@ rec {
      they take effect as soon as the oldest release reaches end of life. */
   oldestSupportedRelease =
     # Update on master only. Do not backport.
-    2111;
+    2205;
 
   /* Whether a feature is supported in all supported releases (at the time of
      release branch-off, if applicable). See `oldestSupportedRelease`. */
@@ -195,7 +195,7 @@ rec {
      On each release the first letter is bumped and a new animal is chosen
      starting with that new letter.
   */
-  codeName = "Quokka";
+  codeName = "Raccoon";
 
   /* Returns the current nixpkgs version suffix as string. */
   versionSuffix =
@@ -229,6 +229,13 @@ rec {
   */
   inNixShell = builtins.getEnv "IN_NIX_SHELL" != "";
 
+  /* Determine whether the function is being called from inside pure-eval mode
+     by seeing whether `builtins` contains `currentSystem`. If not, we must be in
+     pure-eval mode.
+
+     Type: inPureEvalMode :: bool
+  */
+  inPureEvalMode = ! builtins ? currentSystem;
 
   ## Integer operations
 
diff --git a/nixpkgs/lib/types.nix b/nixpkgs/lib/types.nix
index 5c4b96310617..d7655bc1a6a2 100644
--- a/nixpkgs/lib/types.nix
+++ b/nixpkgs/lib/types.nix
@@ -55,6 +55,7 @@ let
     concatMapStringsSep
     concatStringsSep
     escapeNixString
+    hasInfix
     isCoercibleToString
     ;
   inherit (lib.trivial)
@@ -360,6 +361,11 @@ rec {
       deprecationMessage = "See https://github.com/NixOS/nixpkgs/pull/66346 for better alternative types.";
     };
 
+    passwdEntry = entryType: addCheck entryType (str: !(hasInfix ":" str || hasInfix "\n" str)) // {
+      name = "passwdEntry ${entryType.name}";
+      description = "${entryType.description}, not containing newlines or colons";
+    };
+
     attrs = mkOptionType {
       name = "attrs";
       description = "attribute set";
@@ -397,7 +403,7 @@ rec {
 
     listOf = elemType: mkOptionType rec {
       name = "listOf";
-      description = "list of ${elemType.description}s";
+      description = "list of ${elemType.description}";
       check = isList;
       merge = loc: defs:
         map (x: x.value) (filter (x: x ? value) (concatLists (imap1 (n: def:
@@ -426,7 +432,7 @@ rec {
 
     attrsOf = elemType: mkOptionType rec {
       name = "attrsOf";
-      description = "attribute set of ${elemType.description}s";
+      description = "attribute set of ${elemType.description}";
       check = isAttrs;
       merge = loc: defs:
         mapAttrs (n: v: v.value) (filterAttrs (n: v: v ? value) (zipAttrsWith (name: defs:
@@ -449,7 +455,7 @@ rec {
     # error that it's not defined. Use only if conditional definitions don't make sense.
     lazyAttrsOf = elemType: mkOptionType rec {
       name = "lazyAttrsOf";
-      description = "lazy attribute set of ${elemType.description}s";
+      description = "lazy attribute set of ${elemType.description}";
       check = isAttrs;
       merge = loc: defs:
         zipAttrsWith (name: defs:
@@ -526,9 +532,11 @@ rec {
       check = isFunction;
       merge = loc: defs:
         fnArgs: (mergeDefinitions (loc ++ [ "[function body]" ]) elemType (map (fn: { inherit (fn) file; value = fn.value fnArgs; }) defs)).mergedValue;
-      getSubOptions = elemType.getSubOptions;
+      getSubOptions = prefix: elemType.getSubOptions (prefix ++ [ "[function body]" ]);
       getSubModules = elemType.getSubModules;
       substSubModules = m: functionTo (elemType.substSubModules m);
+      functor = (defaultFunctor "functionTo") // { wrapped = elemType; };
+      nestedTypes.elemType = elemType;
     };
 
     # A submodule (like typed attribute set). See NixOS manual.
@@ -537,6 +545,36 @@ rec {
       modules = toList modules;
     };
 
+    # A module to be imported in some other part of the configuration.
+    deferredModule = deferredModuleWith { };
+
+    # A module to be imported in some other part of the configuration.
+    # `staticModules`' options will be added to the documentation, unlike
+    # options declared via `config`.
+    deferredModuleWith = attrs@{ staticModules ? [] }: mkOptionType {
+      name = "deferredModule";
+      description = "module";
+      check = x: isAttrs x || isFunction x || path.check x;
+      merge = loc: defs: {
+        imports = staticModules ++ map (def: lib.setDefaultModuleLocation "${def.file}, via option ${showOption loc}" def.value) defs;
+      };
+      inherit (submoduleWith { modules = staticModules; })
+        getSubOptions
+        getSubModules;
+      substSubModules = m: deferredModuleWith (attrs // {
+        staticModules = m;
+      });
+      functor = defaultFunctor "deferredModuleWith" // {
+        type = types.deferredModuleWith;
+        payload = {
+          inherit staticModules;
+        };
+        binOp = lhs: rhs: {
+          staticModules = lhs.staticModules ++ rhs.staticModules;
+        };
+      };
+    };
+
     # The type of a type!
     optionType = mkOptionType {
       name = "optionType";
@@ -568,23 +606,15 @@ rec {
       { modules
       , specialArgs ? {}
       , shorthandOnlyDefinesConfig ? false
+      , description ? null
       }@attrs:
       let
         inherit (lib.modules) evalModules;
 
-        shorthandToModule = if shorthandOnlyDefinesConfig == false
-          then value: value
-          else value: { config = value; };
-
-        allModules = defs: imap1 (n: { value, file }:
-          if isFunction value
-          then setFunctionArgs
-                (args: lib.modules.unifyModuleSyntax file "${toString file}-${toString n}" (value args))
-                (functionArgs value)
-          else if isAttrs value
-          then
-            lib.modules.unifyModuleSyntax file "${toString file}-${toString n}" (shorthandToModule value)
-          else value
+        allModules = defs: map ({ value, file }:
+          if isAttrs value && shorthandOnlyDefinesConfig
+          then { _file = file; config = value; }
+          else { _file = file; imports = [ value ]; }
         ) defs;
 
         base = evalModules {
@@ -611,10 +641,14 @@ rec {
 
         freeformType = base._module.freeformType;
 
-      in
-      mkOptionType rec {
         name = "submodule";
-        description = freeformType.description or name;
+
+      in
+      mkOptionType {
+        inherit name;
+        description =
+          if description != null then description
+          else freeformType.description or name;
         check = x: isAttrs x || isFunction x || path.check x;
         merge = loc: defs:
           (base.extendModules {
@@ -639,9 +673,7 @@ rec {
         functor = defaultFunctor name // {
           type = types.submoduleWith;
           payload = {
-            modules = modules;
-            specialArgs = specialArgs;
-            shorthandOnlyDefinesConfig = shorthandOnlyDefinesConfig;
+            inherit modules specialArgs shorthandOnlyDefinesConfig description;
           };
           binOp = lhs: rhs: {
             modules = lhs.modules ++ rhs.modules;
@@ -658,6 +690,14 @@ rec {
               else if lhs.shorthandOnlyDefinesConfig == rhs.shorthandOnlyDefinesConfig
               then lhs.shorthandOnlyDefinesConfig
               else throw "A submoduleWith option is declared multiple times with conflicting shorthandOnlyDefinesConfig values";
+            description =
+              if lhs.description == null
+              then rhs.description
+              else if rhs.description == null
+              then lhs.description
+              else if lhs.description == rhs.description
+              then lhs.description
+              else throw "A submoduleWith option is declared multiple times with conflicting descriptions";
           };
         };
       };
diff --git a/nixpkgs/lib/zip-int-bits.nix b/nixpkgs/lib/zip-int-bits.nix
index edbcdfe1e682..53efd2bb0a04 100644
--- a/nixpkgs/lib/zip-int-bits.nix
+++ b/nixpkgs/lib/zip-int-bits.nix
@@ -1,5 +1,5 @@
 /* Helper function to implement a fallback for the bit operators
-   `bitAnd`, `bitOr` and `bitXOr` on older nix version.
+   `bitAnd`, `bitOr` and `bitXor` on older nix version.
    See ./trivial.nix
 */
 f: x: y: