diff --git a/.github/ISSUE_TEMPLATE/missing_documentation.md b/.github/ISSUE_TEMPLATE/missing_documentation.md
index 942d7a971..be3f6af97 100644
--- a/.github/ISSUE_TEMPLATE/missing_documentation.md
+++ b/.github/ISSUE_TEMPLATE/missing_documentation.md
@@ -11,6 +11,10 @@ assignees: ''
+## Proposal
+
+
+
## Checklist
@@ -22,10 +26,6 @@ assignees: ''
[source]: https://github.com/NixOS/nix/tree/master/doc/manual/src
[open documentation issues and pull requests]: https://github.com/NixOS/nix/labels/documentation
-## Proposal
-
-
-
## Priorities
Add :+1: to [issues you find important](https://github.com/NixOS/nix/issues?q=is%3Aissue+is%3Aopen+sort%3Areactions-%2B1-desc).
diff --git a/.github/workflows/backport.yml b/.github/workflows/backport.yml
index 37966bab2..816474ed5 100644
--- a/.github/workflows/backport.yml
+++ b/.github/workflows/backport.yml
@@ -21,7 +21,7 @@ jobs:
fetch-depth: 0
- name: Create backport PRs
# should be kept in sync with `version`
- uses: zeebe-io/backport-action@v1.3.0
+ uses: zeebe-io/backport-action@v1.3.1
with:
# Config README: https://github.com/zeebe-io/backport-action#backport-action
github_token: ${{ secrets.GITHUB_TOKEN }}
diff --git a/doc/manual/src/command-ref/nix-channel.md b/doc/manual/src/command-ref/nix-channel.md
index a210583ae..025f758e7 100644
--- a/doc/manual/src/command-ref/nix-channel.md
+++ b/doc/manual/src/command-ref/nix-channel.md
@@ -4,7 +4,7 @@
# Synopsis
-`nix-channel` {`--add` url [*name*] | `--remove` *name* | `--list` | `--update` [*names…*] | `--rollback` [*generation*] }
+`nix-channel` {`--add` url [*name*] | `--remove` *name* | `--list` | `--update` [*names…*] | `--list-generations` | `--rollback` [*generation*] }
# Description
@@ -39,6 +39,15 @@ This command has the following operations:
for `nix-env` operations (by symlinking them from the directory
`~/.nix-defexpr`).
+ - `--list-generations`\
+ Prints a list of all the current existing generations for the
+ channel profile.
+
+ Works the same way as
+ ```
+ nix-env --profile /nix/var/nix/profiles/per-user/$USER/channels --list-generations
+ ```
+
- `--rollback` \[*generation*\]\
Reverts the previous call to `nix-channel
--update`. Optionally, you can specify a specific channel generation
diff --git a/doc/manual/src/command-ref/nix-collect-garbage.md b/doc/manual/src/command-ref/nix-collect-garbage.md
index 51db5fc67..a679ceaf7 100644
--- a/doc/manual/src/command-ref/nix-collect-garbage.md
+++ b/doc/manual/src/command-ref/nix-collect-garbage.md
@@ -1,6 +1,6 @@
# Name
-`nix-collect-garbage` - delete unreachable store paths
+`nix-collect-garbage` - delete unreachable [store objects]
# Synopsis
@@ -8,17 +8,57 @@
# Description
-The command `nix-collect-garbage` is mostly an alias of [`nix-store
---gc`](@docroot@/command-ref/nix-store/gc.md), that is, it deletes all
-unreachable paths in the Nix store to clean up your system. However,
-it provides two additional options: `-d` (`--delete-old`), which
-deletes all old generations of all profiles in `/nix/var/nix/profiles`
-by invoking `nix-env --delete-generations old` on all profiles (of
-course, this makes rollbacks to previous configurations impossible);
-and `--delete-older-than` *period*, where period is a value such as
-`30d`, which deletes all generations older than the specified number
-of days in all profiles in `/nix/var/nix/profiles` (except for the
-generations that were active at that point in time).
+The command `nix-collect-garbage` is mostly an alias of [`nix-store --gc`](@docroot@/command-ref/nix-store/gc.md).
+That is, it deletes all unreachable [store objects] in the Nix store to clean up your system.
+
+However, it provides two additional options,
+[`--delete-old`](#opt-delete-old) and [`--delete-older-than`](#opt-delete-older-than),
+which also delete old [profiles], allowing potentially more [store objects] to be deleted because profiles are also garbage collection roots.
+These options are the equivalent of running
+[`nix-env --delete-generations`](@docroot@/command-ref/nix-env/delete-generations.md)
+with various augments on multiple profiles,
+prior to running `nix-collect-garbage` (or just `nix-store --gc`) without any flags.
+
+> **Note**
+>
+> Deleting previous configurations makes rollbacks to them impossible.
+
+These flags should be used with care, because they potentially delete generations of profiles used by other users on the system.
+
+## Locations searched for profiles
+
+`nix-collect-garbage` cannot know about all profiles; that information doesn't exist.
+Instead, it looks in a few locations, and acts on all profiles it finds there:
+
+1. The default profile locations as specified in the [profiles] section of the manual.
+
+2. > **NOTE**
+ >
+ > Not stable; subject to change
+ >
+ > Do not rely on this functionality; it just exists for migration purposes and is may change in the future.
+ > These deprecated paths remain a private implementation detail of Nix.
+
+ `$NIX_STATE_DIR/profiles` and `$NIX_STATE_DIR/profiles/per-user`.
+
+ With the exception of `$NIX_STATE_DIR/profiles/per-user/root` and `$NIX_STATE_DIR/profiles/default`, these directories are no longer used by other commands.
+ `nix-collect-garbage` looks there anyways in order to clean up profiles from older versions of Nix.
+
+# Options
+
+These options are for deleting old [profiles] prior to deleting unreachable [store objects].
+
+- [`--delete-old`](#opt-delete-old) / `-d`\
+ Delete all old generations of profiles.
+
+ This is the equivalent of invoking `nix-env --delete-generations old` on each found profile.
+
+- [`--delete-older-than`](#opt-delete-older-than) *period*\
+ Delete all generations of profiles older than the specified amount (except for the generations that were active at that point in time).
+ *period* is a value such as `30d`, which would mean 30 days.
+
+ This is the equivalent of invoking [`nix-env --delete-generations `](@docroot@/command-ref/nix-env/delete-generations.md#generations-days) on each found profile.
+ See the documentation of that command for additional information about the *period* argument.
{{#include ./opt-common.md}}
@@ -32,3 +72,6 @@ generations of each profile, do
```console
$ nix-collect-garbage -d
```
+
+[profiles]: @docroot@/command-ref/files/profiles.md
+[store objects]: @docroot@/glossary.md#gloss-store-object
diff --git a/doc/manual/src/command-ref/nix-env/delete-generations.md b/doc/manual/src/command-ref/nix-env/delete-generations.md
index 92cb7f0d9..d828a5b9e 100644
--- a/doc/manual/src/command-ref/nix-env/delete-generations.md
+++ b/doc/manual/src/command-ref/nix-env/delete-generations.md
@@ -9,14 +9,39 @@
# Description
This operation deletes the specified generations of the current profile.
-The generations can be a list of generation numbers, the special value
-`old` to delete all non-current generations, a value such as `30d` to
-delete all generations older than the specified number of days (except
-for the generation that was active at that point in time), or a value
-such as `+5` to keep the last `5` generations ignoring any newer than
-current, e.g., if `30` is the current generation `+5` will delete
-generation `25` and all older generations. Periodically deleting old
-generations is important to make garbage collection effective.
+
+*generations* can be a one of the following:
+
+- `...`:\
+ A list of generation numbers, each one a separate command-line argument.
+
+ Delete exactly the profile generations given by their generation number.
+ Deleting the current generation is not allowed.
+
+- The special value `old`
+
+ Delete all generations older than the current one.
+
+- `d`:\
+ The last *days* days
+
+ *Example*: `30d`
+
+ Delete all generations older than *days* days.
+ The generation that was active at that point in time is excluded, and will not be deleted.
+
+- `+`:\
+ The last *count* generations up to the present
+
+ *Example*: `+5`
+
+ Keep the last *count* generations, along with any newer than current.
+
+Periodically deleting old generations is important to make garbage collection
+effective.
+The is because profiles are also garbage collection roots — any [store object] reachable from a profile is "alive" and ineligible for deletion.
+
+[store object]: @docroot@/glossary.md#gloss-store-object
{{#include ./opt-common.md}}
@@ -28,19 +53,35 @@ generations is important to make garbage collection effective.
# Examples
+## Delete explicit generation numbers
+
```console
$ nix-env --delete-generations 3 4 8
```
+Delete the generations numbered 3, 4, and 8, so long as the current active generation is not any of those.
+
+## Keep most-recent by count count
+
```console
$ nix-env --delete-generations +5
```
+Suppose `30` is the current generation, and we currently have generations numbered `20` through `32`.
+
+Then this command will delete generations `20` through `25` (`<= 30 - 5`),
+and keep generations `26` through `31` (`> 30 - 5`).
+
+## Keep most-recent in days
+
```console
$ nix-env --delete-generations 30d
```
+This command will delete all generations older than 30 days, except for the generation that was active 30 days ago (if it currently exists).
+
+## Delete all older
+
```console
$ nix-env --profile other_profile --delete-generations old
```
-
diff --git a/doc/manual/src/glossary.md b/doc/manual/src/glossary.md
index e142bd415..47a484826 100644
--- a/doc/manual/src/glossary.md
+++ b/doc/manual/src/glossary.md
@@ -112,9 +112,10 @@
from some server.
- [substituter]{#gloss-substituter}\
- A *substituter* is an additional store from which Nix will
- copy store objects it doesn't have. For details, see the
- [`substituters` option](./command-ref/conf-file.md#conf-substituters).
+ An additional [store]{#gloss-store} from which Nix can obtain store objects instead of building them.
+ Often the substituter is a [binary cache](#gloss-binary-cache), but any store can serve as substituter.
+
+ See the [`substituters` configuration option](./command-ref/conf-file.md#conf-substituters) for details.
[substituter]: #gloss-substituter
diff --git a/doc/manual/src/installation/prerequisites-source.md b/doc/manual/src/installation/prerequisites-source.md
index 5a708f11b..d4babf1ea 100644
--- a/doc/manual/src/installation/prerequisites-source.md
+++ b/doc/manual/src/installation/prerequisites-source.md
@@ -10,7 +10,7 @@
- Bash Shell. The `./configure` script relies on bashisms, so Bash is
required.
- - A version of GCC or Clang that supports C++17.
+ - A version of GCC or Clang that supports C++20.
- `pkg-config` to locate dependencies. If your distribution does not
provide it, you can get it from
diff --git a/doc/manual/src/release-notes/rl-next.md b/doc/manual/src/release-notes/rl-next.md
index 78ae99f4b..bde9057c6 100644
--- a/doc/manual/src/release-notes/rl-next.md
+++ b/doc/manual/src/release-notes/rl-next.md
@@ -1,2 +1,3 @@
# Release X.Y (202?-??-??)
+- [`nix-channel`](../command-ref/nix-channel.md) now supports a `--list-generations` subcommand
diff --git a/maintainers/upload-release.pl b/maintainers/upload-release.pl
index 07b19c13f..ebc536f12 100755
--- a/maintainers/upload-release.pl
+++ b/maintainers/upload-release.pl
@@ -80,6 +80,38 @@ my $s3_us = Net::Amazon::S3->new(
my $channelsBucket = $s3_us->bucket($channelsBucketName) or die;
+sub getStorePath {
+ my ($jobName, $output) = @_;
+ my $buildInfo = decode_json(fetch("$evalUrl/job/$jobName", 'application/json'));
+ return $buildInfo->{buildoutputs}->{$output or "out"}->{path} or die "cannot get store path for '$jobName'";
+}
+
+sub copyManual {
+ my $manual = getStorePath("build.x86_64-linux", "doc");
+ print "$manual\n";
+
+ my $manualNar = "$tmpDir/$releaseName-manual.nar.xz";
+ print "$manualNar\n";
+
+ unless (-e $manualNar) {
+ system("NIX_REMOTE=$binaryCache nix store dump-path '$manual' | xz > '$manualNar'.tmp") == 0
+ or die "unable to fetch $manual\n";
+ rename("$manualNar.tmp", $manualNar) or die;
+ }
+
+ unless (-e "$tmpDir/manual") {
+ system("xz -d < '$manualNar' | nix-store --restore $tmpDir/manual.tmp") == 0
+ or die "unable to unpack $manualNar\n";
+ rename("$tmpDir/manual.tmp/share/doc/nix/manual", "$tmpDir/manual") or die;
+ system("rm -rf '$tmpDir/manual.tmp'") == 0 or die;
+ }
+
+ system("aws s3 sync '$tmpDir/manual' s3://$releasesBucketName/$releaseDir/manual") == 0
+ or die "syncing manual to S3\n";
+}
+
+copyManual;
+
sub downloadFile {
my ($jobName, $productNr, $dstName) = @_;
@@ -179,9 +211,20 @@ if ($isLatest) {
system("docker manifest push nixos/nix:latest") == 0 or die;
}
+# Upload nix-fallback-paths.nix.
+write_file("$tmpDir/fallback-paths.nix",
+ "{\n" .
+ " x86_64-linux = \"" . getStorePath("build.x86_64-linux") . "\";\n" .
+ " i686-linux = \"" . getStorePath("build.i686-linux") . "\";\n" .
+ " aarch64-linux = \"" . getStorePath("build.aarch64-linux") . "\";\n" .
+ " x86_64-darwin = \"" . getStorePath("build.x86_64-darwin") . "\";\n" .
+ " aarch64-darwin = \"" . getStorePath("build.aarch64-darwin") . "\";\n" .
+ "}\n");
+
# Upload release files to S3.
for my $fn (glob "$tmpDir/*") {
my $name = basename($fn);
+ next if $name eq "manual";
my $dstKey = "$releaseDir/" . $name;
unless (defined $releasesBucket->head_key($dstKey)) {
print STDERR "uploading $fn to s3://$releasesBucketName/$dstKey...\n";
@@ -189,8 +232,7 @@ for my $fn (glob "$tmpDir/*") {
my $configuration = ();
$configuration->{content_type} = "application/octet-stream";
- if ($fn =~ /.sha256|install/) {
- # Text files
+ if ($fn =~ /.sha256|install|\.nix$/) {
$configuration->{content_type} = "text/plain";
}
@@ -199,24 +241,6 @@ for my $fn (glob "$tmpDir/*") {
}
}
-# Print new nix-fallback-paths.nix.
-if ($isLatest) {
- sub getStorePath {
- my ($jobName) = @_;
- my $buildInfo = decode_json(fetch("$evalUrl/job/$jobName", 'application/json'));
- return $buildInfo->{buildoutputs}->{out}->{path} or die "cannot get store path for '$jobName'";
- }
-
- print STDERR "nixos/modules/installer/tools/nix-fallback-paths.nix:\n" .
- "{\n" .
- " x86_64-linux = \"" . getStorePath("build.x86_64-linux") . "\";\n" .
- " i686-linux = \"" . getStorePath("build.i686-linux") . "\";\n" .
- " aarch64-linux = \"" . getStorePath("build.aarch64-linux") . "\";\n" .
- " x86_64-darwin = \"" . getStorePath("build.x86_64-darwin") . "\";\n" .
- " aarch64-darwin = \"" . getStorePath("build.aarch64-darwin") . "\";\n" .
- "}\n";
-}
-
# Update the "latest" symlink.
$channelsBucket->add_key(
"nix-latest/install", "",
diff --git a/scripts/install-darwin-multi-user.sh b/scripts/install-darwin-multi-user.sh
index 5111a5dde..0326d3415 100644
--- a/scripts/install-darwin-multi-user.sh
+++ b/scripts/install-darwin-multi-user.sh
@@ -100,7 +100,7 @@ poly_extra_try_me_commands() {
poly_configure_nix_daemon_service() {
task "Setting up the nix-daemon LaunchDaemon"
_sudo "to set up the nix-daemon as a LaunchDaemon" \
- /bin/cp -f "/nix/var/nix/profiles/default$NIX_DAEMON_DEST" "$NIX_DAEMON_DEST"
+ /usr/bin/install -m -rw-r--r-- "/nix/var/nix/profiles/default$NIX_DAEMON_DEST" "$NIX_DAEMON_DEST"
_sudo "to load the LaunchDaemon plist for nix-daemon" \
launchctl load /Library/LaunchDaemons/org.nixos.nix-daemon.plist
diff --git a/scripts/install-multi-user.sh b/scripts/install-multi-user.sh
index 79deb2819..656769d84 100644
--- a/scripts/install-multi-user.sh
+++ b/scripts/install-multi-user.sh
@@ -700,6 +700,10 @@ EOF
}
welcome_to_nix() {
+ local -r NIX_UID_RANGES="${NIX_FIRST_BUILD_UID}..$((NIX_FIRST_BUILD_UID + NIX_USER_COUNT - 1))"
+ local -r RANGE_TEXT=$(echo -ne "${BLUE}(uids [${NIX_UID_RANGES}])${ESC}")
+ local -r GROUP_TEXT=$(echo -ne "${BLUE}(gid ${NIX_BUILD_GROUP_ID})${ESC}")
+
ok "Welcome to the Multi-User Nix Installation"
cat < store)
while (std::cin >> word) {
rawInstallables.emplace_back(std::move(word));
}
+ } else {
+ applyDefaultInstallables(rawInstallables);
}
-
- applyDefaultInstallables(rawInstallables);
run(store, std::move(rawInstallables));
}
diff --git a/src/libexpr/eval.hh b/src/libexpr/eval.hh
index d6f4560a5..ea1f5975b 100644
--- a/src/libexpr/eval.hh
+++ b/src/libexpr/eval.hh
@@ -741,7 +741,7 @@ struct EvalSettings : Config
If set to `true`, the Nix evaluator will not allow access to any
files outside of the Nix search path (as set via the `NIX_PATH`
environment variable or the `-I` option), or to URIs outside of
- `allowed-uri`. The default is `false`.
+ `allowed-uris`. The default is `false`.
)"};
Setting pureEval{this, false, "pure-eval",
diff --git a/src/libexpr/primops.cc b/src/libexpr/primops.cc
index 42efca4e7..5b2f7e8b7 100644
--- a/src/libexpr/primops.cc
+++ b/src/libexpr/primops.cc
@@ -6,7 +6,7 @@
#include "globals.hh"
#include "json-to-value.hh"
#include "names.hh"
-#include "references.hh"
+#include "path-references.hh"
#include "store-api.hh"
#include "util.hh"
#include "value-to-json.hh"
@@ -4058,18 +4058,6 @@ static RegisterPrimOp primop_splitVersion({
RegisterPrimOp::PrimOps * RegisterPrimOp::primOps;
-RegisterPrimOp::RegisterPrimOp(std::string name, size_t arity, PrimOpFun fun)
-{
- if (!primOps) primOps = new PrimOps;
- primOps->push_back({
- .name = name,
- .args = {},
- .arity = arity,
- .fun = fun,
- });
-}
-
-
RegisterPrimOp::RegisterPrimOp(Info && info)
{
if (!primOps) primOps = new PrimOps;
diff --git a/src/libexpr/primops.hh b/src/libexpr/primops.hh
index 4ae73fe1f..73b7b866c 100644
--- a/src/libexpr/primops.hh
+++ b/src/libexpr/primops.hh
@@ -28,11 +28,6 @@ struct RegisterPrimOp
* will get called during EvalState initialization, so there
* may be primops not yet added and builtins is not yet sorted.
*/
- RegisterPrimOp(
- std::string name,
- size_t arity,
- PrimOpFun fun);
-
RegisterPrimOp(Info && info);
};
diff --git a/src/libexpr/primops/context.cc b/src/libexpr/primops/context.cc
index 07bf400cf..8b3468009 100644
--- a/src/libexpr/primops/context.cc
+++ b/src/libexpr/primops/context.cc
@@ -12,7 +12,11 @@ static void prim_unsafeDiscardStringContext(EvalState & state, const PosIdx pos,
v.mkString(*s);
}
-static RegisterPrimOp primop_unsafeDiscardStringContext("__unsafeDiscardStringContext", 1, prim_unsafeDiscardStringContext);
+static RegisterPrimOp primop_unsafeDiscardStringContext({
+ .name = "__unsafeDiscardStringContext",
+ .arity = 1,
+ .fun = prim_unsafeDiscardStringContext
+});
static void prim_hasContext(EvalState & state, const PosIdx pos, Value * * args, Value & v)
@@ -22,7 +26,16 @@ static void prim_hasContext(EvalState & state, const PosIdx pos, Value * * args,
v.mkBool(!context.empty());
}
-static RegisterPrimOp primop_hasContext("__hasContext", 1, prim_hasContext);
+static RegisterPrimOp primop_hasContext({
+ .name = "__hasContext",
+ .args = {"s"},
+ .doc = R"(
+ Return `true` if string *s* has a non-empty context. The
+ context can be obtained with
+ [`getContext`](#builtins-getContext).
+ )",
+ .fun = prim_hasContext
+});
/* Sometimes we want to pass a derivation path (i.e. pkg.drvPath) to a
@@ -51,7 +64,11 @@ static void prim_unsafeDiscardOutputDependency(EvalState & state, const PosIdx p
v.mkString(*s, context2);
}
-static RegisterPrimOp primop_unsafeDiscardOutputDependency("__unsafeDiscardOutputDependency", 1, prim_unsafeDiscardOutputDependency);
+static RegisterPrimOp primop_unsafeDiscardOutputDependency({
+ .name = "__unsafeDiscardOutputDependency",
+ .arity = 1,
+ .fun = prim_unsafeDiscardOutputDependency
+});
/* Extract the context of a string as a structured Nix value.
@@ -119,7 +136,30 @@ static void prim_getContext(EvalState & state, const PosIdx pos, Value * * args,
v.mkAttrs(attrs);
}
-static RegisterPrimOp primop_getContext("__getContext", 1, prim_getContext);
+static RegisterPrimOp primop_getContext({
+ .name = "__getContext",
+ .args = {"s"},
+ .doc = R"(
+ Return the string context of *s*.
+
+ The string context tracks references to derivations within a string.
+ It is represented as an attribute set of [store derivation](@docroot@/glossary.md#gloss-store-derivation) paths mapping to output names.
+
+ Using [string interpolation](@docroot@/language/string-interpolation.md) on a derivation will add that derivation to the string context.
+ For example,
+
+ ```nix
+ builtins.getContext "${derivation { name = "a"; builder = "b"; system = "c"; }}"
+ ```
+
+ evaluates to
+
+ ```
+ { "/nix/store/arhvjaf6zmlyn8vh8fgn55rpwnxq0n7l-a.drv" = { outputs = [ "out" ]; }; }
+ ```
+ )",
+ .fun = prim_getContext
+});
/* Append the given context to a given string.
@@ -192,6 +232,10 @@ static void prim_appendContext(EvalState & state, const PosIdx pos, Value * * ar
v.mkString(orig, context);
}
-static RegisterPrimOp primop_appendContext("__appendContext", 2, prim_appendContext);
+static RegisterPrimOp primop_appendContext({
+ .name = "__appendContext",
+ .arity = 2,
+ .fun = prim_appendContext
+});
}
diff --git a/src/libexpr/primops/fetchMercurial.cc b/src/libexpr/primops/fetchMercurial.cc
index 2c0d98e74..322692b52 100644
--- a/src/libexpr/primops/fetchMercurial.cc
+++ b/src/libexpr/primops/fetchMercurial.cc
@@ -88,6 +88,10 @@ static void prim_fetchMercurial(EvalState & state, const PosIdx pos, Value * * a
state.allowPath(tree.storePath);
}
-static RegisterPrimOp r_fetchMercurial("fetchMercurial", 1, prim_fetchMercurial);
+static RegisterPrimOp r_fetchMercurial({
+ .name = "fetchMercurial",
+ .arity = 1,
+ .fun = prim_fetchMercurial
+});
}
diff --git a/src/libexpr/primops/fetchTree.cc b/src/libexpr/primops/fetchTree.cc
index fe880aaa8..be8159cc8 100644
--- a/src/libexpr/primops/fetchTree.cc
+++ b/src/libexpr/primops/fetchTree.cc
@@ -194,7 +194,11 @@ static void prim_fetchTree(EvalState & state, const PosIdx pos, Value * * args,
}
// FIXME: document
-static RegisterPrimOp primop_fetchTree("fetchTree", 1, prim_fetchTree);
+static RegisterPrimOp primop_fetchTree({
+ .name = "fetchTree",
+ .arity = 1,
+ .fun = prim_fetchTree
+});
static void fetch(EvalState & state, const PosIdx pos, Value * * args, Value & v,
const std::string & who, bool unpack, std::string name)
diff --git a/src/libexpr/primops/fromTOML.cc b/src/libexpr/primops/fromTOML.cc
index 8a5231781..2f4d4022e 100644
--- a/src/libexpr/primops/fromTOML.cc
+++ b/src/libexpr/primops/fromTOML.cc
@@ -3,6 +3,8 @@
#include "../../toml11/toml.hpp"
+#include
+
namespace nix {
static void prim_fromTOML(EvalState & state, const PosIdx pos, Value * * args, Value & val)
@@ -58,8 +60,18 @@ static void prim_fromTOML(EvalState & state, const PosIdx pos, Value * * args, V
case toml::value_t::offset_datetime:
case toml::value_t::local_date:
case toml::value_t::local_time:
- // We fail since Nix doesn't have date and time types
- throw std::runtime_error("Dates and times are not supported");
+ {
+ if (experimentalFeatureSettings.isEnabled(Xp::ParseTomlTimestamps)) {
+ auto attrs = state.buildBindings(2);
+ attrs.alloc("_type").mkString("timestamp");
+ std::ostringstream s;
+ s << t;
+ attrs.alloc("value").mkString(s.str());
+ v.mkAttrs(attrs);
+ } else {
+ throw std::runtime_error("Dates and times are not supported");
+ }
+ }
break;;
case toml::value_t::empty:
v.mkNull();
@@ -78,6 +90,24 @@ static void prim_fromTOML(EvalState & state, const PosIdx pos, Value * * args, V
}
}
-static RegisterPrimOp primop_fromTOML("fromTOML", 1, prim_fromTOML);
+static RegisterPrimOp primop_fromTOML({
+ .name = "fromTOML",
+ .args = {"e"},
+ .doc = R"(
+ Convert a TOML string to a Nix value. For example,
+
+ ```nix
+ builtins.fromTOML ''
+ x=1
+ s="a"
+ [table]
+ y=2
+ ''
+ ```
+
+ returns the value `{ s = "a"; table = { y = 2; }; x = 1; }`.
+ )",
+ .fun = prim_fromTOML
+});
}
diff --git a/src/libstore/build/local-derivation-goal.cc b/src/libstore/build/local-derivation-goal.cc
index b0289ac75..7f87cdf55 100644
--- a/src/libstore/build/local-derivation-goal.cc
+++ b/src/libstore/build/local-derivation-goal.cc
@@ -4,7 +4,7 @@
#include "worker.hh"
#include "builtins.hh"
#include "builtins/buildenv.hh"
-#include "references.hh"
+#include "path-references.hh"
#include "finally.hh"
#include "util.hh"
#include "archive.hh"
@@ -1457,7 +1457,7 @@ void LocalDerivationGoal::startDaemon()
(struct sockaddr *) &remoteAddr, &remoteAddrLen);
if (!remote) {
if (errno == EINTR || errno == EAGAIN) continue;
- if (errno == EINVAL) break;
+ if (errno == EINVAL || errno == ECONNABORTED) break;
throw SysError("accepting connection");
}
@@ -1487,8 +1487,22 @@ void LocalDerivationGoal::startDaemon()
void LocalDerivationGoal::stopDaemon()
{
- if (daemonSocket && shutdown(daemonSocket.get(), SHUT_RDWR) == -1)
- throw SysError("shutting down daemon socket");
+ if (daemonSocket && shutdown(daemonSocket.get(), SHUT_RDWR) == -1) {
+ // According to the POSIX standard, the 'shutdown' function should
+ // return an ENOTCONN error when attempting to shut down a socket that
+ // hasn't been connected yet. This situation occurs when the 'accept'
+ // function is called on a socket without any accepted connections,
+ // leaving the socket unconnected. While Linux doesn't seem to produce
+ // an error for sockets that have only been accepted, more
+ // POSIX-compliant operating systems like OpenBSD, macOS, and others do
+ // return the ENOTCONN error. Therefore, we handle this error here to
+ // avoid raising an exception for compliant behaviour.
+ if (errno == ENOTCONN) {
+ daemonSocket.close();
+ } else {
+ throw SysError("shutting down daemon socket");
+ }
+ }
if (daemonThread.joinable())
daemonThread.join();
@@ -1499,7 +1513,8 @@ void LocalDerivationGoal::stopDaemon()
thread.join();
daemonWorkerThreads.clear();
- daemonSocket = -1;
+ // release the socket.
+ daemonSocket.close();
}
@@ -2379,18 +2394,21 @@ SingleDrvOutputs LocalDerivationGoal::registerOutputs()
continue;
auto references = *referencesOpt;
- auto rewriteOutput = [&]() {
+ auto rewriteOutput = [&](const StringMap & rewrites) {
/* Apply hash rewriting if necessary. */
- if (!outputRewrites.empty()) {
+ if (!rewrites.empty()) {
debug("rewriting hashes in '%1%'; cross fingers", actualPath);
- /* FIXME: this is in-memory. */
- StringSink sink;
- dumpPath(actualPath, sink);
+ /* FIXME: Is this actually streaming? */
+ auto source = sinkToSource([&](Sink & nextSink) {
+ RewritingSink rsink(rewrites, nextSink);
+ dumpPath(actualPath, rsink);
+ rsink.flush();
+ });
+ Path tmpPath = actualPath + ".tmp";
+ restorePath(tmpPath, *source);
deletePath(actualPath);
- sink.s = rewriteStrings(sink.s, outputRewrites);
- StringSource source(sink.s);
- restorePath(actualPath, source);
+ movePath(tmpPath, actualPath);
/* FIXME: set proper permissions in restorePath() so
we don't have to do another traversal. */
@@ -2439,7 +2457,7 @@ SingleDrvOutputs LocalDerivationGoal::registerOutputs()
"since recursive hashing is not enabled (one of outputHashMode={flat,text} is true)",
actualPath);
}
- rewriteOutput();
+ rewriteOutput(outputRewrites);
/* FIXME optimize and deduplicate with addToStore */
std::string oldHashPart { scratchPath->hashPart() };
HashModuloSink caSink { outputHash.hashType, oldHashPart };
@@ -2477,16 +2495,14 @@ SingleDrvOutputs LocalDerivationGoal::registerOutputs()
Hash::dummy,
};
if (*scratchPath != newInfo0.path) {
- // Also rewrite the output path
- auto source = sinkToSource([&](Sink & nextSink) {
- RewritingSink rsink2(oldHashPart, std::string(newInfo0.path.hashPart()), nextSink);
- dumpPath(actualPath, rsink2);
- rsink2.flush();
- });
- Path tmpPath = actualPath + ".tmp";
- restorePath(tmpPath, *source);
- deletePath(actualPath);
- movePath(tmpPath, actualPath);
+ // If the path has some self-references, we need to rewrite
+ // them.
+ // (note that this doesn't invalidate the ca hash we calculated
+ // above because it's computed *modulo the self-references*, so
+ // it already takes this rewrite into account).
+ rewriteOutput(
+ StringMap{{oldHashPart,
+ std::string(newInfo0.path.hashPart())}});
}
HashResult narHashAndSize = hashPath(htSHA256, actualPath);
@@ -2508,7 +2524,7 @@ SingleDrvOutputs LocalDerivationGoal::registerOutputs()
outputRewrites.insert_or_assign(
std::string { scratchPath->hashPart() },
std::string { requiredFinalPath.hashPart() });
- rewriteOutput();
+ rewriteOutput(outputRewrites);
auto narHashAndSize = hashPath(htSHA256, actualPath);
ValidPathInfo newInfo0 { requiredFinalPath, narHashAndSize.first };
newInfo0.narSize = narHashAndSize.second;
diff --git a/src/libstore/build/personality.cc b/src/libstore/build/personality.cc
index 4ad477869..1a6201758 100644
--- a/src/libstore/build/personality.cc
+++ b/src/libstore/build/personality.cc
@@ -21,7 +21,8 @@ void setPersonality(std::string_view system)
&& (std::string_view(SYSTEM) == "x86_64-linux"
|| (!strcmp(utsbuf.sysname, "Linux") && !strcmp(utsbuf.machine, "x86_64"))))
|| system == "armv7l-linux"
- || system == "armv6l-linux")
+ || system == "armv6l-linux"
+ || system == "armv5tel-linux")
{
if (personality(PER_LINUX32) == -1)
throw SysError("cannot set 32-bit personality");
diff --git a/src/libstore/globals.hh b/src/libstore/globals.hh
index 07f524858..820898350 100644
--- a/src/libstore/globals.hh
+++ b/src/libstore/globals.hh
@@ -691,20 +691,19 @@ public:
Strings{"https://cache.nixos.org/"},
"substituters",
R"(
- A list of [URLs of Nix stores](@docroot@/command-ref/new-cli/nix3-help-stores.md#store-url-format)
- to be used as substituters, separated by whitespace.
- Substituters are tried based on their Priority value, which each substituter can set
- independently. Lower value means higher priority.
- The default is `https://cache.nixos.org`, with a Priority of 40.
+ A list of [URLs of Nix stores](@docroot@/command-ref/new-cli/nix3-help-stores.md#store-url-format) to be used as substituters, separated by whitespace.
+ A substituter is an additional [store]{@docroot@/glossary.md##gloss-store} from which Nix can obtain [store objects](@docroot@/glossary.md#gloss-store-object) instead of building them.
- At least one of the following conditions must be met for Nix to use
- a substituter:
+ Substituters are tried based on their priority value, which each substituter can set independently.
+ Lower value means higher priority.
+ The default is `https://cache.nixos.org`, which has a priority of 40.
+
+ At least one of the following conditions must be met for Nix to use a substituter:
- the substituter is in the [`trusted-substituters`](#conf-trusted-substituters) list
- the user calling Nix is in the [`trusted-users`](#conf-trusted-users) list
- In addition, each store path should be trusted as described
- in [`trusted-public-keys`](#conf-trusted-public-keys)
+ In addition, each store path should be trusted as described in [`trusted-public-keys`](#conf-trusted-public-keys)
)",
{"binary-caches"}};
@@ -896,12 +895,11 @@ public:
this, {}, "hashed-mirrors",
R"(
A list of web servers used by `builtins.fetchurl` to obtain files by
- hash. The default is `http://tarballs.nixos.org/`. Given a hash type
- *ht* and a base-16 hash *h*, Nix will try to download the file from
- *hashed-mirror*/*ht*/*h*. This allows files to be downloaded even if
- they have disappeared from their original URI. For example, given
- the default mirror `http://tarballs.nixos.org/`, when building the
- derivation
+ hash. Given a hash type *ht* and a base-16 hash *h*, Nix will try to
+ download the file from *hashed-mirror*/*ht*/*h*. This allows files to
+ be downloaded even if they have disappeared from their original URI.
+ For example, given an example mirror `http://tarballs.nixos.org/`,
+ when building the derivation
```nix
builtins.fetchurl {
diff --git a/src/libstore/path-references.cc b/src/libstore/path-references.cc
new file mode 100644
index 000000000..33cf66ce3
--- /dev/null
+++ b/src/libstore/path-references.cc
@@ -0,0 +1,73 @@
+#include "path-references.hh"
+#include "hash.hh"
+#include "util.hh"
+#include "archive.hh"
+
+#include