···1415 /* Return an attribute from nested attribute sets.
160000000017 Example:
18 x = { a = { b = 3; }; }
19 # ["a" "b"] is equivalent to x.a.b
···50 attrByPath' 0 set;
5152 /* Return if an attribute from nested attribute set exists.
000000005354 **Laws**:
55 1. ```nix
···176177 /* Like `attrByPath`, but without a default value. If it doesn't find the
178 path it will throw an error.
00000000179180 Example:
181 x = { a = { b = 3; }; }
···1415 /* Return an attribute from nested attribute sets.
1617+ 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:
18+19+ ```nix
20+ (x.a.b or 6) == attrByPath ["a" "b"] 6 x
21+ # and
22+ (x.${f p}."example.com" or 6) == attrByPath [ (f p) "example.com" ] 6 x
23+ ```
24+25 Example:
26 x = { a = { b = 3; }; }
27 # ["a" "b"] is equivalent to x.a.b
···58 attrByPath' 0 set;
5960 /* Return if an attribute from nested attribute set exists.
61+62+ 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:
63+64+ ```nix
65+ (x?a.b) == hasAttryByPath ["a" "b"] x
66+ # and
67+ (x?${f p}."example.com") == hasAttryByPath [ (f p) "example.com" ] x
68+ ```
6970 **Laws**:
71 1. ```nix
···192193 /* Like `attrByPath`, but without a default value. If it doesn't find the
194 path it will throw an error.
195+196+ 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:
197+198+ ```nix
199+ x.a.b == getAttrByPath ["a" "b"] x
200+ # and
201+ x.${f p}."example.com" == getAttrByPath [ (f p) "example.com" ] x
202+ ```
203204 Example:
205 x = { a = { b = 3; }; }
+17-41
lib/fileset/default.nix
···107 _printFileset
108 _intersection
109 _difference
110- _mirrorStorePath
111 _fetchGitSubmodulesMinver
112 _emptyWithoutBase
113 ;
···148 inherit (lib.trivial)
149 isFunction
150 pipe
151- inPureEvalMode
152 ;
153154in {
···754 This directory must contain a `.git` file or subdirectory.
755 */
756 path:
757- # See the gitTrackedWith implementation for more explanatory comments
758- let
759- fetchResult = builtins.fetchGit path;
760- in
761- if inPureEvalMode then
762- 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."
763- else if ! isPath path then
764- throw "lib.fileset.gitTracked: Expected the argument to be a path, but it's a ${typeOf path} instead."
765- else if ! pathExists (path + "/.git") then
766- throw "lib.fileset.gitTracked: Expected the argument (${toString path}) to point to a local working tree of a Git repository, but it's not."
767- else
768- _mirrorStorePath path fetchResult.outPath;
769770 /*
771 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 This directory must contain a `.git` file or subdirectory.
808 */
809 path:
810- let
811- # This imports the files unnecessarily, which currently can't be avoided
812- # because `builtins.fetchGit` is the only function exposing which files are tracked by Git.
813- # With the [lazy trees PR](https://github.com/NixOS/nix/pull/6530),
814- # the unnecessarily import could be avoided.
815- # However a simpler alternative still would be [a builtins.gitLsFiles](https://github.com/NixOS/nix/issues/2944).
816- fetchResult = builtins.fetchGit {
817- url = path;
818-819- # This is the only `fetchGit` parameter that makes sense in this context.
820- # We can't just pass `submodules = recurseSubmodules` here because
821- # this would fail for Nix versions that don't support `submodules`.
822- ${if recurseSubmodules then "submodules" else null} = true;
823- };
824- in
825- if inPureEvalMode then
826- 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."
827- else if ! isBool recurseSubmodules then
828 throw "lib.fileset.gitTrackedWith: Expected the attribute `recurseSubmodules` of the first argument to be a boolean, but it's a ${typeOf recurseSubmodules} instead."
829 else if recurseSubmodules && versionOlder nixVersion _fetchGitSubmodulesMinver then
830 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."
831- else if ! isPath path then
832- throw "lib.fileset.gitTrackedWith: Expected the second argument to be a path, but it's a ${typeOf path} instead."
833- # We can identify local working directories by checking for .git,
834- # see https://git-scm.com/docs/gitrepository-layout#_description.
835- # Note that `builtins.fetchGit` _does_ work for bare repositories (where there's no `.git`),
836- # even though `git ls-files` wouldn't return any files in that case.
837- else if ! pathExists (path + "/.git") then
838- 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."
839 else
840- _mirrorStorePath path fetchResult.outPath;
000000000841}
···107 _printFileset
108 _intersection
109 _difference
110+ _fromFetchGit
111 _fetchGitSubmodulesMinver
112 _emptyWithoutBase
113 ;
···148 inherit (lib.trivial)
149 isFunction
150 pipe
0151 ;
152153in {
···753 This directory must contain a `.git` file or subdirectory.
754 */
755 path:
756+ _fromFetchGit
757+ "gitTracked"
758+ "argument"
759+ path
760+ {};
0000000761762 /*
763 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.
···799 This directory must contain a `.git` file or subdirectory.
800 */
801 path:
802+ if ! isBool recurseSubmodules then
00000000000000000803 throw "lib.fileset.gitTrackedWith: Expected the attribute `recurseSubmodules` of the first argument to be a boolean, but it's a ${typeOf recurseSubmodules} instead."
804 else if recurseSubmodules && versionOlder nixVersion _fetchGitSubmodulesMinver then
805 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."
00000000806 else
807+ _fromFetchGit
808+ "gitTrackedWith"
809+ "second argument"
810+ path
811+ # This is the only `fetchGit` parameter that makes sense in this context.
812+ # We can't just pass `submodules = recurseSubmodules` here because
813+ # this would fail for Nix versions that don't support `submodules`.
814+ (lib.optionalAttrs recurseSubmodules {
815+ submodules = true;
816+ });
817}
+33
lib/fileset/internal.nix
···10 split
11 trace
12 typeOf
013 ;
1415 inherit (lib.attrsets)
···55 hasSuffix
56 ;
5700058in
59# Rare case of justified usage of rec:
60# - This file is internal, so the return value doesn't matter, no need to make things overridable
···852 in
853 _create localPath
854 (recurse storePath);
00000000000000000000000000000855}
···10 split
11 trace
12 typeOf
13+ fetchGit
14 ;
1516 inherit (lib.attrsets)
···56 hasSuffix
57 ;
5859+ inherit (lib.trivial)
60+ inPureEvalMode
61+ ;
62in
63# Rare case of justified usage of rec:
64# - This file is internal, so the return value doesn't matter, no need to make things overridable
···856 in
857 _create localPath
858 (recurse storePath);
859+860+ # Create a file set from the files included in the result of a fetchGit call
861+ # Type: String -> String -> Path -> Attrs -> FileSet
862+ _fromFetchGit = function: argument: path: extraFetchGitAttrs:
863+ let
864+ # This imports the files unnecessarily, which currently can't be avoided
865+ # because `builtins.fetchGit` is the only function exposing which files are tracked by Git.
866+ # With the [lazy trees PR](https://github.com/NixOS/nix/pull/6530),
867+ # the unnecessarily import could be avoided.
868+ # However a simpler alternative still would be [a builtins.gitLsFiles](https://github.com/NixOS/nix/issues/2944).
869+ fetchResult = fetchGit ({
870+ url = path;
871+ } // extraFetchGitAttrs);
872+ in
873+ if inPureEvalMode then
874+ 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."
875+ else if ! isPath path then
876+ throw "lib.fileset.${function}: Expected the ${argument} to be a path, but it's a ${typeOf path} instead."
877+ else if pathType path != "directory" then
878+ throw "lib.fileset.${function}: Expected the ${argument} (${toString path}) to be a directory, but it's a file instead."
879+ # We can identify local working directories by checking for .git,
880+ # see https://git-scm.com/docs/gitrepository-layout#_description.
881+ # Note that `builtins.fetchGit` _does_ work for bare repositories (where there's no `.git`),
882+ # even though `git ls-files` wouldn't return any files in that case.
883+ else if ! pathExists (path + "/.git") then
884+ throw "lib.fileset.${function}: Expected the ${argument} (${toString path}) to point to a local working tree of a Git repository, but it's not."
885+ else
886+ _mirrorStorePath path fetchResult.outPath;
887+888}
+6
lib/fileset/tests.sh
···1317expectFailure 'gitTracked null' 'lib.fileset.gitTracked: Expected the argument to be a path, but it'\''s a null instead.'
1318expectFailure 'gitTrackedWith {} null' 'lib.fileset.gitTrackedWith: Expected the second argument to be a path, but it'\''s a null instead.'
13190000001320# The path has to contain a .git directory
1321expectFailure 'gitTracked ./.' 'lib.fileset.gitTracked: Expected the argument \('"$work"'\) to point to a local working tree of a Git repository, but it'\''s not.'
1322expectFailure 'gitTrackedWith {} ./.' 'lib.fileset.gitTrackedWith: Expected the second argument \('"$work"'\) to point to a local working tree of a Git repository, but it'\''s not.'
···1317expectFailure 'gitTracked null' 'lib.fileset.gitTracked: Expected the argument to be a path, but it'\''s a null instead.'
1318expectFailure 'gitTrackedWith {} null' 'lib.fileset.gitTrackedWith: Expected the second argument to be a path, but it'\''s a null instead.'
13191320+# The path must be a directory
1321+touch a
1322+expectFailure 'gitTracked ./a' 'lib.fileset.gitTracked: Expected the argument \('"$work"'/a\) to be a directory, but it'\''s a file instead'
1323+expectFailure 'gitTrackedWith {} ./a' 'lib.fileset.gitTrackedWith: Expected the second argument \('"$work"'/a\) to be a directory, but it'\''s a file instead'
1324+rm -rf -- *
1325+1326# The path has to contain a .git directory
1327expectFailure 'gitTracked ./.' 'lib.fileset.gitTracked: Expected the argument \('"$work"'\) to point to a local working tree of a Git repository, but it'\''s not.'
1328expectFailure 'gitTrackedWith {} ./.' 'lib.fileset.gitTrackedWith: Expected the second argument \('"$work"'\) to point to a local working tree of a Git repository, but it'\''s not.'
+15-19
lib/meta.nix
···4{ lib }:
56let
7- inherit (lib) matchAttrs any all;
8- inherit (builtins) isString;
910in
11rec {
···154 getExe pkgs.mustache-go
155 => "/nix/store/am9ml4f4ywvivxnkiaqwr0hyxka1xjsf-mustache-go-1.3.0/bin/mustache"
156 */
157- getExe = x:
158- let
159- y = x.meta.mainProgram or (
160- # This could be turned into an error when 23.05 is at end of life
161- 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\"."
162- lib.getName
163- x
164- );
165- in
166- getExe' x y;
167168 /* Get the path of a program of a derivation.
169···175 => "/nix/store/5rs48jamq7k6sal98ymj9l4k2bnwq515-imagemagick-7.1.1-15/bin/convert"
176 */
177 getExe' = x: y:
178- assert lib.assertMsg (lib.isDerivation x)
179- "lib.meta.getExe': The first argument is of type ${builtins.typeOf x}, but it should be a derivation instead.";
180- assert lib.assertMsg (lib.isString y)
181- "lib.meta.getExe': The second argument is of type ${builtins.typeOf y}, but it should be a string instead.";
182- assert lib.assertMsg (builtins.length (lib.splitString "/" y) == 1)
183- "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.";
184- "${lib.getBin x}/bin/${y}";
185}
···4{ lib }:
56let
7+ inherit (lib) matchAttrs any all isDerivation getBin assertMsg;
8+ inherit (builtins) isString match typeOf;
910in
11rec {
···154 getExe pkgs.mustache-go
155 => "/nix/store/am9ml4f4ywvivxnkiaqwr0hyxka1xjsf-mustache-go-1.3.0/bin/mustache"
156 */
157+ getExe = x: getExe' x (x.meta.mainProgram or (
158+ # This could be turned into an error when 23.05 is at end of life
159+ 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\"."
160+ lib.getName
161+ x
162+ ));
0000163164 /* Get the path of a program of a derivation.
165···171 => "/nix/store/5rs48jamq7k6sal98ymj9l4k2bnwq515-imagemagick-7.1.1-15/bin/convert"
172 */
173 getExe' = x: y:
174+ assert assertMsg (isDerivation x)
175+ "lib.meta.getExe': The first argument is of type ${typeOf x}, but it should be a derivation instead.";
176+ assert assertMsg (isString y)
177+ "lib.meta.getExe': The second argument is of type ${typeOf y}, but it should be a string instead.";
178+ assert assertMsg (match ".*\/.*" y == null)
179+ "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.";
180+ "${getBin x}/bin/${y}";
181}
+81
lib/path/default.nix
···9 split
10 match
11 typeOf
012 ;
1314 inherit (lib.lists)
···23 take
24 drop
25 ;
002627 inherit (lib.strings)
28 concatStringsSep
···119 if base == dirOf base then { root = base; inherit components; }
120 else recurse ([ (baseNameOf base) ] ++ components) (dirOf base);
121 in recurse [];
0000000000000000000000122123in /* No rec! Add dependencies on this file at the top. */ {
124···320 root = deconstructed.root;
321 subpath = joinRelPath deconstructed.components;
322 };
00000000000000000000000000000000000000000000000000000000323324 /*
325 Whether a value is a valid subpath string.
···9 split
10 match
11 typeOf
12+ storeDir
13 ;
1415 inherit (lib.lists)
···24 take
25 drop
26 ;
27+28+ listHasPrefix = lib.lists.hasPrefix;
2930 inherit (lib.strings)
31 concatStringsSep
···122 if base == dirOf base then { root = base; inherit components; }
123 else recurse ([ (baseNameOf base) ] ++ components) (dirOf base);
124 in recurse [];
125+126+ # The components of the store directory, typically [ "nix" "store" ]
127+ storeDirComponents = splitRelPath ("./" + storeDir);
128+ # The number of store directory components, typically 2
129+ storeDirLength = length storeDirComponents;
130+131+ # Type: [ String ] -> Bool
132+ #
133+ # Whether path components have a store path as a prefix, according to
134+ # https://nixos.org/manual/nix/stable/store/store-path.html#store-path.
135+ componentsHaveStorePathPrefix = components:
136+ # path starts with the store directory (typically /nix/store)
137+ listHasPrefix storeDirComponents components
138+ # is not the store directory itself, meaning there's at least one extra component
139+ && storeDirComponents != components
140+ # and the first component after the store directory has the expected format.
141+ # NOTE: We could change the hash regex to be [0-9a-df-np-sv-z],
142+ # because these are the actual ASCII characters used by Nix's base32 implementation,
143+ # but this is not fully specified, so let's tie this too much to the currently implemented concept of store paths.
144+ # Similar reasoning applies to the validity of the name part.
145+ # We care more about discerning store path-ness on realistic values. Making it airtight would be fragile and slow.
146+ && match ".{32}-.+" (elemAt components storeDirLength) != null;
147148in /* No rec! Add dependencies on this file at the top. */ {
149···345 root = deconstructed.root;
346 subpath = joinRelPath deconstructed.components;
347 };
348+349+ /*
350+ Whether a [path](https://nixos.org/manual/nix/stable/language/values.html#type-path)
351+ has a [store path](https://nixos.org/manual/nix/stable/store/store-path.html#store-path)
352+ as a prefix.
353+354+ :::{.note}
355+ As with all functions of this `lib.path` library, it does not work on paths in strings,
356+ which is how you'd typically get store paths.
357+358+ Instead, this function only handles path values themselves,
359+ which occur when Nix files in the store use relative path expressions.
360+ :::
361+362+ Type:
363+ hasStorePathPrefix :: Path -> Bool
364+365+ Example:
366+ # Subpaths of derivation outputs have a store path as a prefix
367+ hasStorePathPrefix /nix/store/nvl9ic0pj1fpyln3zaqrf4cclbqdfn1j-foo/bar/baz
368+ => true
369+370+ # The store directory itself is not a store path
371+ hasStorePathPrefix /nix/store
372+ => false
373+374+ # Derivation outputs are store paths themselves
375+ hasStorePathPrefix /nix/store/nvl9ic0pj1fpyln3zaqrf4cclbqdfn1j-foo
376+ => true
377+378+ # Paths outside the Nix store don't have a store path prefix
379+ hasStorePathPrefix /home/user
380+ => false
381+382+ # Not all paths under the Nix store are store paths
383+ hasStorePathPrefix /nix/store/.links/10gg8k3rmbw8p7gszarbk7qyd9jwxhcfq9i6s5i0qikx8alkk4hq
384+ => false
385+386+ # Store derivations are also store paths themselves
387+ hasStorePathPrefix /nix/store/nvl9ic0pj1fpyln3zaqrf4cclbqdfn1j-foo.drv
388+ => true
389+ */
390+ hasStorePathPrefix = path:
391+ let
392+ deconstructed = deconstructPath path;
393+ in
394+ assert assertMsg
395+ (isPath path)
396+ "lib.path.hasStorePathPrefix: Argument is of type ${typeOf path}, but a path was expected";
397+ assert assertMsg
398+ # This function likely breaks or needs adjustment if used with other filesystem roots, if they ever get implemented.
399+ # 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.
400+ # See also https://github.com/NixOS/nix/pull/6530#discussion_r1422843117
401+ (deconstructed.root == /. && toString deconstructed.root == "/")
402+ "lib.path.hasStorePathPrefix: Argument has a filesystem root (${toString deconstructed.root}) that's not /, which is currently not supported.";
403+ componentsHaveStorePathPrefix deconstructed.components;
404405 /*
406 Whether a value is a valid subpath string.
+29-1
lib/path/tests/unit.nix
···3{ libpath }:
4let
5 lib = import libpath;
6- inherit (lib.path) hasPrefix removePrefix append splitRoot subpath;
00078 cases = lib.runTests {
9 # Test examples from the lib.path.append documentation
···89 testSplitRootExample4 = {
90 expr = (builtins.tryEval (splitRoot "/foo/bar")).success;
91 expected = false;
000000000000000000000000092 };
9394 # Test examples from the lib.path.subpath.isValid documentation
···89 # is why we use the more obscure "bfd" and not "binutils" for this
90 # choice.
91 else "bfd";
000000092 extensions = lib.optionalAttrs final.hasSharedLibraries {
93 sharedLibrary =
94 if final.isDarwin then ".dylib"
···89 # is why we use the more obscure "bfd" and not "binutils" for this
90 # choice.
91 else "bfd";
92+ # The standard lib directory name that non-nixpkgs binaries distributed
93+ # for this platform normally assume.
94+ libDir = if final.isLinux then
95+ if final.isx86_64 || final.isMips64 || final.isPower64
96+ then "lib64"
97+ else "lib"
98+ else null;
99 extensions = lib.optionalAttrs final.hasSharedLibraries {
100 sharedLibrary =
101 if final.isDarwin then ".dylib"
···1+#!/usr/bin/env nix-shell
2+#!nix-shell -i zsh -p zsh
3+set -euo pipefail
4+5+# cd into nixpkgs' root, get the store path of `systemd.man`
6+cd "$(dirname "$0")/../../.."
7+SYSTEMD_MAN_DIR="$(nix-build -A systemd.man)/share/man"
8+9+# For each manual section
10+for section in {1..8}; do
11+ sec_dir="${SYSTEMD_MAN_DIR}/man${section}"
12+13+ # skip section 3 (library calls)
14+ ! [[ $section -eq 3 ]] || continue
15+16+ # for each manpage in that section (potentially none)
17+ for manpage in ${sec_dir}/*(N); do
18+ # strip the directory prefix and (compressed) manpage suffix
19+ page="$(basename "$manpage" ".${section}.gz")"
20+21+ # if this is the manpage of a service unit
22+ if [[ "$page" =~ ".*\.service" ]]; then
23+ # ... and a manpage exists without the `.service` suffix
24+ potential_alias="${sec_dir}/${page%\.service}.${section}.gz"
25+ ! [[ -e "${potential_alias}" &&
26+ # ... which points to the same file, then skip
27+ "$(gunzip -c "${potential_alias}")" == ".so ${page}.${section}" ]] || continue
28+ fi
29+30+ # else produce a JSON fragment, with the link to the upstream manpage (as HTML)
31+ echo " \"${page}(${section})\": \"https://www.freedesktop.org/software/systemd/man/${page}.html\","
32+ done
33+done
···1+# NixOS {#sec-nixos-state}
2+3+## `/nix` {#sec-state-nix}
4+5+NixOS needs the entirety of `/nix` to be persistent, as it includes:
6+- `/nix/store`, which contains all the system's executables, libraries, and supporting data;
7+- `/nix/var/nix`, which contains:
8+ - the Nix daemon's database;
9+ - roots whose transitive closure is preserved when garbage-collecting the Nix store;
10+ - system-wide and per-user profiles.
11+12+## `/boot` {#sec-state-boot}
13+14+`/boot` should also be persistent, as it contains:
15+- the kernel and initrd which the bootloader loads,
16+- the bootloader's configuration, including the kernel's command-line which
17+ determines the store path to use as system environment.
18+19+20+## Users and groups {#sec-state-users}
21+22+- `/var/lib/nixos` should persist: it holds state needed to generate stable
23+ uids and gids for declaratively-managed users and groups, etc.
24+- `users.mutableUsers` should be false, *or* the following files under `/etc`
25+ should all persist:
26+ - {manpage}`passwd(5)` and {manpage}`group(5)`,
27+ - {manpage}`shadow(5)` and {manpage}`gshadow(5)`,
28+ - {manpage}`subuid(5)` and {manpage}`subgid(5)`.
···1+# Necessary system state {#ch-system-state}
2+3+Normally — on systems with a persistent `rootfs` — system services can persist state to
4+the filesystem without administrator intervention.
5+6+However, it is possible and not-uncommon to create [impermanent systems], whose
7+`rootfs` is either a `tmpfs` or reset during boot. While NixOS itself supports
8+this kind of configuration, special care needs to be taken.
9+10+[impermanent systems]: https://nixos.wiki/wiki/Impermanence
11+12+13+```{=include=} sections
14+nixos-state.section.md
15+systemd-state.section.md
16+zfs-state.section.md
17+```
···1+# systemd {#sec-systemd-state}
2+3+## `machine-id(5)` {#sec-machine-id}
4+5+`systemd` uses per-machine identifier — {manpage}`machine-id(5)` — which must be
6+unique and persistent; otherwise, the system journal may fail to list earlier
7+boots, etc.
8+9+`systemd` generates a random `machine-id(5)` during boot if it does not already exist,
10+and persists it in `/etc/machine-id`. As such, it suffices to make that file persistent.
11+12+Alternatively, it is possible to generate a random `machine-id(5)`; while the
13+specification allows for *any* hex-encoded 128b value, systemd itself uses
14+[UUIDv4], *i.e.* random UUIDs, and it is thus preferable to do so as well, in
15+case some software assumes `machine-id(5)` to be a UUIDv4. Those can be
16+generated with `uuidgen -r | tr -d -` (`tr` being used to remove the dashes).
17+18+Such a `machine-id(5)` can be set by writing it to `/etc/machine-id` or through
19+the kernel's command-line, though NixOS' systemd maintainers [discourage] the
20+latter approach.
21+22+[UUIDv4]: https://en.wikipedia.org/wiki/Universally_unique_identifier#Version_4_(random)
23+[discourage]: https://github.com/NixOS/nixpkgs/pull/268995
24+25+26+## `/var/lib/systemd` {#sec-var-systemd}
27+28+Moreover, `systemd` expects its state directory — `/var/lib/systemd` — to persist, for:
29+- {manpage}`systemd-random-seed(8)`, which loads a 256b “seed” into the kernel's RNG
30+ at boot time, and saves a fresh one during shutdown;
31+- {manpage}`systemd.timer(5)` with `Persistent=yes`, which are then run after boot if
32+ the timer would have triggered during the time the system was shut down;
33+- {manpage}`systemd-coredump(8)` to store core dumps there by default;
34+ (see {manpage}`coredump.conf(5)`)
35+- {manpage}`systemd-timesyncd(8)`;
36+- {manpage}`systemd-backlight(8)` and {manpage}`systemd-rfkill(8)` persist hardware-related
37+ state;
38+- possibly other things, this list is not meant to be exhaustive.
39+40+In any case, making `/var/lib/systemd` persistent is recommended.
41+42+43+## `/var/log/journal/{machine-id}` {#sec-var-journal}
44+45+Lastly, {manpage}`systemd-journald(8)` writes the system's journal in binary
46+form to `/var/log/journal/{machine-id}`; if (locally) persisting the entire log
47+is desired, it is recommended to make all of `/var/log/journal` persistent.
48+49+If not, one can set `Storage=volatile` in {manpage}`journald.conf(5)`
50+([`services.journald.storage = "volatile";`](#opt-services.journald.storage)),
51+which disables journal persistence and causes it to be written to
52+`/run/log/journal`.
···1+# ZFS {#sec-zfs-state}
2+3+When using ZFS, `/etc/zfs/zpool.cache` should be persistent (or a symlink to a persistent
4+location) as it is the default value for the `cachefile` [property](man:zpoolprops(7)).
5+6+This cachefile is used on system startup to discover ZFS pools, so ZFS pools
7+holding the `rootfs` and/or early-boot datasets such as `/nix` can be set to
8+`cachefile=none`.
9+10+In principle, if there are no other pools attached to the system, `zpool.cache`
11+does not need to be persisted; it is however *strongly recommended* to persist
12+it, in case additional pools are added later on, temporarily or permanently:
13+14+While mishandling the cachefile does not lead to data loss by itself, it may
15+cause zpools not to be imported during boot, and services may then write to a
16+location where a dataset was expected to be mounted.
+4
nixos/doc/manual/release-notes/rl-2405.section.md
···1011- `screen`'s module has been cleaned, and will now require you to set `programs.screen.enable` in order to populate `screenrc` and add the program to the environment.
12000013## New Services {#sec-release-24.05-new-services}
1415<!-- To avoid merge conflicts, consider adding your item at an arbitrary place in the list instead. -->
···1011- `screen`'s module has been cleaned, and will now require you to set `programs.screen.enable` in order to populate `screenrc` and add the program to the environment.
1213+- NixOS now installs a stub ELF loader that prints an informative error message when users attempt to run binaries not made for NixOS.
14+ - This can be disabled through the `environment.stub-ld.enable` option.
15+ - If you use `programs.nix-ld.enable`, no changes are needed. The stub will be disabled automatically.
16+17## New Services {#sec-release-24.05-new-services}
1819<!-- To avoid merge conflicts, consider adding your item at an arbitrary place in the list instead. -->
···1435 remote_timeout = mkOpt types.str ''
1436 Timeout for requests to the remote write endpoint.
1437 '';
00001438 write_relabel_configs = mkOpt (types.listOf promTypes.relabel_config) ''
1439 List of remote write relabel configurations.
1440 '';
···1529 '';
1530 remote_timeout = mkOpt types.str ''
1531 Timeout for requests to the remote read endpoint.
00001532 '';
1533 read_recent = mkOpt types.bool ''
1534 Whether reads should be made for queries for time ranges that
···1435 remote_timeout = mkOpt types.str ''
1436 Timeout for requests to the remote write endpoint.
1437 '';
1438+ headers = mkOpt (types.attrsOf types.str) ''
1439+ Custom HTTP headers to be sent along with each remote write request.
1440+ Be aware that headers that are set by Prometheus itself can't be overwritten.
1441+ '';
1442 write_relabel_configs = mkOpt (types.listOf promTypes.relabel_config) ''
1443 List of remote write relabel configurations.
1444 '';
···1533 '';
1534 remote_timeout = mkOpt types.str ''
1535 Timeout for requests to the remote read endpoint.
1536+ '';
1537+ headers = mkOpt (types.attrsOf types.str) ''
1538+ Custom HTTP headers to be sent along with each remote read request.
1539+ Be aware that headers that are set by Prometheus itself can't be overwritten.
1540 '';
1541 read_recent = mkOpt types.bool ''
1542 Whether reads should be made for queries for time ranges that
···20 , extraScripts ? []
21 , ... }@args:
22 let
00000023 # either passthru.scriptName, inferred from scriptPath, or from pname
24 scriptName = (args.passthru or {}).scriptName or (
25 if args ? scriptPath
26 then fileName args.scriptPath
27- else "${pname}.lua"
28 );
29 scriptPath = args.scriptPath or "./${scriptName}";
30 in {
···20 , extraScripts ? []
21 , ... }@args:
22 let
23+ strippedName = with builtins;
24+ let groups = match "mpv[-_](.*)" pname; in
25+ if groups != null
26+ then head groups
27+ else pname
28+ ;
29 # either passthru.scriptName, inferred from scriptPath, or from pname
30 scriptName = (args.passthru or {}).scriptName or (
31 if args ? scriptPath
32 then fileName args.scriptPath
33+ else "${strippedName}.lua"
34 );
35 scriptPath = args.scriptPath or "./${scriptName}";
36 in {
···14 };
1516 meta = {
17+ description = "Automatically skips chapters based on title";
18+ longDescription = ''
19+ MPV script that skips chapters based on their title, categorized using regexes.
20+ The set of skipped categories can be configured globally, or by an auto-profile.
21+ '';
22 homepage = "https://github.com/po5/chapterskip";
23+ license = lib.licenses.unfree; # https://github.com/po5/chapterskip/issues/10
24 maintainers = with lib.maintainers; [ nicoo ];
25 };
26}
···1+{ lib
2+, stdenvNoCC
3+, fetchFromGitHub
4+, nixosTests
5+, gitUpdater
6+}:
7+8+stdenvNoCC.mkDerivation rec {
9+ pname = "noto-fonts-cjk-sans";
10+ version = "2.004";
11+12+ src = fetchFromGitHub {
13+ owner = "notofonts";
14+ repo = "noto-cjk";
15+ rev = "Sans${version}";
16+ hash = "sha256-IgalJkiOAVjNxKaPAQWfb5hKeqclliR4qVXCq63FGWY=";
17+ sparseCheckout = [ "Sans/Variable/OTC" ];
18+ };
19+20+ installPhase = ''
21+ install -m444 -Dt $out/share/fonts/opentype/noto-cjk Sans/Variable/OTC/*.otf.ttc
22+ '';
23+24+ passthru.tests.noto-fonts = nixosTests.noto-fonts;
25+26+ passthru.updateScript = gitUpdater {
27+ rev-prefix = "Sans";
28+ };
29+30+ meta = {
31+ description = "Beautiful and free fonts for CJK languages";
32+ homepage = "https://www.google.com/get/noto/help/cjk/";
33+ longDescription = ''
34+ Noto Sans CJK is a sans typeface designed as
35+ an intermediate style between the modern and traditional. It is
36+ intended to be a multi-purpose digital font for user interface
37+ designs, digital content, reading on laptops, mobile devices, and
38+ electronic books. Noto Sans CJK comprehensively covers
39+ Simplified Chinese, Traditional Chinese, Japanese, and Korean in a
40+ unified font family. It supports regional variants of ideographic
41+ characters for each of the four languages. In addition, it supports
42+ Japanese kana, vertical forms, and variant characters (itaiji); it
43+ supports Korean hangeul — both contemporary and archaic.
44+ '';
45+ license = lib.licenses.ofl;
46+ platforms = lib.platforms.all;
47+ maintainers = with lib.maintainers; [ mathnerd314 emily ];
48+ };
49+}
···1+{ lib
2+, stdenvNoCC
3+, fetchFromGitHub
4+, nixosTests
5+, gitUpdater
6+}:
7+8+stdenvNoCC.mkDerivation rec {
9+ pname = "noto-fonts-cjk-serif";
10+ version = "2.002";
11+12+ src = fetchFromGitHub {
13+ owner = "notofonts";
14+ repo = "noto-cjk";
15+ rev = "Serif${version}";
16+ hash = "sha256-GLjpTAiHfygj1J4AdUVDJh8kykkFOglq+h4kyat5W9s=";
17+ sparseCheckout = [ "Serif/Variable/OTC" ];
18+ };
19+20+ installPhase = ''
21+ install -m444 -Dt $out/share/fonts/opentype/noto-cjk Serif/Variable/OTC/*.otf.ttc
22+ '';
23+24+ passthru.tests.noto-fonts = nixosTests.noto-fonts;
25+26+ passthru.updateScript = gitUpdater {
27+ rev-prefix = "Serif";
28+ };
29+30+ meta = with lib; {
31+ description = "Beautiful and free fonts for CJK languages";
32+ homepage = "https://www.google.com/get/noto/help/cjk/";
33+ longDescription = ''
34+ Noto Serif CJK is a serif typeface designed as
35+ an intermediate style between the modern and traditional. It is
36+ intended to be a multi-purpose digital font for user interface
37+ designs, digital content, reading on laptops, mobile devices, and
38+ electronic books. Noto Serif CJK comprehensively covers
39+ Simplified Chinese, Traditional Chinese, Japanese, and Korean in a
40+ unified font family. It supports regional variants of ideographic
41+ characters for each of the four languages. In addition, it supports
42+ Japanese kana, vertical forms, and variant characters (itaiji); it
43+ supports Korean hangeul — both contemporary and archaic.
44+ '';
45+ license = licenses.ofl;
46+ platforms = platforms.all;
47+ maintainers = with maintainers; [ mathnerd314 emily ];
48+ };
49+}
···1+{ lib
2+, stdenvNoCC
3+, fetchFromGitHub
4+, gitUpdater
5+, variants ? [ ]
6+, suffix ? ""
7+, longDescription ? ''
8+ When text is rendered by a computer, sometimes characters are
9+ displayed as “tofu”. They are little boxes to indicate your device
10+ doesn’t have a font to display the text.
11+ Google has been developing a font family called Noto, which aims to
12+ support all languages with a harmonious look and feel. Noto is
13+ Google’s answer to tofu. The name noto is to convey the idea that
14+ Google’s goal is to see “no more tofu”. Noto has multiple styles and
15+ weights, and freely available to all.
16+ ''
17+}:
18+19+stdenvNoCC.mkDerivation rec {
20+ pname = "noto-fonts${suffix}";
21+ version = "23.11.1";
22+23+ src = fetchFromGitHub {
24+ owner = "notofonts";
25+ repo = "notofonts.github.io";
26+ rev = "noto-monthly-release-${version}";
27+ hash = "sha256-qBHLCOfVBOn9CV194S4cYw9nhHyAe2AUBJHQMvyEfW8=";
28+ };
29+30+ _variants = map (variant: builtins.replaceStrings [ " " ] [ "" ] variant) variants;
31+32+ installPhase = ''
33+ # We check availability in order of variable -> otf -> ttf
34+ # unhinted -- the hinted versions use autohint
35+ # maintaining maximum coverage.
36+ #
37+ # We have a mix of otf and ttf fonts
38+ local out_font=$out/share/fonts/noto
39+ '' + (if _variants == [ ] then ''
40+ for folder in $(ls -d fonts/*/); do
41+ if [[ -d "$folder"unhinted/variable-ttf ]]; then
42+ install -m444 -Dt $out_font "$folder"unhinted/variable-ttf/*.ttf
43+ elif [[ -d "$folder"unhinted/otf ]]; then
44+ install -m444 -Dt $out_font "$folder"unhinted/otf/*.otf
45+ else
46+ install -m444 -Dt $out_font "$folder"unhinted/ttf/*.ttf
47+ fi
48+ done
49+ '' else ''
50+ for variant in $_variants; do
51+ if [[ -d fonts/"$variant"/unhinted/variable-ttf ]]; then
52+ install -m444 -Dt $out_font fonts/"$variant"/unhinted/variable-ttf/*.ttf
53+ elif [[ -d fonts/"$variant"/unhinted/otf ]]; then
54+ install -m444 -Dt $out_font fonts/"$variant"/unhinted/otf/*.otf
55+ else
56+ install -m444 -Dt $out_font fonts/"$variant"/unhinted/ttf/*.ttf
57+ fi
58+ done
59+ '');
60+61+ passthru.updateScript = gitUpdater {
62+ rev-prefix = "noto-monthly-release-";
63+ };
64+65+ meta = {
66+ description = "Beautiful and free fonts for many languages";
67+ homepage = "https://www.google.com/get/noto/";
68+ inherit longDescription;
69+ license = lib.licenses.ofl;
70+ platforms = lib.platforms.all;
71+ maintainers = with lib.maintainers; [ mathnerd314 emily jopejoe1 ];
72+ };
73+}
···258 rm $out/host-linux-x64/libstdc++.so*
259 ''}
260 ${
261- lib.optionalString (lib.versionAtLeast version "11.8")
262 # error: auto-patchelf could not satisfy dependency libtiff.so.5 wanted by /nix/store/.......-cudatoolkit-12.0.1/host-linux-x64/Plugins/imageformats/libqtiff.so
263 # we only ship libtiff.so.6, so let's use qt plugins built by Nix.
264 # TODO: don't copy, come up with a symlink-based "merge"
265 ''
266 rsync ${lib.getLib qt6Packages.qtimageformats}/lib/qt-6/plugins/ $out/host-linux-x64/Plugins/ -aP
000000000000000267 ''
268 }
269···336 wrapProgram "$out/bin/$b" \
337 --set GDK_PIXBUF_MODULE_FILE "$GDK_PIXBUF_MODULE_FILE"
338 done
00000000000000339 '';
340341 # cuda-gdb doesn't run correctly when not using sandboxing, so
···258 rm $out/host-linux-x64/libstdc++.so*
259 ''}
260 ${
261+ lib.optionalString (lib.versionAtLeast version "11.8" && lib.versionOlder version "12")
262 # error: auto-patchelf could not satisfy dependency libtiff.so.5 wanted by /nix/store/.......-cudatoolkit-12.0.1/host-linux-x64/Plugins/imageformats/libqtiff.so
263 # we only ship libtiff.so.6, so let's use qt plugins built by Nix.
264 # TODO: don't copy, come up with a symlink-based "merge"
265 ''
266 rsync ${lib.getLib qt6Packages.qtimageformats}/lib/qt-6/plugins/ $out/host-linux-x64/Plugins/ -aP
267+ ''
268+ }
269+ ${
270+ lib.optionalString (lib.versionAtLeast version "12")
271+ # Use Qt plugins built by Nix.
272+ ''
273+ for qtlib in $out/host-linux-x64/Plugins/*/libq*.so; do
274+ qtdir=$(basename $(dirname $qtlib))
275+ filename=$(basename $qtlib)
276+ for qtpkgdir in ${lib.concatMapStringsSep " " (x: qt6Packages.${x}) ["qtbase" "qtimageformats" "qtsvg" "qtwayland"]}; do
277+ if [ -e $qtpkgdir/lib/qt-6/plugins/$qtdir/$filename ]; then
278+ ln -snf $qtpkgdir/lib/qt-6/plugins/$qtdir/$filename $qtlib
279+ fi
280+ done
281+ done
282 ''
283 }
284···351 wrapProgram "$out/bin/$b" \
352 --set GDK_PIXBUF_MODULE_FILE "$GDK_PIXBUF_MODULE_FILE"
353 done
354+ ${
355+ lib.optionalString (lib.versionAtLeast version "12")
356+ # Check we don't have any lurking vendored qt libraries that weren't
357+ # replaced during installPhase
358+ ''
359+ qtlibfiles=$(find $out -name "libq*.so" -type f)
360+ if [ ! -z "$qtlibfiles" ]; then
361+ echo "Found unexpected vendored Qt library files in $out" >&2
362+ echo $qtlibfiles >&2
363+ echo "These should be replaced with symlinks in installPhase" >&2
364+ exit 1
365+ fi
366+ ''
367+ }
368 '';
369370 # cuda-gdb doesn't run correctly when not using sandboxing, so
···170 ''
171 # Handle the existence of libPath, which requires us to re-arrange the lib directory
172 + strings.optionalString (libPath != null) ''
173- if [[ ! -d "${libPath}" ]] ; then
174- echo "${finalAttrs.pname}: ${libPath} does not exist, only found:" >&2
175- find "$(dirname ${libPath})"/ -maxdepth 1 >&2
0176 echo "This release might not support your CUDA version" >&2
177 exit 1
178 fi
179- mv "lib/${libPath}" lib_new
0180 rm -r lib
181 mv lib_new lib
182 ''
···187 ''
188 # Move the outputs into their respective outputs.
189 + strings.concatMapStringsSep "\n" mkMoveToOutputCommand (builtins.tail finalAttrs.outputs)
000190 # Post-install hook
191 + ''
192 runHook postInstall
···170 ''
171 # Handle the existence of libPath, which requires us to re-arrange the lib directory
172 + strings.optionalString (libPath != null) ''
173+ full_lib_path="lib/${libPath}"
174+ if [[ ! -d "$full_lib_path" ]] ; then
175+ echo "${finalAttrs.pname}: '$full_lib_path' does not exist, only found:" >&2
176+ find lib/ -mindepth 1 -maxdepth 1 >&2
177 echo "This release might not support your CUDA version" >&2
178 exit 1
179 fi
180+ echo "Making libPath '$full_lib_path' the root of lib" >&2
181+ mv "$full_lib_path" lib_new
182 rm -r lib
183 mv lib_new lib
184 ''
···189 ''
190 # Move the outputs into their respective outputs.
191 + strings.concatMapStringsSep "\n" mkMoveToOutputCommand (builtins.tail finalAttrs.outputs)
192+ # Add a newline to the end of the installPhase, so that the post-install hook doesn't
193+ # get concatenated with the last moveToOutput command.
194+ + "\n"
195 # Post-install hook
196 + ''
197 runHook postInstall
···31 license = licenses.asl20;
32 mainProgram = "iwasm";
33 maintainers = with maintainers; [ ereslibre ];
34- # TODO (ereslibre): this derivation should be improved to support
35- # more platforms.
36- broken = !stdenv.isLinux;
37 };
38})
···40 version = "146";
41 buildVersion = makeBuildVersion version;
420043 selectedGlew = if enableWayland then glew-egl else glew;
4445 Mindustry = fetchFromGitHub {
···114 inherit version unpackPhase patches;
115 postPatch = cleanupMindustrySrc;
116117- nativeBuildInputs = [ gradle perl ];
118 # Here we download dependencies for both the server and the client so
119 # we only have to specify one hash for 'deps'. Deps can be garbage
120 # collected after the build, so this is not really an issue.
···149 ];
150 nativeBuildInputs = [
151 pkg-config
152- gradle
153 makeWrapper
154 jdk
155 ] ++ lib.optionals enableClient [
···40 version = "146";
41 buildVersion = makeBuildVersion version;
4243+ gradleWithJdk = gradle.override { java = jdk; };
44+45 selectedGlew = if enableWayland then glew-egl else glew;
4647 Mindustry = fetchFromGitHub {
···116 inherit version unpackPhase patches;
117 postPatch = cleanupMindustrySrc;
118119+ nativeBuildInputs = [ gradleWithJdk perl ];
120 # Here we download dependencies for both the server and the client so
121 # we only have to specify one hash for 'deps'. Deps can be garbage
122 # collected after the build, so this is not really an issue.
···151 ];
152 nativeBuildInputs = [
153 pkg-config
154+ gradleWithJdk
155 makeWrapper
156 jdk
157 ] ++ lib.optionals enableClient [
···56rustPlatform.buildRustPackage rec {
7 pname = "automatic-timezoned";
8- version = "1.0.131";
910 src = fetchFromGitHub {
11 owner = "maxbrunet";
12 repo = pname;
13 rev = "v${version}";
14- sha256 = "sha256-92OpvUt+0iN+UdEGjDdVCjUUlbuOjUgOjc+DGMUnx9U=";
15 };
1617- cargoHash = "sha256-FaQwxt3XcDOXlzcKEdMyE9TpmGykQOnJdxtM3EqMpfU=";
1819 meta = with lib; {
20 description = "Automatically update system timezone based on location";
···56rustPlatform.buildRustPackage rec {
7 pname = "automatic-timezoned";
8+ version = "1.0.137";
910 src = fetchFromGitHub {
11 owner = "maxbrunet";
12 repo = pname;
13 rev = "v${version}";
14+ sha256 = "sha256-+/P+pt79kGIr399c3oTwqbvtMc1nJNRhBYmYJsLrmDg=";
15 };
1617+ cargoHash = "sha256-QCWlyuoogrU09JvP+X5If1KcYjaoL0zVhBexXwSqc1U=";
1819 meta = with lib; {
20 description = "Automatically update system timezone based on location";
+3
pkgs/top-level/aliases.nix
···372 hepmc = throw "'hepmc' has been renamed to/replaced by 'hepmc2'"; # Converted to throw 2023-09-10
373 hip = throw "'hip' has been removed in favor of 'rocmPackages.clr'"; # Added 2023-10-08
374 hipcc = throw "'hipcc' has been replaced with 'rocmPackages.hipcc'"; # Added 2023-10-08
0375 hipify = throw "'hipify' has been replaced with 'rocmPackages.hipify'"; # Added 2023-10-08
376 hipcub = throw "'hipcub' has been replaced with 'rocmPackages.hipcub'"; # Added 2023-10-08
377 hipsparse = throw "'hipsparse' has been replaced with 'rocmPackages.hipsparse'"; # Added 2023-10-08
···598 miopen-hip = throw "'miopen-hip' has been replaced with 'rocmPackages.miopen-hip'"; # Added 2023-10-08
599 miopen-opencl = throw "'miopen-opencl' has been replaced with 'rocmPackages.miopen-opencl'"; # Added 2023-10-08
600 mime-types = mailcap; # Added 2022-01-21
00601 minizip2 = pkgs.minizip-ng; # Added 2022-12-28
602 mirage-im = throw "'mirage-im' has been removed, as it was broken and unmaintained"; # Added 2023-11-26
603 monero = monero-cli; # Added 2021-11-28
···372 hepmc = throw "'hepmc' has been renamed to/replaced by 'hepmc2'"; # Converted to throw 2023-09-10
373 hip = throw "'hip' has been removed in favor of 'rocmPackages.clr'"; # Added 2023-10-08
374 hipcc = throw "'hipcc' has been replaced with 'rocmPackages.hipcc'"; # Added 2023-10-08
375+ hipchat = throw "'hipchat' has been discontinued since 2019; upstream recommends Slack."; # Added 2023-12-02
376 hipify = throw "'hipify' has been replaced with 'rocmPackages.hipify'"; # Added 2023-10-08
377 hipcub = throw "'hipcub' has been replaced with 'rocmPackages.hipcub'"; # Added 2023-10-08
378 hipsparse = throw "'hipsparse' has been replaced with 'rocmPackages.hipsparse'"; # Added 2023-10-08
···599 miopen-hip = throw "'miopen-hip' has been replaced with 'rocmPackages.miopen-hip'"; # Added 2023-10-08
600 miopen-opencl = throw "'miopen-opencl' has been replaced with 'rocmPackages.miopen-opencl'"; # Added 2023-10-08
601 mime-types = mailcap; # Added 2022-01-21
602+ minetestclient_5 = minetestclient; # Added 2023-12-11
603+ minetestserver_5 = minetestserver; # Added 2023-12-11
604 minizip2 = pkgs.minizip-ng; # Added 2022-12-28
605 mirage-im = throw "'mirage-im' has been removed, as it was broken and unmaintained"; # Added 2023-11-26
606 monero = monero-cli; # Added 2021-11-28