Compare commits

..
45 Commits
Author SHA1 Message Date
Raito Bezarius 8c5e1c8f63 release: 2.92.3 "Bombe glacée"
Release produced with releng/create_release.xsh

Change-Id: If52727313f7f9a0dd4ce6c95dccfeb33585eac0a
2025-06-30 00:59:27 +02:00
Raito Bezarius fe090a7b9b release: release notes for 2.92.3
Release created with releng/create_release.xsh

Change-Id: I7683080179723ab8e36206c511c1e8819250fe5f
2025-06-30 00:59:26 +02:00
Raito Bezarius 4a3983906f version: 2.92.2 -> 2.92.3
Resolves critical correctness bugs as reported in fj#883.
Following the CVE fixes.

Change-Id: Ife85fb4ca71ba920aae4b98a6dc141857e10d759
Signed-off-by: Raito Bezarius <raito@lix.systems>
2025-06-29 20:36:57 +00:00
EmilyandRaito Bezarius bac0d67928 libstore: fix Unix sockets in the build directory on sandboxed macOS
We’re already allowing `/tmp` anyway, so this should be harmless,
and it fixes a regression in the default configuration caused by
moving the build directories out of `temp-dir`. (For instance, that
broke the Lix `guessOrInventPath.sockets` test.)

Note that removing `/tmp` breaks quite a few builds, so although it may
be a good idea in general it would require work on the Nixpkgs side.

Fixes: 749afbbe99
Change-Id: I6a6a69645f429bc50d4cb24283feda3d3091f534
(cherry picked from commit d1db3e5fa3)
2025-06-29 20:36:57 +00:00
Raito Bezarius 22a1d233bb libstore: fallback on creating a safe space in the default tempdir
If `settings.buildDir` cannot be written to, because we are in a chroot
store, unprivileged or anything.

We can and should always gracefully fallback to a *secure* location
inside of /tmp, i.e. `/tmp/<a directory under 0700>/<our temporary
directory for build under 0700>/...`.

This does not reintroduce CVE-2025-52991 because we are creating a
directory in-between compared to creating only ONE level of directory.

Under macOS, the first level of directory has actually mode 0755 instead
of 0700 as macOS often do not possess the right primitives to chroot
inside of these directories, leading to
https://github.com/NixOS/nix/pull/11031.

Thanks to Emily for the heads-up on this type of matter.

Fixes #876.

Change-Id: Ie521202923f763225e1901ab1b9b6c6132aaf548
Signed-off-by: Raito Bezarius <raito@lix.systems>
2025-06-29 20:36:57 +00:00
eldritch horrors 4901a6cda9 Revert "libstore/build: automatic clean up of unsuccessfully built scratch outputs"
This reverts commit 2f32368136 as this is the root cause for the critical correctness bug.

Change-Id: I573cdfbaed8316ad93197aab621ad7e3ff966c1d
2025-06-29 20:36:57 +00:00
eldritch horrors 8cbdc36a60 Revert "libstore: fix scratch output cleanup"
This reverts commit 1d36dd7596 as this is an insufficient fix for the critical correctness bug.

Change-Id: Id48fec6e2146ccc98edb39629d9944404a17ba8c
2025-06-29 20:36:57 +00:00
eldritch horrors 56b0b39465 Revert "libstore: don't delete already valid outputs after build"
This reverts commit 99456a0c7e as this is an insufficient fix for the critical correctness bug.

Change-Id: I950b4d007f4919877df6dd9eef5d6c1e95ca270e
2025-06-29 20:15:29 +00:00
Raito Bezarius 438e1839f1 releng: go back to a non-official release
This is necessary to unblock releng.

This is suboptimal releng because we usually merge the branches back and
the releng script did not.

Change-Id: I12c9f6713adf344c1597e2d4adafaf2d3c62f537
Signed-off-by: Raito Bezarius <raito@lix.systems>
2025-06-27 23:17:38 +02:00
eldritch horrors 99456a0c7e libstore: don't delete already valid outputs after build
eagerly consider outputs as not needing deletion during output
registration rather than only doing so after registration. not
waiting for registration to succeed may keep store paths alive
in the file system if registration fails for some reason; that
seem preferrable to the possibility of having another instance
of this bug. since we only leave *good* outputs around there's
not much to worry about except maybe bit of wasted disk space.

fixes #883

Change-Id: I8c22c92e39b9e203f1061278f86cde19dc4474a4
2025-06-27 16:35:53 +02:00
eldritch horrors 1d36dd7596 libstore: fix scratch output cleanup
the daemon must use real store paths, not virtual store paths. using
virtual paths may inadvertently delete paths in the system nix store
when a build was run on a redirected store as root, which isn't good

Change-Id: Id048b236bda0e0ab1f3be6ccba0ddc1de2a3e941
2025-06-27 16:35:04 +02:00
Raito Bezarius 21317ce965 release: 2.92.2 "Bombe glacée"
Release produced with releng/create_release.xsh

Change-Id: I43476413ebe28aa60618f24205fdda9a3a92902c
2025-06-24 10:50:36 +00:00
Raito Bezarius b43a289c02 release: release notes for 2.92.2
Release created with releng/create_release.xsh

Change-Id: I9bcc8bec834ebf9b2d0ec9df00a585b610152ba8
2025-06-24 10:50:36 +00:00
Raito Bezarius 2138b0f7e9 version: 2.92.1 -> 2.92.2
Fixes CVE-2025-46415, CVE-2025-46416, CVE-2025-52991, CVE-2025-52992,
and CVE-2025-52993.

Change-Id: Ia73b43abd1cf2d0cfa185938c59d3a836a9eb160
Signed-off-by: Raito Bezarius <raito@lix.systems>
2025-06-24 10:50:36 +00:00
Raito Bezarius 2f32368136 libstore/build: automatic clean up of unsuccessfully built scratch outputs
When a build fails, its scratch output paths are not cleaned up.

Until recently, this was deemed not a problem but as part of the effort
to harden the Nix builds and protect these paths against being part of a
staged attack (race conditions, etc.), we automatically cleanup after
failed builds.

Fixes CVE-2025-52992.

Change-Id: I58481b1cc83826298b9d80d37fecf81f117ccb09
Signed-off-by: Raito Bezarius <raito@lix.systems>
2025-06-24 10:50:36 +00:00
eldritch horrorsandRaito Bezarius a713c064af libstore: don't default build-dir to temp-dir
if a build directory is accessible to other users it is possible to
smuggle data in and out of build directories. usually this ins only
a build purity problem, but in combination with other issues it can
be used to break out of a build sandbox. to prevent this we default
to using a subdirectory of nixStateDir (which is more restrictive).

Fixes CVE-2025-52991.

Change-Id: Iacfc9b50534de158618c815f9fb99d7dae1be4d0
2025-06-24 10:50:36 +00:00
cba05329ee libstore: use pasta for FODs if available
This allows using a userspace program, pasta, to handle comms between
the build sandbox, and the outside world; allowing for full isolation
including the network namespace, closing the "fixed-output derivation
talks to the host over an abstract domain socket" hole for good.

Fixes CVE-2025-46416.

Co-Authored-By: Puck Meerburg <puck@puckipedia.com>
Change-Id: Ifd499b7dbb3784600a6e842fede65fc031ff9f15
2025-06-24 10:50:36 +00:00
eldritch horrors f5f2e1537d libutil: add capability support to runProgram2
launching pasta to not run as root will ambient require capabilities.

Change-Id: I1dd2506a1fa3944a9d9062123ef8a74903c597ea
2025-06-24 10:50:36 +00:00
eldritch horrors 0ea8649445 libutil: add generic redirections runProgram2
explicit stderr redirection makes mergeStderrToStdout unnecessary also.

Change-Id: I63de929e6dc53f6c5ceb2d43c2ce288bfc04d872
2025-06-24 10:50:36 +00:00
eldritch horrors d0678a57f9 libutil: make RunningProgram more useful
make it moveable, make it killable, and add a stdout fd accessor.

Change-Id: I2387cbe8ac67b899a322cd6c7d306ef9ea7abcd0
2025-06-24 10:50:36 +00:00
Raito Bezarius 1d4ddb7e3b libutil: ensure that _deletePath does NOT use absolute paths with dirfds
When calling `_deletePath` with a parent file descriptor, `openat` is
made effective by using relative paths to the directory file descriptor.

To avoid the problem, the signature is changed to resist misuse with an
assert in the prologue of the function.

Fixes CVE-2025-46415.

Change-Id: I6b3fc766bad2afe54dc27d47d1df3873e188de96
Signed-off-by: Raito Bezarius <raito@lix.systems>
2025-06-24 10:50:36 +00:00
Raito Bezarius c7867e89f9 libstore: ensure that passAsFile is created in the original temp dir
This ensures that `passAsFile` data is created inside the expected
temporary build directory by `openat()` from the parent directory file
descriptor.

Fixes CVE-2025-52993.

Change-Id: Ie5273446c4a19403088d0389ae8e3f473af8879a
Signed-off-by: Raito Bezarius <raito@lix.systems>
2025-06-24 10:45:29 +00:00
Raito Bezarius deb5150c82 libutil: writeFile variant for file descriptors
`writeFile` lose its `sync` boolean flag to make things simpler.

A new `writeFileAndSync` function is created and all call sites are
converted to it.

Change-Id: Ib871a5283a9c047db1e4fe48a241506e4aab9192
Signed-off-by: Raito Bezarius <raito@lix.systems>
2025-06-23 16:50:44 +02:00
Raito Bezarius 92eb418a59 libstore: chown to builder variant for file descriptors
We use it immediately for the build temporary directory.

Change-Id: I180193c63a2b98721f5fb8e542c4e39c099bb947
Signed-off-by: Raito Bezarius <raito@lix.systems>
2025-06-23 16:50:44 +02:00
Raito Bezarius 7fceee3ce3 libstore: open build directory as a dirfd as well
We now keep around a proper AutoCloseFD around the temporary directory
which we plan to use for openat operations and avoiding the build
directory being swapped out while we are doing something else.

Change-Id: I18d387b0f123ebf2d20c6405cd47ebadc5505f2a
Signed-off-by: Raito Bezarius <raito@lix.systems>
2025-06-23 16:50:44 +02:00
Raito Bezarius 31f976dc19 libutil: guess or invent a path from file descriptors
This is useful for certain error recovery paths (no pun intended) that
does not thread through the original path name.

Change-Id: I2d800740cb4f9912e64c923120d3f977c58ccb7e
Signed-off-by: Raito Bezarius <raito@lix.systems>
2025-06-23 16:50:44 +02:00
jadeandLix Systems Gerrit d8db15010d Merge changes I577bdc15,I1c5f7a5c,I75f56c80 into release-2.92
* changes:
  version: 2.92.1
  release-notes: note the flake.lock fix
  libexpr/flake: user friendly error if parsing flake.lock fails
2025-03-17 22:26:40 +00:00
Jade Lovelace 253be7d2ba version: 2.92.1
Change-Id: I577bdc1549e80c0b7621624e2d390153581b3bf2
2025-03-17 12:11:24 -07:00
Jade Lovelace a34e583305 releng: workaround awscli breaking garage by changing checksum algo
CHERRY-PICK: required to release

We use https://garagehq.deuxfleurs.fr for https://docs.lix.systems,
https://releases.lix.systems, https://cache.lix.systems. It's generally
great, but AWS doesn't, erm, care, about other implementations and broke
their client library.

We already ran into
https://git.deuxfleurs.fr/Deuxfleurs/garage/issues/824, which was
mitigated by a garage upgrade to create a new error. These bugs were
what got us:
- https://github.com/boto/boto3/issues/4392
- https://github.com/aws/aws-cli/issues/9214

Error:
upload failed: release/manual/.nojekyll to s3://docs/manual/lix/nightly/.nojekyll An error occurred (InvalidRequest) when calling the PutObject operation: Bad request: invalid checksum algorithm

The missing checksum algorithm is CRC32NVME, with a bug filed here:
https://git.deuxfleurs.fr/Deuxfleurs/garage/issues/963

Change-Id: Ib78a89034bf0f2a6773fc505a347b2aadb775e93
(cherry picked from commit 0f4c5b3b8a)
2025-03-17 19:10:24 +00:00
Rebecca Turnerandjade a8fb008106 releng: use aws s3 sync --delete when uploading manual
CHERRY-PICK: prereq to fixing the aws flags for garage shenanigans

For the user manual, we don't delete things that are missing when doing
aws s3 sync. This doesn't seem wise. If you delete a page in the manual,
it will stay public and visible.

This adds `--delete` to the `aws s3 sync` commands to fix this issue.

Closes #396

Change-Id: I6d7fb97bcdab96c0115d6c66fea0310125207df4
(cherry picked from commit 16df34b295)
2025-03-17 19:09:10 +00:00
Jade Lovelace 32cbb66a69 release-notes: note the flake.lock fix
Change-Id: I1c5f7a5cba12da1bfb2896ffc04ec89862cb346d
2025-03-17 12:06:39 -07:00
giliceandjade 9631e9a30f libexpr/flake: user friendly error if parsing flake.lock fails
CHERRY-PICK: repeated bug reports

Fix: https://git.lix.systems/lix-project/lix/issues/559
Change-Id: I75f56c801d7f16a2e1ef8e702f16f1ef91f7b01f
(cherry picked from commit 68373f8664)
2025-03-17 19:01:23 +00:00
jadeandLix Systems Gerrit 98772c4a3b Merge "Revert "libstore: don't use curl decompression support"" into release-2.92 2025-03-17 18:17:41 +00:00
jadeandLix Systems Gerrit a7fd5c3867 Merge "libexpr: fix checkSourcePath purity regression" into release-2.92 2025-03-17 17:58:29 +00:00
jadeandLix Systems Gerrit 6f3a7bbeb0 Revert "libstore: don't use curl decompression support"
This reverts commit 0d1f794178.

Reason for revert: pennae says it makes the bug more visible, for some unknown reason. See https://git.lix.systems/lix-project/lix/issues/662

Change-Id: I91853cb6645f188a260c4c71cfff4de7bea99feb
2025-03-17 17:56:20 +00:00
eldritch horrorsandjade 0d1f794178 libstore: don't use curl decompression support
it's broken with http2 and transfer flow control. cf fj#662

BACKPORT: due to regression
Change-Id: Iaf6312bfcefa18d168faef47f57481199dd30b8d
2025-03-09 08:39:10 +00:00
Alois Wohlschlager ef5689dc1b libexpr: fix checkSourcePath purity regression
Starting with commit 0dbfa7b26e access would also
be allowed to ancestors of allowed paths. This is (ironically) a significant
purity regression, since several users of the purity checks will themselves
assume that arbitrary descent is allowed. For example, `builtins.readDir` and
`builtins.path` could now refer to the filesystem root, breaking purity
entirely in the latter case by allowing to read arbitrary files. Restore the
previous behaviour of only allowing access to explicitly allowed paths.

Change-Id: Ie64180733ab735da9873255e1ccbf95ba7c9161c
(cherry picked from commit 9d99a7c2cf)
2025-03-04 18:15:15 +01:00
Lunaphiedandjade db55ca9a2e hotfix for releng scripts, fixes #440
Change-Id: I33b29fbf3920a2d4fb53b58db477dff5bf7a1af1
2025-02-21 01:17:44 +00:00
eldritch horrorsandJade Lovelace 4cd618272a libstore: never return from lockFile without a lock
signals could cause lockFile to return without having locked the file.
the garbage collector didn't check for this, and then hilarity ensued.

Change-Id: If86d33595e8bf5510d2b032139342261dc6e07c9
(cherry picked from commit d186064c3d)
2025-02-09 12:54:48 -08:00
eldritch horrorsandJade Lovelace 73f0213500 libstore: split tryLockFile lockFile
lockFile is currently interruptible by signals like SIGCHLD. which
happen a lot in daemons. now imagine that daemon enabled automatic
garbage collection. observe that the local store does not actually
check whether its lock operations have succeeded ... get the idea?

Change-Id: Ibfd7ee786c4fee3add72d4456a7e95e73e09c73e
(cherry picked from commit 79f9c39e36)
2025-02-09 12:54:48 -08:00
eldritch horrorsandJade Lovelace 9c1db3cd8b libstore: extract unlockFile from lockFile
propagating the "unlock" lock type through this high-level api is
nonsense. it doesn't make sense to treat locking and unlocking as
similar operations; unlocking *must* not not interruptible by our
checkInterrupt machinery or it will just leave locks lying around
for a potentially very long time. unlock operations should not be
taking long enough to *want* them interrupted anyway. even on nfs
this makes very little sense because nfs waits *uninterruptibly*.

Change-Id: I10d605c8fe6c651bee64466eee1f8e20251d39f4
(cherry picked from commit aa87c8aa93)
2025-02-09 12:54:48 -08:00
Dusk Banksandeldritch horrors 5fa27057b2 libexpr: fix --debugger --ignore-try
a65e9e5828 did not inform `tryEval` that
(as far as it's concerned) `state.debug` moved to `state.errors.debug`
and changed types. this resulted in the REPL erroneously coming up, that
REPL having a non-debug state, and segfaulting after that REPL exited.

it's probably good that `state.debug` isn't mutated by `--ignore-try`
anymore.

Change-Id: I1918e93edacd626452aa423fc2eb825080738835
Fixes: a65e9e5828 ("libexpr: extract eval error creation into new type")
Signed-off-by: Dusk Banks <me@bb010g.com>
(cherry picked from commit 6a583136b7)
2025-01-31 12:36:14 +00:00
Dusk Banksandeldritch horrors d2b1af70ee doc: add bb010g to change-authors
Change-Id: Iddd3c21b2c42669cec394ac6b80f4e766e4cb81c
Signed-off-by: Dusk Banks <me@bb010g.com>
(cherry picked from commit 050cf17307)
2025-01-31 03:21:44 +00:00
Raito Bezariusandjade 50def3fa73 fix(gc): log sudden "path in use" exceptions and recover during GC gracefully
Original-Author: picnoir <picnoir@alternativebit.fr>

Inspired from
https://github.com/NixOS/nix/pull/11922/commits/ced8d311a593fcf9c3823e4e118474ac132d8e60
and adapted for Lix needs.

TL;DR: The topological sort should ensure that it is possible to delete
the path iterated upon. Nonetheless, in some cases,
`invalidatePathChecked` can still throw `PathInUse`, the exception
bubbles up and cancel the garbage collection procedure, leaving the rest
of the paths untouched. This change ensure that the error is logged for
further investigation but doesn't prevent the GC to continue when it
can.

After code review, we decided to make it a `printInfo` to inform the
user about sudden "in use" dependencies during garbage collection and
let them re-run garbage collection if they care about this.

References: https://github.com/NixOS/nix/issues/11923
References: https://git.lix.systems/lix-project/lix/issues/621
Change-Id: I5606c9afd16b5faa747b713fde2dc24016990ba3
Signed-off-by: Raito Bezarius <raito@lix.systems>
(cherry picked from commit 6a41dae49a)
2025-01-23 06:04:00 +00:00
eldritch horrorsandjade 8e2ab5532c FileTransfer: fix race condition on awaitData
There's a race condition where awaitData could early-return for data
coming from a 404 response or similar and thus not rethrow the exception
that is forthcoming, and a related race during transfer setup (which
could retry a transfer *twice* per retry round).

This would then cause substitution failures like below since the exception
isn't caught in HttpBinaryCacheStore::getFile as intended, but instead
by an exception handler downstream of `drain()` which would error out
the entire operation.

Symptom:

 » nix-build ./docs-service.nix -o "docs-service-result"
error: unable to download 'https://cache.nixos.org/7mr3fy8w66gi5inmf0jkkkl90lxy4jyg.narinfo': HTTP e
rror 404 ()

       response body:

This is kind of a hack in how it is implemented: it assumes that you
can't intentionally be receiving a large unsuccessful response since in
such a case, `awaitData` will wait for finish() to be called to throw an
exception and will never escape until the download finishes, while
continuing to buffer the entire response into memory, which could be bad
if an error response had a large payload.

That said, nobody is sending Lix 1GiB of 404, so meh I guess, and this
is how it is seemingly intended to work. That was a design flaw of the
thing before any of the Lix team got our paws on it.

I tested this by adding _exit(0) inside the expected exception catch and
then running the offending command repeatedly to see if the symptom ever
appeared again, and it did not.

Needs cherry-pick to 2.92 and a 2.92.1 release once reviewed.

Fixes: https://git.lix.systems/lix-project/lix/issues/635
Co-Authored-By: lix@jade.fyi
Change-Id: If54f6eeaad60b5ca9d5b77d4d9232da1d295e7d1
(cherry picked from commit de58cd6e80)
2025-01-22 21:06:58 +00:00
664 changed files with 9348 additions and 24059 deletions
-2
View File
@@ -18,8 +18,6 @@ Checks:
- -bugprone-branch-clone
# extremely noisy before clang 19: https://github.com/llvm/llvm-project/issues/93959
- -bugprone-multi-level-implicit-pointer-conversion
# we don't compile out our asserts
- -bugprone-assert-side-effect
# all thrown exceptions must derive from std::exception
- hicpp-exception-baseclass
# capturing async lambdas are dangerous
+1 -4
View File
@@ -17,13 +17,10 @@ For systems that **already have a Nix implementation installed**, such as NixOS
## Building And Developing
See our [Hacking guide](https://git.lix.systems/lix-project/lix/src/branch/main/doc/manual/src/contributing/hacking.md) in our manual for instruction on how to set up a development environment and build Lix from source.
See our [Hacking guide](https://git.lix.systems/lix-project/lix/src/branch/main/doc/manual/src/contributing/hacking.md) in our manual for instruction on how to to set up a development environment and build Lix from source.
## Additional Resources
- The Lix reference manual:
- [Stable](https://docs.lix.systems/manual/lix/stable/)
- [Nightly](https://docs.lix.systems/manual/lix/nightly/) (NOTE: [not automatically updated, yet](https://git.lix.systems/lix-project/lix/issues/742))
- [Our wiki](https://wiki.lix.systems)
- [Matrix - #space:lix.systems](https://matrix.to/#/#space:lix.systems)
-1
View File
@@ -1,4 +1,3 @@
bench-*.json
bench-*.md
perf-*.json
nixpkgs
-116
View File
@@ -1,116 +0,0 @@
#!/usr/bin/env nix-shell
#!nix-shell -i python3 -p python3 -p hyperfine -p "if stdenv.isLinux then linuxPackages.perf else null"
import argparse
import subprocess
import os
import json
import tempfile
import platform
flake_args = ["--extra-experimental-features","'nix-command flakes'"]
# hyperfine has its own variable substitution, so we use that and pass build="{BUILD}" here.
# perf doesn't have variable substitution, so we call these with build being the actual build directory.
cases = {
"search": lambda build: [f"{build}/bin/nix", *flake_args, "search", "--no-eval-cache", "github:nixos/nixpkgs/e1fa12d4f6c6fe19ccb59cac54b5b3f25e160870", "hello"],
"rebuild": lambda build: [f"{build}/bin/nix", *flake_args, "eval", "--raw", "--impure", "--expr", "'with import <nixpkgs/nixos> {}; system'"],
"rebuild_lh": lambda build: ["GC_INITIAL_HEAP_SIZE=10g", f"{build}/bin/nix", *flake_args, "eval", "--raw", "--impure", "--expr", "'with import <nixpkgs/nixos> {}; system'"],
"parse": lambda build: [f"{build}/bin/nix", *flake_args, "eval", "-f", "bench/nixpkgs/pkgs/development/haskell-modules/hackage-packages.nix"],
}
arg_parser = argparse.ArgumentParser()
# FIXME(jade, gilice): it is a reasonable use case to want to run a benchmark run
# on just one build. However, since we are using hyperfine in comparison
# mode, we would have to combine the JSON ourselves to support that, which
# would probably be better done by writing a benchmarking script in
# not-bash.
arg_parser.add_argument('builds', nargs='+', help="At least two build directories to compare, containing bin/nix")
arg_parser.add_argument('--cases', type=str, help="A comma-separated list of cases you want to run. Defaults to running all")
available_modes = [ "walltime" ] + [ "icount" ] if platform.system() == 'Linux' else [] # perf doesn't run on Darwin
arg_parser.add_argument('--mode', choices=available_modes, default="walltime")
args = arg_parser.parse_args()
if len(args.builds) < 2:
raise ValueError("need at least two build directories to compare")
benchmarks: list[str] = []
if args.cases is None:
benchmarks = list(cases.keys())
else:
for case in args.cases.split(","):
if case not in cases: raise ValueError(f"no such case: {case}")
benchmarks.append(case)
def bench_walltime(env):
hyperfine_args = ["--parameter-list", "BUILD", ','.join(args.builds), "--warmup", "2", "--runs", "10"]
for case in benchmarks:
case_command = cases[case]("{BUILD}") # see the comment on cases
subprocess.run([
"taskset", "-c", "2,3",
"chrt", "-f","50",
"hyperfine", *hyperfine_args, "--export-json", f"bench/bench-{case}.json", "--export-markdown", f"bench/bench-{case}.md", "--", " ".join(case_command)
], env=env, check=True)
print("Benchmarks summary\n---\n")
for case in benchmarks:
fd = open(f"bench/bench-{case}.json")
result_json = json.load(fd)
fd.close()
for result in result_json["results"]:
print(result["command"])
print("-" * min(80,len(result["command"])))
attr_rounded = lambda attr: f"{result[attr]:.3f}"
print(" mean: ", attr_rounded("mean"), "±", attr_rounded("stddev"))
print(" user:", attr_rounded("user"), "| system", attr_rounded("system"))
print(" median: ", attr_rounded("median"))
print(" range: ", attr_rounded("min") + "s.." + attr_rounded("max")+"s")
print(" relative:", f"{result["mean"]/result_json["results"][0]["mean"]:.3f}")
print("\n")
def bench_icount(env):
perf_results_for: dict[str, list[tuple[str, float]]] = {}
for case in benchmarks:
for build in args.builds:
case_command = cases[case](build)
# the perf stat -j output (incorrectly) localizes numbers, which will trip up the json parser.
env["LC_ALL"]="C"
commandline = [
"perf", "stat", "-o", f"bench/perf-{case}.json", "-j", "sh", "-c", " ".join(case_command)
]
subprocess.run(commandline, env=env, check=True, stdout=subprocess.DEVNULL) # warmup run
subprocess.run(commandline, env=env, check=True, stdout=subprocess.DEVNULL)
perf_fd = open(f"bench/perf-{case}.json")
perf_data = [json.loads(x) for x in perf_fd.readlines()]
perf_fd.close()
instr = next(x for x in perf_data if x["event"] in ["instructions", "instructions:u"]) # an implementation of a find_first iterator
if case not in perf_results_for: perf_results_for[case] = []
perf_results_for[case].append((" ".join(case_command), float(instr["counter-value"])))
print("Benchmarks summary\n---\n")
for (case, entries) in perf_results_for.items():
for entry in entries:
cmd,instr = entry
print(cmd)
print("-" * min(80,len(cmd)))
print(" instructions: ", int(instr))
print(" relative instructions:", int(instr)/perf_results_for[case][0][1])
print("\n")
with tempfile.TemporaryDirectory() as tmp_dir:
subprocess.run([
"nix", "build",
"--extra-experimental-features", "nix-command flakes",
"--impure", "--expr",'(builtins.getFlake "git+file:.").inputs.nixpkgs.outPath',
"-o","bench/nixpkgs"
], check=True)
subenv = os.environ.copy()
subenv["NIX_CONF_DIR"] = "/var/empty"
subenv["NIX_REMOTE"] = tmp_dir
subenv["NIX_PATH"] = "nixpkgs=bench/nixpkgs:nixos-config=bench/configuration.nix"
if args.mode == "walltime":
bench_walltime(subenv)
else:
bench_icount(subenv)
Executable
+68
View File
@@ -0,0 +1,68 @@
#!/usr/bin/env nix-shell
#!nix-shell -i bash -p bash -p hyperfine
set -euo pipefail
shopt -s inherit_errexit
scriptdir=$(cd "$(dirname -- "$0")" ; pwd -P)
cd "$scriptdir/.."
if [[ $# -lt 2 ]]; then
# FIXME(jade): it is a reasonable use case to want to run a benchmark run
# on just one build. However, since we are using hyperfine in comparison
# mode, we would have to combine the JSON ourselves to support that, which
# would probably be better done by writing a benchmarking script in
# not-bash.
echo "Fewer than two result dirs given, nothing to compare!" >&2
echo "Pass some directories (with names indicating which alternative they are) with bin/nix in them" >&2
echo "Usage: ./bench/bench.sh result-1 result-2 [result-3...]" >&2
exit 1
fi
_exit=""
trap "$_exit" EXIT
flake_args=("--extra-experimental-features" "nix-command flakes")
# XXX: yes this is very silly. flakes~!!
nix build "${flake_args[@]}" --impure --expr '(builtins.getFlake "git+file:.").inputs.nixpkgs.outPath' -o bench/nixpkgs
# We must ignore the global config, or else NIX_PATH won't work reliably.
# See https://github.com/NixOS/nix/issues/9574
export NIX_CONF_DIR='/var/empty'
export NIX_REMOTE="$(mktemp -d)"
_exit='rm -rfv "$NIX_REMOTE"; $_exit'
export NIX_PATH="nixpkgs=bench/nixpkgs:nixos-config=bench/configuration.nix"
builds=("$@")
flake_args="${flake_args[*]@Q}"
hyperfineArgs=(
--parameter-list BUILD "$(IFS=,; echo "${builds[*]}")"
--warmup 2 --runs 10
)
declare -A cases
cases=(
[search]="{BUILD}/bin/nix $flake_args search --no-eval-cache github:nixos/nixpkgs/e1fa12d4f6c6fe19ccb59cac54b5b3f25e160870 hello"
[rebuild]="{BUILD}/bin/nix $flake_args eval --raw --impure --expr 'with import <nixpkgs/nixos> {}; system'"
[rebuild-lh]="GC_INITIAL_HEAP_SIZE=10g {BUILD}/bin/nix eval $flake_args --raw --impure --expr 'with import <nixpkgs/nixos> {}; system'"
[parse]="{BUILD}/bin/nix $flake_args eval -f bench/nixpkgs/pkgs/development/haskell-modules/hackage-packages.nix"
)
benches=(
rebuild
rebuild-lh
search
parse
)
for k in "${benches[@]}"; do
taskset -c 2,3 \
chrt -f 50 \
hyperfine "${hyperfineArgs[@]}" --export-json="bench/bench-${k}.json" --export-markdown="bench/bench-${k}.md" "${cases[$k]}"
done
echo "Benchmarks summary (from ./bench/summarize.jq bench/bench-*.json)"
bench/summarize.jq bench/*.json
+22
View File
@@ -0,0 +1,22 @@
#!/usr/bin/env -S jq -Mrf
def round3:
. * 1000 | round | . / 1000
;
def stats($first):
[
" mean: \(.mean | round3)s ± \(.stddev | round3)s",
" user: \(.user | round3)s | system: \(.system | round3)s",
" median: \(.median | round3)s",
" range: \(.min | round3)s ... \(.max | round3)s",
" relative: \(.mean / $first.mean | round3)"
]
| join("\n")
;
def fmt($first):
"\(.command)\n" + (. | stats($first))
;
[.results | .[0] as $first | .[] | fmt($first)] | join("\n\n") | (. + "\n\n---\n")
+3 -2
View File
@@ -7,8 +7,9 @@ create-missing = false
[output.html]
additional-css = ["custom.css"]
additional-js = ["redirects.js"]
# Jumps directly into a new Gerrit CL editing the file in question.
edit-url-template = "https://gerrit.lix.systems/admin/repos/edit/repo/lix/branch/main/file/doc/manual/{path}"
# Using our GitHub mirror enables easier typo fixes since there is no easy way
# to just submit a Gerrit CL by the web for trivial stuff.
edit-url-template = "https://github.com/lix-project/lix/tree/main/doc/manual/{path}"
git-repository-url = "https://git.lix.systems/lix-project/lix"
# Folding by default would prevent things like "Ctrl+F for nix-env" from working
# trivially, but the user should be able to fold if they want to.
-59
View File
@@ -12,10 +12,6 @@
forgejo: rbt
github: 9999years
9p4:
display_name: Ersei Saggi
github: 9p4
Artturin:
github: Artturin
@@ -40,10 +36,6 @@ alois31:
forgejo: alois31
github: alois31
andrewhamon:
display_name: Andrew Hamon
github: andrewhamon
artemist:
display_name: Artemis Tosini
forgejo: artemist
@@ -53,10 +45,6 @@ bb010g:
forgejo: bb010g
github: bb010g
blitz:
display_name: Julian Stecklina
github: blitz
cole-h:
display_name: Cole Helbling
github: cole-h
@@ -66,10 +54,6 @@ delan:
forgejo: delan
github: delan
detroyejr:
display_name: Jonathan De Troye
github: detroyejr
edolstra:
display_name: Eelco Dolstra
github: edolstra
@@ -109,11 +93,6 @@ jade:
just1602:
forgejo: just1602
kfears:
display_name: KFears
forgejo: kfearsoff
github: kfearsoff
kiara:
github: KiaraGrouwstra
@@ -129,10 +108,6 @@ lheckemann:
forgejo: lheckemann
github: lheckemann
lily:
forgejo: lilyinstarlight
github: lilyinstarlight
lilyball:
forgejo: lilyball
github: lilyball
@@ -155,32 +130,16 @@ midnightveil:
ncfavier:
github: ncfavier
p-e-meunier:
display_name: Pierre-Etienne Meunier
github: P-E-Meunier
pamplemousse:
display_name: Xavier Maso
github: pamplemousse
piegames:
display_name: piegames
forgejo: piegames
github: piegamesde
poliorcetics:
display_name: Poliorcetics
github: poliorcetics
puck:
display_name: puck
forgejo: puck
github: puckipedia
quantenzitrone:
display_name: Zitrone
forgejo: quantenzitrone
quantumjump:
display_name: Quantum Jump
github: QuantumBJump
@@ -197,18 +156,6 @@ roberth:
display_name: Robert Hensing
github: roberth
seppel3210:
github: Seppel3210
teofilc:
forgejo: teofilc
github: TeofilC
thubrecht:
display_name: Tom Hubrecht
forgejo: tom-hubrecht
github: Tom-Hubrecht
thufschmitt:
display_name: Théophane Hufschmitt
github: thufschmitt
@@ -230,12 +177,6 @@ winter:
forgejo: winter
github: winterqt
xanderio:
github: xanderio
yorickvp:
github: yorickvp
yshui:
github: yshui
-1
View File
@@ -1,7 +1,6 @@
nix_env_for_docs = {
'HOME': '/dummy',
'NIX_CONF_DIR': '/dummy',
'XDG_CONFIG_HOME': '/dummy',
'NIX_SSL_CERT_FILE': '/dummy/no-ca-bundle.crt',
'NIX_STATE_DIR': '/dummy',
'NIX_CONFIG': 'cores = 0',
-1
View File
@@ -198,7 +198,6 @@
- [Release Notes](release-notes/release-notes.md)
- [Upcoming release](release-notes/rl-next.md)
<!-- RELENG-AUTO-INSERTION-MARKER (see releng/release_notes.py) -->
- [Lix 2.93 (2025-05-09)](release-notes/rl-2.93.md)
- [Lix 2.92 (2025-01-18)](release-notes/rl-2.92.md)
- [Lix 2.91 (2024-08-12)](release-notes/rl-2.91.md)
- [Lix 2.90 (2024-07-10)](release-notes/rl-2.90.md)
@@ -75,9 +75,7 @@ by spaces. Only the first element is required. To leave a field at its
default, set it to `-`.
1. The URI of the remote store in the format
`ssh://[username@]hostname[?port=<port>]`, e.g. `ssh://nix@mac` or `ssh://mac`.
If the ssh server is not listening on port 22 (e.g. port 1337 in this case)
the URI would be `ssh://nix@mac?port=1337`
`ssh://[username@]hostname`, e.g. `ssh://nix@mac` or `ssh://mac`.
For backward compatibility, `ssh://` may be omitted. The hostname
may be an alias defined in your `~/.ssh/config`.
+3 -3
View File
@@ -22,9 +22,9 @@ The following [concept map] shows its main components (rectangles), the objects
| |
+----------|-------------------|--------------------------------+
| Nix impl.| V |
| (Lix) | +------------------------+ |
| | | command line interface |------. |
| | +------------------------+ | |
| (Lix) | +-------------------------+ |
| | | commmand line interface |------. |
| | +-------------------------+ | |
| | | | |
| evaluated by calls manages |
| | | | |
@@ -1,12 +0,0 @@
<!--
File-ish argument syntax summary.
This file gets included into pages like nix-build.md and nix-instantiate.md, and each individual page that includes
this also links to nix-build.md for the full explanation.
-->
- A normal filesystem path, like `/home/meow/nixfiles/default.nix`
- Or a directory, like `/home/meow/nixfiles`, equivalent to above
- A single lookup path, like `<nixpkgs>` or `<nixos>`
- A URL to a tarball, like `https://github.com/NixOS/nixpkgs/archive/refs/heads/release-23.11.tar.gz`
- A [flakeref](@docroot@/command-ref/new-cli/nix3-flake.md#flake-references), introduced by the prefix `flake:`, like `flake:git+https://git.lix.systems/lix-project/lix`
- A *nixpkgs* channel tarball name, introduced by the prefix `channel:`, like `channel:nixos-unstable`.
- This uses a hard-coded URL pattern and is *not* related to the subscribed channels managed by the [nix-channel](@docroot@/command-ref/nix-channel.md) command.
+7 -43
View File
@@ -4,7 +4,7 @@
# Synopsis
`nix-build` [*fileish…*]
`nix-build` [*paths…*]
[`--arg` *name* *value*]
[`--argstr` *name* *value*]
[{`--attr` | `-A`} *attrPath*]
@@ -20,55 +20,19 @@ For documentation on the latter, run `nix build --help` or see `man nix3-build`.
# Description
The `nix-build` command builds the derivations described by the Nix
expressions in each *fileish*. If the build succeeds, it places a symlink to
expressions in *paths*. If the build succeeds, it places a symlink to
the result in the current directory. The symlink is called `result`. If
there are multiple Nix expressions, or the Nix expressions evaluate to
multiple derivations, multiple sequentially numbered symlinks are
created (`result`, `result-2`, and so on).
If no *fileish* is specified, then `nix-build` will use `default.nix` in
If no *paths* are specified, then `nix-build` will use `default.nix` in
the current directory, if it exists.
## Fileish Syntax
A given *fileish* may take one of a few different forms, the first being a simple filesystem path, e.g. `nix-build /tmp/some-file.nix`.
Like the [import builtin](../language/builtins.md#builtins-import) specifying a directory is equivalent to specifying `default.nix` within that directory.
It may also be a [search path](./env-common.md#env-NIX_PATH) (also known as a lookup path) like `<nixpkgs>`, which is convenient to use with `--attr`/`-A`:
```console
$ nix-build '<nixpkgs>' -A firefox
```
(Note the quotation marks around `<nixpkgs>`, which will be necessary in most Unix shells.)
If a *fileish* starts with `http://` or `https://`, it is interpreted as the URL of a tarball which will be fetched and unpacked.
Lix will then `import` the unpacked directory, so these tarballs must include at least a single top-level directory with a file called `default.nix`
For example, you could build from a specific version of Nixpkgs with something like:
```console
$ nix-build "https://github.com/NixOS/nixpkgs/archive/refs/heads/release-23.11.tar.gz" -A firefox
```
If a path starts with `flake:`, the rest of the argument is interpreted as a [flakeref](./new-cli/nix3-flake.md#flake-references) (see `nix flake --help` or `man nix3-flake`), which requires the "flakes" experimental feature to be enabled.
Lix will fetch the flake, and then `import` its unpacked directory, so the flake must include a file called `default.nix`.
For example, the flake analogues to the above `nix-build` commands are:
```console
$ nix-build flake:nixpkgs -A firefox
$ nix-build flake:github:NixOS/nixpkgs/release-23.11 -A firefox
```
Finally, for legacy reasons, if a path starts with `channel:`, the rest of the argument is interpreted as the name of a *nixpkgs* channel tarball to fetch from `https://nixos.org/channels/$CHANNEL_NAME/nixexprs.tar.xz`.
This is a **hard coded URL** pattern and is *not* related to the subscribed channels managed by the [nix-channel](./nix-channel.md) command.
> **Note**: any of the special syntaxes may always be disambiguated by prefixing the path.
> For example: a file in the current directory literally called `<nixpkgs>` can be addressed as `./<nixpkgs>`, to escape the special interpretation.
In summary, a path argument may be one of:
{{#include ./fileish-summary.md}}
## Notes
If an element of *paths* starts with `http://` or `https://`, it is
interpreted as the URL of a tarball that will be downloaded and unpacked
to a temporary location. The tarball must include a single top-level
directory containing at least a file named `default.nix`.
`nix-build` is essentially a wrapper around
[`nix-instantiate`](nix-instantiate.md) (to translate a high-level Nix
+1 -1
View File
@@ -14,7 +14,7 @@ The moving parts of channels are:
- The official channels listed at <https://nixos.org/channels>
- The user-specific list of [subscribed channels](#subscribed-channels)
- The [downloaded channel contents](#channels)
- The [Nix expression search path](@docroot@/command-ref/conf-file.md#conf-nix-path), set with the [`-I` option](#opt-I) or the [`NIX_PATH` environment variable](#env-NIX_PATH)
- The [Nix expression search path](@docroot@/command-ref/conf-file.md#conf-nix-path), set with the [`-I` option](#opt-i) or the [`NIX_PATH` environment variable](#env-NIX_PATH)
> **Note**
>
@@ -36,7 +36,7 @@ Instead, it looks in a few locations, and acts on all profiles it finds there:
>
> Not stable; subject to change
>
> Do not rely on this functionality; it just exists for migration purposes and may change in the future.
> 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 Lix.
<!-- FIXME(Qyriad): this is inconsistent with https://git.lix.systems/lix-project/lix/issues/215, needs updating when that happens -->
+1 -1
View File
@@ -8,7 +8,7 @@
[`--option` *name* *value*]
[`--arg` *name* *value*]
[`--argstr` *name* *value*]
[{`--file` | `-f`} *fileish*]
[{`--file` | `-f`} *path*]
[{`--profile` | `-p`} *path*]
[`--system-filter` *system*]
[`--dry-run`]
@@ -26,7 +26,7 @@ This operation deletes the specified generations of the current profile.
>
> Older *and newer* generations will be deleted by this operation.
>
> One might expect this to just delete older generations than the current one, but that is only true if the current generation is also the latest.
> One might expect this to just delete older generations than the curent one, but that is only true if the current generation is also the latest.
> Because one can roll back to a previous generation, it is possible to have generations newer than the current one.
> They will also be deleted.
@@ -11,7 +11,6 @@
[`--from-profile` *path*]
[`--preserve-installed` | `-P`]
[`--remove-all` | `-r`]
[`--priority` *priority*]
# Description
@@ -60,11 +59,6 @@ a number of possible ways:
unambiguous way, which is necessary if there are multiple
derivations with the same name.
- If `--priority` *priority* is given, the priority of the derivations being
installed is set to *priority*. This can be used to override the priority of
the derivations being installed. This is useful if *args* are store paths,
which don't have any priority information.
- If *args* are [store derivations](@docroot@/glossary.md#gloss-store-derivation), then these are
[realised](@docroot@/command-ref/nix-store/realise.md), and the resulting output paths
are installed.
@@ -2,16 +2,16 @@
The following options are allowed for all `nix-env` operations, but may not always have an effect.
- `--file` / `-f` *fileish*\
- `--file` / `-f` *path*\
Specifies the Nix expression (designated below as the *active Nix
expression*) used by the `--install`, `--upgrade`, and `--query
--available` operations to obtain derivations. The default is
`~/.nix-defexpr`.
*fileish* is interpreted the same as with [nix-build](../nix-build.md#fileish-syntax).
See that section for complete details (`nix-build --help`), but in summary, a path argument may be one of:
{{#include ../fileish-summary.md}}
If the argument starts with `http://` or `https://`, it is
interpreted as the URL of a tarball that will be downloaded and
unpacked to a temporary location. The tarball must include a single
top-level directory containing at least a file named `default.nix`.
- `--profile` / `-p` *path*\
Specifies the profile to be used by those operations that operate on
@@ -22,7 +22,7 @@ left untouched; this is not an error. It is also not an error if an
element of *args* matches no installed derivations.
For a description of how *args* is mapped to a set of store paths, see
[`--install`](install.md). If *args* describes multiple
[`--install`](#operation---install). If *args* describes multiple
store paths with the same symbolic name, only the one with the highest
version is installed.
+4 -14
View File
@@ -11,7 +11,7 @@
[{`--attr`| `-A`} *attrPath*]
[`--add-root` *path*]
[`--expr` | `-E`]
*fileish*
*files*
`nix-instantiate` `--find-file` *files…*
@@ -25,11 +25,8 @@ of the resulting store derivations are printed on standard output.
[store derivation]: ../glossary.md#gloss-store-derivation
If *fileish* is the character `-`, then a Nix expression will be read from standard input.
Otherwise, each *fileish* is interpreted the same as with [nix-build](./nix-build.md#fileish-syntax).
See that section for complete details (`nix-build --help`), but in summary, a path argument may be one of:
{{#include ./fileish-summary.md}}
If *files* is the character `-`, then a Nix expression will be read from
standard input.
# Options
@@ -38,14 +35,7 @@ See that section for complete details (`nix-build --help`), but in summary, a pa
- `--parse`\
Just parse the input files, and print their abstract syntax trees on
standard output. The output format of the AST depends on the current
internal representation and may change in the future.
Tooling can use the stderr and exit code of `--parse` to check any
Nix code for correctness, but should not rely on stdout without careful
versioning. Note that `--parse` also checks for unbound variables.
In cases where this is undesired, `with {};` can be prepended
to the program to transform all such parse errors into eval errors.
standard output as a Nix expression.
- `--eval`\
Just parse and evaluate the input files, and print the resulting
+4 -3
View File
@@ -33,9 +33,10 @@ the environment of a derivation for development.
If *path* is not given, `nix-shell` defaults to `shell.nix` if it
exists, and `default.nix` otherwise.
If *path* is given it is interpreted like a [*fileish* argument to nix-build](./nix-build.md#fileish-syntax):
{{#include ./fileish-summary.md}}
If *path* starts with `http://` or `https://`, it is interpreted as the
URL of a tarball that will be downloaded and unpacked to a temporary
location. The tarball must include a single top-level directory
containing at least a file named `default.nix`.
If the derivation defines the variable `shellHook`, it will be run
after `$stdenv/setup` has been sourced. Since this hook is not executed
@@ -4,7 +4,7 @@
# Synopsis
`nix-store` `--delete` [`--ignore-liveness`] [`--skip-live`] [`--delete-closure`] *paths…*
`nix-store` `--delete` [`--ignore-liveness`] *paths…*
# Description
@@ -18,13 +18,6 @@ With the option `--ignore-liveness`, reachability from the roots is
ignored. However, the path still wont be deleted if there are other
paths in the store that refer to it (i.e., depend on it).
This operation will raise an error if any of the paths are still live
and `--ignore-liveness` is not passed. Passing `--skip-live` will
prevent this from being considered an error.
The option `--delete-closure` will also attempt to delete any paths
that are in the given path's dependency closure.
{{#include ./opt-common.md}}
{{#include ../opt-common.md}}
@@ -93,12 +93,9 @@ symlink.
[deriver]: ../../glossary.md#gloss-deriver
- `--valid-derivers`\
Prints the set of all [derivers](../../glossary.md#gloss-deriver) that can be
used to build the store paths *paths*.
This differs from `--deriver`, which prints the deriver that actually
produced *paths*.
No deriver may be returned if is not present in the store,
eg, if *paths* were substituted from a binary cache.
Prints a set of derivation files (`.drv`) which are supposed produce
said paths when realized. Might print nothing, for example for source paths
or paths subsituted from a binary cache.
- `--graph`\
Prints the references graph of the store paths *paths* in the format
+1 -1
View File
@@ -85,7 +85,7 @@ Most commands in Lix accept the following command-line options:
- `multiline-with-logs`
Display the raw logs, with a progress bar and activities each in a new line at the bottom.
Displayes the raw logs, with a progress bar and activities each in a new line at the bottom.
- <span id="opt-no-build-output">[`--no-build-output`](#opt-no-build-output)</span> / `-Q`
+13 -25
View File
@@ -39,28 +39,17 @@ $ nix-shell -A native-clangStdenvPackages
### Building from the development shell
Run a clean build and test with `just clean build install test`.
You can also run the unit tests and integration tests separately:
You can build and test Lix with just:
```bash
$ just setup build test-unit
$ just install test-integration
$ just setup
$ just build
$ just test --suite=check
$ just install
$ just test --suite=installcheck
```
Many targets have a `-custom` variant which pass extra arguments to `meson`.
For example, to work on both Lix and nix-eval-jobs you can run:
```
$ just setup-custom -Dnix-eval-jobs=enabled
$ # or
$ mesonFlags=-Dnix-eval-jobs=enabled just setup
```
Note that only targets which don't accept extra arguments can be used when
running multiple targets at once; `just setup build` is fine, but `just
setup-custom build` is an error. The `test` target is usually the last one to
run, so it always accepts extra arguments.
(Check and installcheck may both be done after install, allowing you to omit the --suite argument entirely, but this is the order package.nix runs them in.)
You can also build Lix manually:
@@ -148,11 +137,10 @@ You can also build Lix for one of the [supported platforms](#platforms).
Lix can be built for various platforms, as specified in [`flake.nix`]:
[`flake.nix`]: https://git.lix.systems/lix-project/lix/src/branch/main/flake.nix
[`flake.nix`]: https://git.lix.systems/lix-project/lix/lix/branch/main/flake.nix
- `x86_64-linux`
- `x86_64-darwin`
- `x86_64-freebsd`
- `i686-linux`
- `aarch64-linux`
- `aarch64-darwin`
@@ -229,7 +217,7 @@ Lix uses a string with the following format to identify the *system type* or *pl
It is set when Lix is compiled for the given system, and determined by [Meson's `host_machine.cpu_family()` and `host_machine.system()` values](https://mesonbuild.com/Reference-manual_builtin_host_machine.html).
For historic reasons and backward-compatibility, some CPU and OS identifiers are translated from the GNU Autotools naming convention in [`meson.build`](https://git.lix.systems/lix-project/lix/src/branch/main/meson.build) as follows:
For historic reasons and backward-compatibility, some CPU and OS identifiers are translated from the GNU Autotools naming convention in [`meson.build`](https://git.lix.systems/lix-project/lix/blob/main/meson.build) as follows:
| `host_machine.cpu_family()` | Nix |
|----------------------------|---------------------|
@@ -256,13 +244,13 @@ To build with one of those environments, you can use
$ nix build .#nix-ccacheStdenv
```
for <a id="nix-with-flakes">flake-enabled Nix</a>, or
for flake-enabled Nix, or
```console
$ nix-build --attr nix-ccacheStdenv
```
for <a id="classic-nix">classic Nix</a>.
for classic Nix.
You can use any of the other supported environments in place of `nix-ccacheStdenv`.
@@ -463,11 +451,11 @@ The following metadata properties are supported for builtin functions:
* `implementation` (optional): a C++ expression specifying the implementation of the builtin.
It must be a function of signature `void(EvalState &, PosIdx, Value * *, Value &)`.
If not specified, defaults to `prim_${name}`.
* `renameInGlobalScope` (optional): whether the definition should be "hidden" in the global scope by prefixing its name with two underscores.
* `renameInGlobalScope` (optional): whether the definiton should be "hidden" in the global scope by prefixing its name with two underscores.
If not specified, defaults to `true`.
* `args` (required): list containing the names of the arguments, as shown in the documentation.
All arguments must be listed here since the function arity is derived as the length of this list.
* `experimental_feature` (optional): the user-facing name of the experimental feature which needs to be enabled for the builtin function to be available.
* `experimental_feature` (optional): the user-facing name of the experimental feature which needs to be enabled for the bultin function to be available.
If not specified, no experimental feature is required.
New builtin function definition files must be added to `lix/libexpr/builtins` and registered in the `builtin_definitions` list in `lix/libexpr/meson.build`.
+2 -2
View File
@@ -101,7 +101,7 @@
See [File System Object](@docroot@/architecture/file-system-object.md) for details.
[file system object]: #gloss-store-path
[file system object]: #gloss-file-system-object
- [store object]{#gloss-store-object}
@@ -249,7 +249,7 @@
links. NARs are generated and unpacked using `nix-store --dump`
and `nix-store --restore`.
- [`∅`]{#gloss-empty-set}
- [`∅`]{#gloss-emtpy-set}
The empty set symbol. In the context of profile history, this denotes a package is not present in a particular version of the profile.
+1 -1
View File
@@ -148,7 +148,7 @@ paths) are set.
For example, the following command gets all dependencies of the
Pan newsreader, as described by [its
Nix expression](https://github.com/NixOS/nixpkgs/blob/master/pkgs/by-name/pa/pan/package.nix):
Nix expression](https://github.com/NixOS/nixpkgs/blob/master/pkgs/applications/networking/newsreaders/pan/default.nix):
```console
$ nix-shell '<nixpkgs>' --attr pan
@@ -261,7 +261,7 @@ Derivations can declare some infrequently used optional attributes.
useful for very trivial derivations (such as `writeText` in Nixpkgs)
that are cheaper to build than to substitute from a binary cache.
You may disable the effects of this attribute by enabling the
You may disable the effects of this attibute by enabling the
`always-allow-substitutes` configuration option in Lix.
> **Note**
+1 -1
View File
@@ -164,7 +164,7 @@ Note that lists are only lazy in values, and they are strict in length.
An attribute set is a collection of name-value-pairs (called *attributes*) enclosed in curly brackets (`{ }`).
An attribute name can be an identifier or a [string](#type-string).
An attribute name can be an identifier or a [string](#string).
An identifier must start with a letter (`a-z`, `A-Z`) or underscore (`_`), and can otherwise contain letters (`a-z`, `A-Z`), numbers (`0-9`), underscores (`_`), apostrophes (`'`), or dashes (`-`).
> *name* = *identifier* | *string* \
@@ -126,7 +126,7 @@ $ nix-env --install --attr nixpkgs.subversion
```
will install the package called `subversion` from `nixpkgs` channel (which is, of course, the
[Subversion version management system](https://subversion.apache.org/)).
[Subversion version management system](http://subversion.tigris.org/)).
> **Note**
>
+1 -1
View File
@@ -13,7 +13,7 @@
- The `discard-references` feature has been stabilized.
This means that the
`unsafeDiscardReferences`
[unsafeDiscardReferences](@docroot@/contributing/experimental-features.md#xp-feature-discard-references)
attribute is no longer guarded by an experimental flag and can be used
freely.
+135 -2
View File
@@ -1,4 +1,137 @@
# Lix 2.92 "Bombe glacée" (2025-01-18)
# Lix 2.92.3 (2025-06-30)
## Fixes
- Revert CVE-2025-52992 failed mitigation [fj#883](https://git.lix.systems/lix-project/lix/issues/883) [fj#887](https://git.lix.systems/lix-project/lix/issues/887) [cl/3432](https://gerrit.lix.systems/c/lix/+/3432) [cl/3525](https://gerrit.lix.systems/c/lix/+/3525) [cl/3526](https://gerrit.lix.systems/c/lix/+/3526) [cl/3527](https://gerrit.lix.systems/c/lix/+/3527)
Following the initial mitigation of **CVE-2025-52992** in `cl/3432`, we
received reports of **unexpected deletion of in-use store paths**.
Upon investigation, we found that the patch did **not correctly cancel all
automatic deleters**, resulting in potentially critical path loss during normal
operation.
Given the severity and time-sensitive nature of the situation ([see incident
report](https://lix.systems/blog/2025-06-27-lix-critical-bug/)), we evaluated
possible options to repair the behavior safely. However, we concluded that a
rushed fix would either
* **Overdelete**, i.e. breaking running systems, or,
* **Underdelete**, effectively **reopening CVE-2025-52992** while leaving
orphaned paths behind.
As **CVE-2025-52992 has no known exploit vector**, and correctness is critical
in the Lix project, we have **fully reverted the previous mitigations**.
The affected patches (`cl/3432`) have been rolled back for the time being.
Moving forward, the Lix team will rework this code path in a **long-term,
correctness-first fix** on the main branch. We will explore backporting it to
stable channels once its safety is assured.
We are deeply sorry for the stability incident and the Lix team remain
available for assisting you in recovering your systems.
Many thanks to [Raito Bezarius](https://git.lix.systems/raito) and [eldritch horrors](https://git.lix.systems/pennae) for this.
- Fallback to safe temp dir when build-dir is unwritable [fj#876](https://git.lix.systems/lix-project/lix/issues/876) [cl/3503](https://gerrit.lix.systems/c/lix/+/3503)
Non-daemon builds started failing with a permission error after introducing the `build-dir` option:
```
$ nix build --store ~/scratch nixpkgs#hello --rebuild
error: creating directory '/nix/var/nix/builds/nix-build-hello-2.12.2.drv-0': Permission denied
```
This happens because:
1. These builds are not run via the daemon, which owns `/nix/var/nix/builds`.
2. The user lacks permissions for that path.
We considered making `build-dir` a store-level option and defaulting it to `<chroot-root>/nix/var/nix/builds` for chroot stores, but opted instead for a fallback: if the default fails, Nix now creates a safe build directory under `/tmp`.
To avoid CVE-2025-52991, the fallback uses an extra path component between `/tmp` and the build dir.
**Note**: this fallback clutters `/tmp` with build directories that are not cleaned up. To prevent this, explicitly set `build-dir` to a path managed by Lix, even for local workloads.
Many thanks to [Raito Bezarius](https://git.lix.systems/raito) and [eldritch horrors](https://git.lix.systems/pennae) for this.
# Lix 2.92.2 (2025-06-23)
## Breaking Changes
- Fixed output derivations can be run using `pasta` network isolation [fj#285](https://git.lix.systems/lix-project/lix/issues/285) [cl/3430](https://gerrit.lix.systems/c/lix/+/3430)
Fixed output derivations traditionally run in the host network namespace.
On Linux this allows such derivations to communicate with other sandboxes
or the host using the abstract Unix domains socket namespace; this hasn't
been unproblematic in the past and has been used in two distinct exploits
to break out of the sandbox. For this reason fixed output derivations can
now run in a network namespace (provided by [`pasta`]), restricted to TCP
and UDP communication with the rest of the world. When enabled this could
be a breaking change and we classify it as such, even though we don't yet
enable or require such isolation by default. We may enforce this in later
releases of Lix once we have sufficient confidence that breakage is rare.
[`pasta`]: https://passt.top/
Many thanks to [eldritch horrors](https://git.lix.systems/pennae) and [puck](https://git.lix.systems/puck) for this.
## Fixes
- Always clean up scratch paths after derivations failed to build [cl/3432](https://gerrit.lix.systems/c/lix/+/3432)
Previously, scratch paths created during builds were not always cleaned up if
the derivation failed, potentially leaving behind unnecessary temporary files
or directories in the Nix store.
This fix ensures that such paths are consistently removed after a failed build,
improving Nix store hygiene, hardening Lix against mis-reuse of failed builds
scratch paths.
Many thanks to [Raito Bezarius](https://git.lix.systems/raito) and [eldritch horrors](https://git.lix.systems/pennae) for this.
- `build-dir` no longer defaults to `temp-dir` [cl/3431](https://gerrit.lix.systems/c/lix/+/3431)
The directory in which temporary build directories are created no longer defaults
to the value of the `temp-dir` setting to avoid builders making their directories
world-accessible. This behavior has been used to escape the build sandbox and can
cause build impurities even when not used maliciously. We now default to `builds`
in `NIX_STATE_DIR` (which is `/nix/var/nix/builds` in the default configuration).
Many thanks to [eldritch horrors](https://git.lix.systems/pennae) for this.
- Forbid impure path accesses in pure evaluation mode again [cl/2708](https://gerrit.lix.systems/c/lix/+/2708)
Lix 2.92.0 mistakenly started allowing the access to ancestors of allowed paths in pure evaluation mode.
This made it possible to bypass the purity restrictions, for example by copying arbitrary files to the store:
```nix
builtins.path {
path = "/";
filter = …;
}
```
Restore the previous behaviour of prohibiting such impure accesses.
Many thanks to [alois31](https://git.lix.systems/alois31) for this.
- Parsing failures in flake.lock no longer crash Lix [fj#559](https://git.lix.systems/lix-project/lix/issues/559) [cl/2401](https://gerrit.lix.systems/c/lix/+/2401)
Failure to parse `flake.lock` no longer hard-crashes Lix and instead produces a nice error message.
```
error:
… while updating the lock file of flake 'git+file:///Users/jade/lix/lix2'
… while parsing the lock file at /nix/store/mm5dqh8a729yazzj82cjffxl97n5c62s-source//flake.lock
error: [json.exception.parse_error.101] parse error at line 1, column 1: syntax error while parsing value - invalid literal;
last read: '#'
```
Many thanks to [gilice](https://git.lix.systems/gilice) for this.
- Fix `--debugger --ignore-try` [cl/2440](https://gerrit.lix.systems/c/lix/+/2440)
When in debug mode (e.g. from using the `--debugger` flag), enabling [`ignore-try`](@docroot@/command-ref/conf-file.md#conf-ignore-try) once again properly disables debug REPLs within [`builtins.tryEval`](@docroot@/language/builtins.md#builtins-tryEval) calls. Previously, a debug REPL would be started as if `ignore-try` was disabled, but that REPL wouldn't actually be in debug mode, and upon exiting the REPL the evaluating process would segfault.
Many thanks to [Dusk Banks](https://git.lix.systems/bb010g) for this.
# Lix 2.92.0 (2025-01-18)
@@ -51,8 +184,8 @@
{
formatter = eachSystem (pkgs:
pkgs.writeShellScriptBin "formatter" ''
if [[ $# = 0 ]]; then set -- .; fi
exec "${pkgs.nixfmt-rfc-style}/bin/nixfmt" "$@"
if [[ $# = 0 ]]; set -- .; fi
exec "${pkgs.nixfmt-rfc-style}/bin/nixfmt "$@"
'');
};
}
-870
View File
@@ -1,870 +0,0 @@
# Lix 2.93 "Bici Bici" (2025-05-09)
# Lix 2.93.3 (2025-07-22)
## Improvements
- `--keep-failed` chowns the build directory to the user that request the build [cl/3678](https://gerrit.lix.systems/c/lix/+/3678)
Running a build with `--keep-failed` now chowns the temporary directory from the
builder user and group to the user that request the build if the build came from
a local user connected to the daemon. This makes inspecting failed derivations a
lot easier. On Linux the build directory made visible to the user will not be in
the same path as it was in the sandbox and continuing builds will usually break.
Many thanks to [eldritch horrors](https://git.lix.systems/pennae) for this.
# Lix 2.93.2 (2025-06-30)
## Fixes
- Revert CVE-2025-52992 failed mitigation [fj#883](https://git.lix.systems/lix-project/lix/issues/883) [fj#887](https://git.lix.systems/lix-project/lix/issues/887) [cl/3444](https://gerrit.lix.systems/c/lix/+/3444) [cl/3528](https://gerrit.lix.systems/c/lix/+/3528)
Following the initial mitigation of **CVE-2025-52992** in `cl/3444`, we
received reports of **unexpected deletion of in-use store paths**.
Upon investigation, we found that the patch did **not correctly cancel all
automatic deleters**, resulting in potentially critical path loss during normal
operation.
Given the severity and time-sensitive nature of the situation ([see incident
report](https://lix.systems/blog/2025-06-27-lix-critical-bug/)), we evaluated
possible options to repair the behavior safely. However, we concluded that a
rushed fix would either
* **Overdelete**, i.e. breaking running systems, or,
* **Underdelete**, effectively **reopening CVE-2025-52992** while leaving
orphaned paths behind.
As **CVE-2025-52992 has no known exploit vector**, and correctness is critical
in the Lix project, we have **fully reverted the previous mitigations**.
The affected patches (`cl/3444`) have been rolled back for the time being.
Moving forward, the Lix team will rework this code path in a **long-term,
correctness-first fix** on the main branch. We will explore backporting it to
stable channels once its safety is assured.
We are deeply sorry for the stability incident and the Lix team remain
available for assisting you in recovering your systems.
Many thanks to [Raito Bezarius](https://git.lix.systems/raito) and [eldritch horrors](https://git.lix.systems/pennae) for this.
- Fallback to safe temp dir when build-dir is unwritable [fj#876](https://git.lix.systems/lix-project/lix/issues/876) [cl/3501](https://gerrit.lix.systems/c/lix/+/3501)
Non-daemon builds started failing with a permission error after introducing the `build-dir` option:
```
$ nix build --store ~/scratch nixpkgs#hello --rebuild
error: creating directory '/nix/var/nix/builds/nix-build-hello-2.12.2.drv-0': Permission denied
```
This happens because:
1. These builds are not run via the daemon, which owns `/nix/var/nix/builds`.
2. The user lacks permissions for that path.
We considered making `build-dir` a store-level option and defaulting it to `<chroot-root>/nix/var/nix/builds` for chroot stores, but opted instead for a fallback: if the default fails, Nix now creates a safe build directory under `/tmp`.
To avoid CVE-2025-52991, the fallback uses an extra path component between `/tmp` and the build dir.
**Note**: this fallback clutters `/tmp` with build directories that are not cleaned up. To prevent this, explicitly set `build-dir` to a path managed by Lix, even for local workloads.
Many thanks to [Raito Bezarius](https://git.lix.systems/raito) and [eldritch horrors](https://git.lix.systems/pennae) for this.
# Lix 2.93.1 (2025-06-23)
## Breaking Changes
- Fixed output derivations can be run using `pasta` network isolation [fj#285](https://git.lix.systems/lix-project/lix/issues/285) [cl/3442](https://gerrit.lix.systems/c/lix/+/3442)
Fixed output derivations traditionally run in the host network namespace.
On Linux this allows such derivations to communicate with other sandboxes
or the host using the abstract Unix domains socket namespace; this hasn't
been unproblematic in the past and has been used in two distinct exploits
to break out of the sandbox. For this reason fixed output derivations can
now run in a network namespace (provided by [`pasta`]), restricted to TCP
and UDP communication with the rest of the world. When enabled this could
be a breaking change and we classify it as such, even though we don't yet
enable or require such isolation by default. We may enforce this in later
releases of Lix once we have sufficient confidence that breakage is rare.
[`pasta`]: https://passt.top/
Many thanks to [eldritch horrors](https://git.lix.systems/pennae) and [puck](https://git.lix.systems/puck) for this.
## Fixes
- Always clean up scratch paths after derivations failed to build [cl/3444](https://gerrit.lix.systems/c/lix/+/3444)
Previously, scratch paths created during builds were not always cleaned up if
the derivation failed, potentially leaving behind unnecessary temporary files
or directories in the Nix store.
This fix ensures that such paths are consistently removed after a failed build,
improving Nix store hygiene, hardening Lix against mis-reuse of failed builds
scratch paths.
Many thanks to [Raito Bezarius](https://git.lix.systems/raito) and [eldritch horrors](https://git.lix.systems/pennae) for this.
- `build-dir` no longer defaults to `temp-dir` [cl/3443](https://gerrit.lix.systems/c/lix/+/3443)
The directory in which temporary build directories are created no longer defaults
to the value of the `temp-dir` setting to avoid builders making their directories
world-accessible. This behavior has been used to escape the build sandbox and can
cause build impurities even when not used maliciously. We now default to `builds`
in `NIX_STATE_DIR` (which is `/nix/var/nix/builds` in the default configuration).
Many thanks to [eldritch horrors](https://git.lix.systems/pennae) for this.
- Remove reliance on Bash for remote stores via SSH [fj#830](https://git.lix.systems/lix-project/lix/issues/830) [fj#805](https://git.lix.systems/lix-project/lix/issues/805) [fj#304](https://git.lix.systems/lix-project/lix/issues/304) [cl/3159](https://gerrit.lix.systems/c/lix/+/3159)
The pre-flight `echo started` handshake -- added years ago to catch race conditions -- has been removed.
After removal of connection sharing in Lix 2.93, it required a Bash-compatible shell and a standard `echo`, so it failed on:
* builders protected by `ForceCommand` wrappers (e.g. `nix-remote-build`),
* BusyBox / initrd images with no Bash,
* hosts using non-POSIX shells such as Nushell.
The race the probe once addressed was tied to SSH connection-sharing -- since connection-sharing code has already been removed, the probe is now pointless.
Real connection or protocol errors are now left to SSH/Nix to report directly.
This is technically a breaking change if you had scripts that relied on the literal "started" which needs to be updated to rely on other signals, e.g., exit codes.
Many thanks to [Raito Bezarius](https://git.lix.systems/raito) for this.
## Miscellany
- Deprecation of CA derivations, dynamic derivations, and impure derivations [fj#815](https://git.lix.systems/lix-project/lix/issues/815)
Content-addressed derivations are now deprecated and slated for removal in Lix 2.94.
We're doing this because the CA derivation system has been a known cause of problems
and inconsistencies, is unmaintained, habitually makes improving the store code very
difficult (or blocks such improvements outright), and is beset by a number of design
flaws that in our opinion cannot be fixed without a full reimplementation from zero.
Dynamic derivations and impure derivations are built on the CA derivation framework,
and owing to this they too are deprecated and slated for removal in another release.
# Lix 2.93.0 (2025-05-09)
## Breaking Changes
- more deprecated features
This release cycle features a new batch of deprecated (anti-)features.
You can opt in into the old behavior with `--extra-deprecated-features` or any equivalent configuration option.
- `cr-line-endings`: Current handling of CR (`\r`) or CRLF (`\r\n`) line endings in Nix is inconsistent and broken, and will lead to unexpected evaluation results with certain strings. Given that fixing the semantics might silently alter the evaluation result of derivations, the only option at the moment is to disallow them altogether. More proper support for CRLF is planned to be added back again in the future. Until then, all files must use `\n` exclusively.
- `nul-bytes`: Currently the Nix grammar allows NUL bytes (`\0`) in strings, and thus indirectly also in identifiers. Unfortunately, several core parts of the code base still work with NUL-terminated strings and cannot easily be migrated. Also note that it is still possible to introduce NUL bytes and thus problematic behavior via other means, those are tracked separately.
Many thanks to [piegames](https://git.lix.systems/piegames) and [eldritch horrors](https://git.lix.systems/pennae) for this.
- Removal of the `recursive-nix` experimental feature [fj#767](https://git.lix.systems/lix-project/lix/issues/767) [cl/2872](https://gerrit.lix.systems/c/lix/+/2872)
The `recursive-nix` experimental feature and all associated code have been removed.
`recursive-nix` enabled running Nix operations (like evaluations and builds) *inside* a derivation builder. This worked by spawning a temporary Nix daemon socket within the build environment, allowing the derivation to emit outputs that appeared in the outer store. This was primarily used to prototype **dynamic derivations** (dyndrvs), where build plans are generated on-the-fly during a build.
However, this approach introduced critical issues:
- It entrenched the legacy Nix daemon protocol as part of the derivation ABI, which is a blocker for future stabilization.
- It imposed tight coupling between sandbox setup code and knowledge of Nix internals, complicating refactoring and long-term maintenance.
- It was never intended to be the final design for dynamic derivations. The original Nix implementation team, who are leading dyndrv development, have agreed it will be replaced (likely via `varlink` or similar) before any stabilization.
- There is currently no known usage of `recursive-nix` on `lix` or elsewhere **in production**.
If you're using `recursive-nix` for something niche or experimental, we'd love to hear from you on the RFD issue.
You can still run `nix` inside a builder manually if needed — including with isolated user namespaces and fake stores — but the special daemon-handshake machinery is gone.
This removal unblocks several important internal cleanups.
Many thanks to [Raito Bezarius](https://git.lix.systems/raito) for this.
- Flake inputs/`builtins.fetchTree` invocations with `type = "file"` now have consistent (but different from previous versions) resulting paths [fj#750](https://git.lix.systems/lix-project/lix/issues/750) [cl/2864](https://gerrit.lix.systems/c/lix/+/2864)
Previously `fetchTree { type = "file"; url = "...", narHash = "sha256-..."; }` could return a different result depending on whether someone has run `nix store add-path --name source ...` on a path with the same `narHash` as the flake input/`fetchTree` invocation (or if such a path exists in an accessible binary cache).
In the past `type = "file"` flake inputs were, in contrast to all other flake inputs, hashed in *flat* hash mode rather than *recursive* hash mode.
The difference between the two is that *flat* mode hashes are just what you get from `sha256sum` of a single file, whereas *recursive* hashes are the SHA256 sum of a NAR (Nix ARchive, a deterministic tarball-like format) of a file tree.
Much of flakes assumes that everything is recursive-hashed including `nix flake archive`, substitution of flake inputs from binary caches, and more, which led to the substitution path code being taken if such a path is present, yielding a different store path non-deterministically.
To fix this non-deterministic evaluation bug, we needed to break derivation hash stability, so some Nix evaluations now produce different results than previous versions of Lix.
Lix now has consistent behaviour with CppNix 2.24 with respect to `file` flake inputs: they are *always* recursively hashed.
Many thanks to [jade](https://git.lix.systems/jade) for this.
- Builders are always started in a fresh cgroup namespace [cl/1996](https://gerrit.lix.systems/c/lix/+/1996)
If you haven't enabled the experimental `cgroups` feature, Nix previously launched builder processes in new namespaces but did not create new cgroup namespaces. As a result, derivations could access and observe the parent cgroup namespace.
Although this update introduces a breaking change, it ensures that all derivations now start in a fresh cgroup namespace by default. This reduces potential impurities observable within the sandbox, improving the likelihood of reproducible builds across different environments.
Many thanks to [Raito Bezarius](https://git.lix.systems/raito) for this.
- `nix-instantiate --parse` outputs json [fj#487](https://git.lix.systems/lix-project/lix/issues/487) [nix#11124](https://github.com/NixOS/nix/issues/11124) [nix#4726](https://github.com/NixOS/nix/issues/4726) [nix#3077](https://github.com/NixOS/nix/issues/3077) [cl/2190](https://gerrit.lix.systems/c/lix/+/2190)
`nix-instantiate --parse` does not print out the AST in a Nix-like format anymore.
Instead, it now prints a JSON representation of the internal expression tree.
Tooling should not rely on the stdout of `nix-instantiate --parse`.
We've done our best to ensure that the new behavior is as compatible with the old one as possible.
If you depend on the old behavior in ways that are not covered anymore or are otherwise negatively affected by this change,
then please reach out so that we can find a sustainable solution together.
Many thanks to [piegames](https://git.lix.systems/piegames) and [eldritch horrors](https://git.lix.systems/pennae) for this.
- Remove experimental repl-flake [gh#10103](https://github.com/NixOS/nix/issues/10103) [fj#557](https://git.lix.systems/lix-project/lix/issues/557) [gh#10299](https://github.com/NixOS/nix/pull/10299) [cl/2147](https://gerrit.lix.systems/c/lix/+/2147)
The `repl-flake` experimental feature flag has been removed, its functionality is now the default when `flakes` experimental feature is active. The `nix repl` command now works like the rest of the new CLI in that `nix repl {path}` now tries to load a flake at `{path}` (or fails if the `flakes` experimental feature isn't enabled).
Many thanks to [Jonathan De Troye](https://github.com/detroyejr) and [KFears](https://git.lix.systems/kfearsoff) for this.
## Features
- `lix foo` now invokes `lix-foo` from PATH [cl/2119](https://gerrit.lix.systems/c/lix/+/2119)
Lix introduces the ability to extend the Nix command line by adding custom
binaries to the `PATH`, similar to how Git integrates with other tools. This
feature allows developers and end users to enhance their workflow by
integrating additional functionalities directly into the Nix CLI.
#### Examples
For example, a user can create a custom deployment tool, `lix-deploy-tool`, and
place it in their `PATH`. This allows them to execute `lix deploy-tool`
directly from the command line, streamlining the process of deploying
applications without needing to switch contexts or use separate commands.
#### Limitations
For now, autocompletion is supported to discover new custom commands, but the
documentation will not render them. Argument autocompletion of the custom
command is not supported either.
This is also locked behind a new experimental feature called
`lix-custom-sub-commands` to enable developing all the required features.
Only the top-level `lix` command can be extended, this is an artificial
limitation for the time being until we flesh out this feature.
#### Outline
In the future, this feature may pave the way for moving the Flake subcommand
line to its own standalone binary, allowing for a more focused approach to
managing Nix Flakes while letting the community explore alternatives to
dependency management.
Many thanks to [Raito Bezarius](https://git.lix.systems/raito) for this.
- `nix-env --install` now accepts a `--priority` flag [cl/2607](https://gerrit.lix.systems/c/lix/+/2607)
`nix-env --install` now has an optional `--priority` flag.
Previously, it was only possible to specify a priority by adding a
`meta.priority` attribute to a derivation. `meta` attributes only exist during
eval, so that wouldn't work for installing a store path. It was also possible
to change a priority after initial installation using `nix-env --set-flag`,
however if there is already a conflict that needs to be resolved via priorities,
this will not work.
Now, a priority can be set at install time using `--priority`, which allows for
cleanly overriding the priority at install time.
#### Example
```console
$ nix-build
$ nix-env --install --priority 100 ./result
```
Many thanks to [Andrew Hamon](https://github.com/andrewhamon) for this.
- Add support for eBPF USDT/dtrace probes inside Lix [fj#727](https://git.lix.systems/lix-project/lix/issues/727) [cl/2884](https://gerrit.lix.systems/c/lix/+/2884)
eBPF tracers like `bpftrace` and `dtrace` are a group of similar tools for debugging production systems.
User-space statically defined tracing probes (USDT) allow for defining zero or near-zero disabled-probe-effect probes, thus allowing instrumentation of hot paths in production builds.
Lix now has internal support for defining these probes and has shipped its first probe.
As of this writing it is available by default in the Linux build of Lix.
To try it out on Linux, you can use the following example command:
```
$ sudo bpftrace -l 'usdt:/path/to/liblixstore.so:*:*'
usdt:/path/to/liblixstore.so:lix_store:filetransfer__read
$ sudo bpftrace -e 'usdt:*:lix_store:filetransfer__read { printf("%s read %d\n", str(arg0), arg1); }'
Attaching 1 probe...
https://cache.nixos.org/wvpzaycmvs39h5bcsfrxkjsg48mj4h73.narinf.. read 8192
https://cache.nixos.org/wvpzaycmvs39h5bcsfrxkjsg48mj4h73.narinf.. read 8192
https://cache.nixos.org/nar/1qshsc30nlarzdig0v9b1aasdkwaxhnv0a0.. read 65536
https://cache.nixos.org/nar/1qshsc30nlarzdig0v9b1aasdkwaxhnv0a0.. read 65536
```
Note that bpftrace does not offer any way to list the arguments to USDT probes in a human readable form.
To get the probe definitions, see the `*.d` files in the Lix source code, for example, `lix/libstore/trace-probes.d`.
For more resources on eBPF/bpftrace and dtrace, see:
* The book "BPF Performance Tools" by Brendan Gregg, which discusses bpftrace at length.
* <https://ebpf.io/get-started/>
* [Illumos' dtrace book](https://illumos.org/books/dtrace/preface.html)
Many thanks to [jade](https://git.lix.systems/jade) for this.
## Improvements
- Always print `post-build-hook` logs [fj#675](https://git.lix.systems/lix-project/lix/issues/675) [cl/2801](https://gerrit.lix.systems/c/lix/+/2801)
Logs of `post-build-hook` are now printed unconditionally.
They used to be tied to whether print-build-logs is set, which made debugging them a nightmare when they fail, since the failure output would be eaten if build logs are disabled.
Most usages of `post-build-hook` are pretty quiet especially compared to build logs, so it should not be that bothersome to not be able to turn off.
Many thanks to [jade](https://git.lix.systems/jade) for this.
- Crashes land in syslog now [cl/2640](https://gerrit.lix.systems/c/lix/+/2640)
When Lix crashes with unexpected exceptions and in some other conditions, it prints bug reporting instructions.
Previously, these only landed in stderr and not in syslog.
However, on larger Lix installations, it may be the case that Lix crashes in the client without the logs landing in the system logs, which impeded diagnosis.
Now, such crashes always land in syslog too.
Many thanks to [jade](https://git.lix.systems/jade) for this.
- Deletion of specific paths no longer fails fast [cl/2778](https://gerrit.lix.systems/c/lix/+/2778)
`nix-store --delete` and `nix store delete` now continue deleting
paths even if some of the given paths are still live. An error is only
thrown once deletion of all the given paths has been
attempted. Previously, if some paths were deletable and others
weren't, the deletable ones would be deleted iff they preceded the
live ones in lexical sort order.
The error message for still-live paths no longer reports the paths
that could not be deleted, because there could potentially be many of
these.
Many thanks to [lheckemann](https://git.lix.systems/lheckemann) for this.
- `--skip-live` for path deletion [cl/2778](https://gerrit.lix.systems/c/lix/+/2778)
`nix-store --delete` and `nix store delete` now support a
`--skip-live` option and a `--delete-closure` option.
This makes custom garbage-collection logic a lot easier to implement
and experiment with:
- Paths known to be large can be thrown at `nix store delete` without
having to manually filter out those that are still reachable from a
root, e.g.
`nix store delete /nix/store/*mbrola-voices*`
- The `--delete-closure` option allows extending this to paths that are
not large themselves but do have a large closure size, e.g.
`nix store delete /nix/store/*nixos-system-gamingpc*`.
- Other heuristics like atime-based deletion can be applied more
easily, because `nix store delete` once again takes over the task of
working out which paths can't be deleted.
Many thanks to [lheckemann](https://git.lix.systems/lheckemann) for this.
- Allow `nix store diff-closures` to output JSON [cl/2360](https://gerrit.lix.systems/c/lix/+/2360)
Add the `--json` option to the `nix store diff-closures` command to allow users to collect diff information into a machine readable format.
```bash
$ build/lix/nix/nix store diff-closures --json /run/current-system /nix/store/n1prick95pihd4lkv58nn3pzg1yivcdb-neovim-0.10.4/bin/nvim | jq | head -n 23
{
"packages": {
"02overridedns": {
"sizeDelta": -688,
"versionsAfter": [],
"versionsBefore": [
""
]
},
"50-coredump.conf": {
"sizeDelta": -1976,
"versionsAfter": [],
"versionsBefore": [
""
]
},
"Diff": {
"sizeDelta": -514864,
"versionsAfter": [],
"versionsBefore": [
"0.4.1"
]
},
```
Many thanks to [Xavier Maso](https://github.com/pamplemousse) for this.
- Show all missing and unexpected arguments in erroneous function calls [cl/2477](https://gerrit.lix.systems/c/lix/+/2477)
When calling a function that expects an attribute set, lix will now show all
missing and unexpected arguments.
e.g. with `({ a, b, c } : a + b + c) { a = 1; d = 1; }` lix will now show the error:
```
[...]
error: function 'anonymous lambda' called without required arguments 'b' and 'c' and with unexpected argument 'd'
[...]
```
Previously lix would just show `b`.
Furthermore lix will now only suggest arguments that aren't yet used.
e.g. with `({ a?1, b?1, c?1 } : a + b + c) { a = 1; d = 1; e = 1; }` lix will now show the error:
```
[...]
error: function 'anonymous lambda' called with unexpected arguments 'd' and 'e'
at «string»:1:2:
1| ({ a?1, b?1, c?1 } : a + b + c) { a = 1; d = 1; e = 1; }
| ^
Did you mean one of b or c?
```
Previously lix would also suggest `a`.
Suggestions are unfortunately still currently just for the first missing argument.
Many thanks to [Zitrone](https://git.lix.systems/quantenzitrone) for this.
- REPL improvements [cl/2319](https://gerrit.lix.systems/c/lix/+/2319) [cl/2320](https://gerrit.lix.systems/c/lix/+/2320) [cl/2321](https://gerrit.lix.systems/c/lix/+/2321)
The REPL has seen various minor improvements:
- Variable declarations have been improved, making copy-pasting code from attrsets a lot easier:
- Declarations can now optionally end with a semicolon
- Multiple declarations can be done within one command, separated by semicolon
- The `foo.bar = "baz";` syntax from attrsets is also supported, however without the attrset merging rules and with restrictions on dynamic attrs like in `let` bindings.
- Variable names now use the proper Nix grammar rules, instead of a regex that only vaguely matched legal identifiers.
- Better error messages overall
- The `:env` command to print currently available variables now also works outside of debug mode
- Adding variables to the REPL now prints a small message on success
Many thanks to [piegames](https://git.lix.systems/piegames) for this.
- Consistently use SRI hashes in hash mismatch errors [cl/2868](https://gerrit.lix.systems/c/lix/+/2868)
Previously there were a few weird cases (flake inputs, e.g., among others) where Lix would print the old Nix base-32 hash format (sha256:abcd...) rather than the newer [SRI base64 format](https://developer.mozilla.org/en-US/docs/Web/Security/Subresource_Integrity) (sha256-AAAA...) that is used in most Lix hash mismatch errors.
This made it annoying to compare them to hashes shown by most of the modern UI surface of Lix which uses SRI.
Many thanks to [jade](https://git.lix.systems/jade) for this.
- Allow specifying ports for remote ssh[-ng] stores [cl/2432](https://gerrit.lix.systems/c/lix/+/2432)
You can now specify which port should be used for a remote ssh store (e.g. for remote/distributed builds) through a uri parameter.
E.g., when a remote builder `foo` is listening on port `1234` instead of the default, it can be specified like this `ssh://foo?port=1234`.
Many thanks to [seppel3210](https://github.com/Seppel3210) for this.
- Implicit `__toString` now have stack trace entries [cl/3055](https://gerrit.lix.systems/c/lix/+/3055)
Coercion of attribute sets to strings via their `__toString` attribute now produce stack
frames pointing to the coercion site and the attribute definition. This makes locating a
coercion function error easier as the fault location is now more likely to be presented.
Previously:
```
nix-repl> builtins.substring 1 1 "${{ __toString = self: throw ''bar''; }}"
error:
… while calling the 'substring' builtin
at «string»:1:1:
1| builtins.substring 1 1 "${{ __toString = self: throw ''bar''; }}"
| ^
… caused by explicit throw
at «string»:1:48:
1| builtins.substring 1 1 "${{ __toString = self: throw ''bar''; }}"
| ^
error: bar
```
Now:
```
nix-repl> builtins.substring 1 1 "${{ __toString = self: throw ''bar''; }}"
error:
… while calling the 'substring' builtin
at «string»:1:1:
1| builtins.substring 1 1 "${{ __toString = self: throw ''bar''; }}"
| ^
… while converting a set to string
at «string»:1:25:
1| builtins.substring 1 1 "${{ __toString = self: throw ''bar''; }}"
| ^
… from call site
at «string»:1:29:
1| builtins.substring 1 1 "${{ __toString = self: throw ''bar''; }}"
| ^
… while calling '__toString'
at «string»:1:42:
1| builtins.substring 1 1 "${{ __toString = self: throw ''bar''; }}"
| ^
… caused by explicit throw
at «string»:1:48:
1| builtins.substring 1 1 "${{ __toString = self: throw ''bar''; }}"
| ^
error: bar
```
Many thanks to [eldritch horrors](https://git.lix.systems/pennae) for this.
## Fixes
- Avoid unnecessarily killing processes for the build user's UID [nix#9142](https://github.com/NixOS/nix/issues/9142) [fj#667](https://git.lix.systems/lix-project/lix/issues/667)
We no longer kill all processes under the build user's UID before and after
builds on Linux with sandboxes enabled.
This avoids unrelated processes being killed. This might happen for instance,
if the user is running Lix inside a container, wherein the build users use the same UIDs as the daemon's.
Many thanks to [teofilc](https://git.lix.systems/teofilc) for this.
- Forbid impure path accesses in pure evaluation mode again [cl/2708](https://gerrit.lix.systems/c/lix/+/2708)
Lix 2.92.0 mistakenly started allowing the access to ancestors of allowed paths in pure evaluation mode.
This made it possible to bypass the purity restrictions, for example by copying arbitrary files to the store:
```nix
builtins.path {
path = "/";
filter = …;
}
```
Restore the previous behaviour of prohibiting such impure accesses.
Many thanks to [alois31](https://git.lix.systems/alois31) for this.
- Ctrl-C works correctly on macOS again [fj#729](https://git.lix.systems/lix-project/lix/issues/729) [cl/3066](https://gerrit.lix.systems/c/lix/+/3066)
Due to a kernel bug in macOS's `poll(2)` implementation where it would forget about event subscriptions, our detection of closed connections in the Lix daemon didn't work and left around lingering daemon processes.
We have rewritten that thread to use `kqueue(2)`, which is what the `poll(2)` implementation uses internally in the macOS kernel, so now Ctrl-C on clients will reliably terminate daemons once more.
This FD close monitoring has had the highest Apple bug ID references per line of code anywhere in the project, and hopefully not using poll anymore will stop us hitting bugs in poll.
Many thanks to [jade](https://git.lix.systems/jade) for this.
- Fetch peer PID for daemon connections on macOS [fj#640](https://git.lix.systems/lix-project/lix/issues/640) [cl/2453](https://gerrit.lix.systems/c/lix/+/2453)
`nix-daemon` will now fetch the peer PID for connections on macOS, to match behavior with Linux.
Besides showing up in the log output line, If `nix-daemon` is given an argument (such as `--daemon`)
that argument will be overwritten with the peer PID for the forked process that handles the connection,
which can be used for debugging purposes.
Many thanks to [lilyball](https://git.lix.systems/lilyball) for this.
- Test group membership better on macOS [gh#5885](https://github.com/NixOS/nix/issues/5885) [cl/2566](https://gerrit.lix.systems/c/lix/+/2566)
`nix-daemon` will now test group membership better on macOS for `trusted-users` and `allowed-users`.
It not only fetches the peer gid (which fixes `@staff`) but it also asks opendirectory for group
membership checks instead of just using the group database, which means nested groups (like `@_developer`)
and groups with synthesized membership (like `@localaccounts`) will work.
Many thanks to [lilyball](https://git.lix.systems/lilyball) for this.
- `nix store delete` no longer builds paths [cl/2782](https://gerrit.lix.systems/c/lix/+/2782)
`nix store delete` no longer realises the installables
specified. Previously, `nix store delete nixpkgs#hello` would download
hello only to immediately delete it again. Now, it exits with an error
if given an installable that isn't in the store.
Many thanks to [lheckemann](https://git.lix.systems/lheckemann) for this.
- Fix nix-store --delete on paths with remaining referrers [cl/2783](https://gerrit.lix.systems/c/lix/+/2783)
Nix 2.5 introduced a regression whereby `nix-store --delete` and `nix
store delete` started to fail when trying to delete a path that was
still referenced by other paths, even if the referrers were not
reachable from any GC roots. The old behaviour, where attempting to
delete a store path would also delete its referrer closure, is now
restored.
Many thanks to [lheckemann](https://git.lix.systems/lheckemann) for this.
- Add a straightforward way to detect if in a Nix3 Shell [nix#6677](https://github.com/NixOS/nix/issues/6677) [nix#3862](https://github.com/NixOS/nix/issues/3862) [cl/2090](https://gerrit.lix.systems/c/lix/+/2090)
Running `nix shell` or `nix develop` will now set `IN_NIX_SHELL` to
either `pure` or `impure`, depending on whether `--ignore-environment`
is passed. `nix develop` will always be an impure environment.
Many thanks to [Ersei Saggi](https://github.com/9p4) for this.
- Fix experimental and deprecated features showing as integers in `nix config show --json` [fj#738](https://git.lix.systems/lix-project/lix/issues/738) [cl/2882](https://gerrit.lix.systems/c/lix/+/2882)
Internal changes in 2.92 caused `nix config show --json` to show deprecated and experimental features not as the list of named features 2.91 and earlier produced, but as integers. This has been fixed.
Many thanks to [eldritch horrors](https://git.lix.systems/pennae) for this.
- `builtins.fetchTree` is no longer visible in `builtins` when flakes are disabled [cl/2399](https://gerrit.lix.systems/c/lix/+/2399)
`builtins.fetchTree` is the foundation of flake inputs and flake lock files, but is not fully specified in behaviour, which leads to regressions, behaviour differences with CppNix, and other unfun times.
It's gated behind the `flakes` experimental feature, but prior to now, would throw an uncatchable error at runtime when used without the `flakes` feature enabled.
Now it's like other builtins which are experimental feature gated, where it is not visible without the relevant feature enabled.
This fixes a bug in using Eelco Dolstra's version of flake-compat on Lix (and a divergence with CppNix): https://github.com/edolstra/flake-compat/issues/66
Many thanks to [jade](https://git.lix.systems/jade) for this.
- fix usage of `builtins.filterSource` and `builtins.path` with the filter argument when using chroot stores [nix#11503](https://github.com/NixOS/nix/issues/11503)
The semantics of `builtins.filterSource` (and the `filter` argument for
`builtins.path`) have been adjusted regarding how paths inside the Nix store
are handled.
Previously, when evaluating whether a path should be included, the filtering
function received the **physical path** if the source was inside the chroot store.
Now, it receives the **logical path** instead.
This ensures consistency in path handling and avoids potential
misinterpretations of paths within the evaluator, which led to various fallouts
mentioned in <https://github.com/NixOS/nixpkgs/pull/369694>.
Many thanks to [lily](https://git.lix.systems/lilyinstarlight), [alois31](https://git.lix.systems/alois31), and [eldritch horrors](https://git.lix.systems/pennae) for this.
- Fix `--help` formatting [fj#622](https://git.lix.systems/lix-project/lix/issues/622) [cl/2776](https://gerrit.lix.systems/c/lix/+/2776)
The help printed when invoking `nix` or `nix-store` and subcommands with `--help` previously contained garbled terminal escapes. These have been removed.
Many thanks to [lheckemann](https://git.lix.systems/lheckemann) for this.
- Parsing failures in flake.lock no longer crash Lix [fj#559](https://git.lix.systems/lix-project/lix/issues/559) [cl/2401](https://gerrit.lix.systems/c/lix/+/2401)
Failure to parse `flake.lock` no longer hard-crashes Lix and instead produces a nice error message.
```
error:
… while updating the lock file of flake 'git+file:///Users/jade/lix/lix2'
… while parsing the lock file at /nix/store/mm5dqh8a729yazzj82cjffxl97n5c62s-source//flake.lock
error: [json.exception.parse_error.101] parse error at line 1, column 1: syntax error while parsing value - invalid literal;
last read: '#'
```
Many thanks to [gilice](https://git.lix.systems/gilice) for this.
- Flakes follow `--eval-system` where it makes sense [fj#673](https://git.lix.systems/lix-project/lix/issues/673) [fj#692](https://git.lix.systems/lix-project/lix/issues/692) [gh#11359](https://github.com/NixOS/nix/issues/11359) [cl/2657](https://gerrit.lix.systems/c/lix/+/2657)
Most flake commands now follow `--eval-system` when choosing attributes to build/evaluate/etc.
The exceptions are commands that actually run something on the local machine:
- nix develop
- nix run
- nix upgrade-nix
- nix fmt
- nix bundle
This is not a principled approach to cross compilation or anything, flakes still impede rather than support cross compilation, but this unbreaks many remote build use cases.
Many thanks to [jade](https://git.lix.systems/jade) for this.
- Remove some gremlins from path garbage collection [fj#621](https://git.lix.systems/lix-project/lix/issues/621) [fj#524](https://git.lix.systems/lix-project/lix/issues/524) [cl/2465](https://gerrit.lix.systems/c/lix/+/2465) [cl/2387](https://gerrit.lix.systems/c/lix/+/2387)
Path garbage collection had some known unsoundness issues where it would delete things improperly and cause desynchronization between the filesystem state and the database state.
Now Lix tolerates better if such a condition exists by not failing the entire GC if a path fails to delete.
We also fixed a bug in our file locking implementation that is one possible root cause, but may not be every root cause.
Many thanks to [eldritch horrors](https://git.lix.systems/pennae) and [Raito Bezarius](https://git.lix.systems/raito) for this.
- Show illegal path references in fixed-outputs derivations [fj#530](https://git.lix.systems/lix-project/lix/issues/530) [cl/2726](https://gerrit.lix.systems/c/lix/+/2726)
The error created when referencing a store path in a Fixed-Output Derivation is now more verbose, listing the offending paths.
This allows for better pinpointing where the issue might be.
An offender is the following derivation:
```nix
pkgs.stdenv.mkDerivation {
name = "illegal-fod";
dontUnpack = true;
dontBuild = true;
installPhase = ''
cp -R ${pkgs.hello} $out
'';
outputHashMode = "recursive";
outputHashAlgo = "sha256";
outputHash = pkgs.lib.fakeHash;
}
```
The previous error shown would have been:
```
error: illegal path references in fixed-output derivation '/nix/store/rpq4m1y79s2nhs1hj7k47yiyykxykiqa-illegal-fod.drv'
```
and is now:
```
error: the fixed-output derivation '/nix/store/rpq4m1y79s2nhs1hj7k47yiyykxykiqa-illegal-fod.drv' must not reference store paths but 2 such references were found:
/nix/store/1q8w6gl1ll0mwfkqc3c2yx005s6wwfrl-hello-2.12.1
/nix/store/wn7v2vhyyyi6clcyn0s9ixvl7d4d87ic-glibc-2.40-36
```
Many thanks to [Tom Hubrecht](https://git.lix.systems/tom-hubrecht) for this.
- Show error when item from NIX_PATH cannot be downloaded
For e.g. `nix-instantiate -I https://example.com/404`, you'd only get a warning if the download failed, such as
warning: Nix search path entry 'https://example.com/404' cannot be downloaded, ignoring
Now, the full error that caused the download failure is displayed with a note that the search
path entry is ignored, e.g.
warning:
… while downloading https://example.com/404 to satisfy NIX_PATH lookup, ignoring search path entry
warning: unable to download 'https://example.com/404': HTTP error 404 ()
response body: […]
Many thanks to [ma27](https://git.lix.systems/ma27) for this.
- Fix Lix crashing on invalid json [fj#642](https://git.lix.systems/lix-project/lix/issues/642) [fj#753](https://git.lix.systems/lix-project/lix/issues/753) [fj#759](https://git.lix.systems/lix-project/lix/issues/759) [fj#769](https://git.lix.systems/lix-project/lix/issues/769) [cl/2907](https://gerrit.lix.systems/c/lix/+/2907)
Lix no longer crashes when it receives invalid JSON. Instead it'll point to the syntax error and give some context about what happened, for example
```
nix derivation add <<<"""
error:
… while parsing a derivation from stdin
error: failed to parse JSON: [json.exception.parse_error.101] parse error at line 2, column 1: syntax error while parsing value - unexpected end of input; expected '[', '{', or a literal
```
Many thanks to [eldritch horrors](https://git.lix.systems/pennae) for this.
- Fix handling of `lastModified` in tarball inputs [cl/2792](https://gerrit.lix.systems/c/lix/+/2792)
Previous versions of Lix would fail with the following error, if a
[tarball flake input](@docroot@/protocols/tarball-fetcher.md) redirect
to a URL that contains a `lastModified` field:
```
error: input attribute 'lastModified' is not an integer
```
This is now fixed.
Many thanks to [xanderio](https://github.com/xanderio) and [Julian Stecklina](https://github.com/blitz) for this.
- Fix `--debugger --ignore-try` [cl/2440](https://gerrit.lix.systems/c/lix/+/2440)
When in debug mode (e.g. from using the `--debugger` flag), enabling [`ignore-try`](@docroot@/command-ref/conf-file.md#conf-ignore-try) once again properly disables debug REPLs within [`builtins.tryEval`](@docroot@/language/builtins.md#builtins-tryEval) calls. Previously, a debug REPL would be started as if `ignore-try` was disabled, but that REPL wouldn't actually be in debug mode, and upon exiting the REPL the evaluating process would segfault.
Many thanks to [Dusk Banks](https://git.lix.systems/bb010g) for this.
- Don't consider a path with a specified rev to be `locked` [cl/2064](https://gerrit.lix.systems/c/lix/+/2064)
Until now it was allowed to do e.g.
$ echo 'lalala' > testfile
$ nix eval --expr '(builtins.fetchTree { path = "/home/ma27/testfile"; rev = "0000000000000000000000000000000000000000"; type = "path"; })'
{ lastModified = 1723656303; lastModifiedDate = "20240814172503"; narHash = "sha256-hOMY06A0ohaaCLwnhpZIMoAqi/8kG2vk30NRiqi0dfc="; outPath = "/nix/store/lhfz259iipmv9ky995rml8018jvriynh-source"; rev = "0000000000000000000000000000000000000000"; shortRev = "0000000"; }
$ cat /nix/store/lhfz259iipmv9ky995rml8018jvriynh-source
lalala
because any kind of input with a `rev` specified is considered to be locked.
With this change, inputs of type `path`, `indirect` and `tarball` are no longer
considered locked with a rev, but no hash specified.
This behavior was changed in
[CppNix 2.21 as well](https://github.com/nixos/nix/commit/071dd2b3a4e6c0b2106f1b6f14ec26e153d97446) as well.
Many thanks to [ma27](https://git.lix.systems/ma27) for this.
- Fix macOS sandbox profile size errors [fj#752](https://git.lix.systems/lix-project/lix/issues/752) [fj#718](https://git.lix.systems/lix-project/lix/issues/718) [cl/2861](https://gerrit.lix.systems/c/lix/+/2861)
Fixed an issue on macOS where the sandbox profile could exceed size limits when building derivations with many dependencies. The profile is now split into multiple allowed sections to stay under the interpreter's limits.
This resolves errors like
```
error: (failed with exit code 1, previous messages: sandbox initialization failed: data object length 65730 exceeds maximum (65535)|failed to configure sandbox)
error: unexpected EOF reading a line
```
Many thanks to [Pierre-Etienne Meunier](https://github.com/P-E-Meunier) and [Poliorcetics](https://github.com/poliorcetics) for this.
- Fix interference of the multiline progress bar with output [cl/2774](https://gerrit.lix.systems/c/lix/+/2774)
In some situations, the progress indicator of the multiline progress bar would interfere with persistent output.
This would result in progress bar headers being visible in place of the desired text, for example the outputs shown after a `:b` command in the repl.
The underlying ordering issue has been fixed, so that the undesired interference does not happen any more.
Many thanks to [alois31](https://git.lix.systems/alois31) for this.
- Paralellise `nix store sign` using a thread pool [fj#399](https://git.lix.systems/lix-project/lix/issues/399) [cl/2606](https://gerrit.lix.systems/c/lix/+/2606)
`nix store sign` with a large collection of provided paths (such as when using with `--all`) has historically
signed these paths serially. Taking extreme amounts of time when preforming operations such as fixing binary
caches. This has been changed. Now these signatures are performed using a thread pool like `nix store copy-sigs`.
Many thanks to [Lunaphied](https://git.lix.systems/Lunaphied) for this.
- `post-build-hook` only receives settings that are set [fj#739](https://git.lix.systems/lix-project/lix/issues/739) [cl/2800](https://gerrit.lix.systems/c/lix/+/2800)
If one is using `post-build-hook` to upload paths to a cache, it used to be broken if CppNix was used inside the script, since CppNix would fail about unsupported configuration option values in some of Lix's defaults.
This is because `post-build-hook` receives the settings of the nix daemon in the `NIX_CONFIG` environment variable.
Now Lix only emits overridden settings to `post-build-hook` invocations, which fixes this issue in the majority of cases: where the configuration is not explicitly incompatible.
Many thanks to [jade](https://git.lix.systems/jade) for this.
- Remove lix-initiated ssh connection sharing [fj#304](https://git.lix.systems/lix-project/lix/issues/304) [fj#644](https://git.lix.systems/lix-project/lix/issues/644) [cl/3005](https://gerrit.lix.systems/c/lix/+/3005)
Lix no longer explicitly requests ssh connection sharing (ControlMaster/ControlPath SSH
options, see also ssh_config(5) man page) when connecting to remote stores. This may
impact command latency when `NIX_REMOTE` is set to a `ssh://` or `ssh-ng://` url, or if
`--store` is specified. Remote build connections did not use ssh connection sharing.
Connection sharing configuration is now inherited from user configuration at all times. It
is now advisable to configure connection sharing for remote builders for improved latency.
Many thanks to [eldritch horrors](https://git.lix.systems/pennae) for this.
## Development
- Add `nix_plugin_entry` entry point for plugins [fj#740](https://git.lix.systems/lix-project/lix/issues/740) [fj#359](https://git.lix.systems/lix-project/lix/issues/359) [gh#8699](https://github.com/NixOS/nix/pull/8699) [cl/2826](https://gerrit.lix.systems/c/lix/+/2826)
Plugins are an exceptionally rarely used feature in Lix, but they are important as a prototyping tool for code destined for Lix itself, and we want to keep supporting them as a low-maintenance-cost feature.
As part of the overall move towards getting rid of static initializers for stability and predictability reasons, we added an explicit `nix_plugin_entry` function like CppNix has, which is called immediately after plugin load, if present.
This makes control flow more explicit and allows for easily registering things that have had their static initializer registration classes removed.
Many thanks to [jade](https://git.lix.systems/jade) and [yorickvp](https://github.com/yorickvp) for this.
## Miscellany
- Set default of `connect-timeout` to `5` [cl/2799](https://gerrit.lix.systems/c/lix/+/2799)
By default, the connection timeout to substituters is now 5s instead of 300s.
That way, unavailable substituters are detected quicker.
Many thanks to [ma27](https://git.lix.systems/ma27) for this.
+5 -39
View File
@@ -1,21 +1,9 @@
#!/usr/bin/env python3
"""
Preprocesses mdbook markdown, primarily for include directives.
The include directive format is as follows:
{{#include foo/bar/baz.md}}
The content of includes will be indented as much as the directive itself.
Including a generated file (from building Lix; generally for the 'new' CLI):
{{#include @generated@/foo/bar/baz.md}}
"""
from pathlib import Path
import json
import os, os.path
import sys
import textwrap
name = 'substitute.py'
@@ -23,12 +11,6 @@ def log(*args, **kwargs):
kwargs['file'] = sys.stderr
return print(f'{name}:', *args, **kwargs)
def remove_prefix_if_present(s: str, prefix: str) -> str | None:
if s.startswith(prefix):
return s.removeprefix(prefix)
else:
return None
def do_include(content: str, relative_md_path: Path, source_root: Path, search_path: Path):
assert not relative_md_path.is_absolute(), f'{relative_md_path=} from mdbook should be relative'
@@ -38,32 +20,16 @@ def do_include(content: str, relative_md_path: Path, source_root: Path, search_p
lines = []
for l in content.splitlines(keepends=True):
if remain := remove_prefix_if_present(l.strip(), "{{#include"):
requested = remain[1:-2]
# We indent bodies of indent directives by the indent of the
# directive itself.
num_leading_indent = len(l) - len(l.lstrip())
if subpath := remove_prefix_if_present(requested, "@generated@/"):
included = search_path / Path(subpath)
if l.strip().startswith("{{#include "):
requested = l.strip()[11:][:-2]
if requested.startswith("@generated@/"):
included = search_path / Path(requested[12:])
requested = included.relative_to(search_path)
else:
included = source_root / relative_md_path.parent / requested
requested = included.resolve().relative_to(source_root)
assert included.exists(), f"{requested} not found at {included}"
lines.append(
textwrap.indent(
do_include(
included.read_text(),
requested,
source_root,
search_path,
),
" " * num_leading_indent
)
+ "\n"
)
lines.append(do_include(included.read_text(), requested, source_root, search_path) + "\n")
else:
lines.append(l)
return "".join(lines)
+9 -8
View File
@@ -157,7 +157,7 @@ let
nixConfContents =
(lib.concatStringsSep "\n" (
lib.mapAttrsToList (
lib.mapAttrsFlatten (
n: v:
let
vStr = if builtins.isList v then lib.concatStringsSep " " v else v;
@@ -193,11 +193,13 @@ let
in
''
{
${lib.concatStringsSep "\n" (
builtins.map (output: ''
${output} = { outPath = "${lib.getOutput output drv}"; };
'') outputs
)}
${
lib.concatStringsSep "\n" (
builtins.map (output: ''
${output} = { outPath = "${lib.getOutput output drv}"; };
'') outputs
)
}
outputs = [ ${lib.concatStringsSep " " (builtins.map (x: "\"${x}\"") outputs)} ];
name = "${drv.name}";
outPath = "${drv}";
@@ -359,8 +361,7 @@ let
"org.opencontainers.image.source" = "https://git.lix.systems/lix-project/lix";
"org.opencontainers.image.vendor" = "Lix project";
"org.opencontainers.image.version" = pkgs.nix.version;
"org.opencontainers.image.description" =
"Minimal Lix container image, with some batteries included.";
"org.opencontainers.image.description" = "Minimal Lix container image, with some batteries included.";
} // lib.optionalAttrs (lixRevision != null) { "org.opencontainers.image.revision" = lixRevision; };
};
Generated
+5 -64
View File
@@ -16,22 +16,6 @@
"type": "github"
}
},
"lowdown-src": {
"flake": false,
"locked": {
"lastModified": 1633514407,
"narHash": "sha256-Dw32tiMjdK9t3ETl5fzGrutQTzh2rufgZV4A/BbxuD4=",
"owner": "kristapsdz",
"repo": "lowdown",
"rev": "d2c2b44ff6c27b936ec27358a2653caaef8f73b8",
"type": "github"
},
"original": {
"owner": "kristapsdz",
"repo": "lowdown",
"type": "github"
}
},
"nix2container": {
"flake": false,
"locked": {
@@ -48,44 +32,18 @@
"type": "github"
}
},
"nix_2_18": {
"inputs": {
"flake-compat": [
"flake-compat"
],
"lowdown-src": "lowdown-src",
"nixpkgs": "nixpkgs",
"nixpkgs-regression": [
"nixpkgs-regression"
]
},
"locked": {
"lastModified": 1730375271,
"narHash": "sha256-RrOFlDGmRXcVRV2p2HqHGqvzGNyWoD0Dado/BNlJ1SI=",
"owner": "NixOS",
"repo": "nix",
"rev": "0f665ff6779454f2117dcc32e44380cda7f45523",
"type": "github"
},
"original": {
"owner": "NixOS",
"ref": "2.18.9",
"repo": "nix",
"type": "github"
}
},
"nixpkgs": {
"locked": {
"lastModified": 1705033721,
"narHash": "sha256-K5eJHmL1/kev6WuqyqqbS1cdNnSidIZ3jeqJ7GbrYnQ=",
"lastModified": 1733348545,
"narHash": "sha256-b4JrUmqT0vFNx42aEN9LTWOHomkTKL/ayLopflVf81U=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "a1982c92d8980a0114372973cbdfe0a307f1bdea",
"rev": "9ecb50d2fae8680be74c08bb0a995c5383747f89",
"type": "github"
},
"original": {
"owner": "NixOS",
"ref": "nixos-23.05-small",
"ref": "nixos-24.11-small",
"repo": "nixpkgs",
"type": "github"
}
@@ -106,22 +64,6 @@
"type": "github"
}
},
"nixpkgs_2": {
"locked": {
"lastModified": 1749522908,
"narHash": "sha256-eWANkhWXFL1MmaxzsZ9bhLCNT8OVs7CC+OXaSDGlA8A=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "e5cb99555c45a13dcc5f1317462238530b0066b7",
"type": "github"
},
"original": {
"owner": "NixOS",
"ref": "nixos-25.05-small",
"repo": "nixpkgs",
"type": "github"
}
},
"pre-commit-hooks": {
"flake": false,
"locked": {
@@ -142,8 +84,7 @@
"inputs": {
"flake-compat": "flake-compat",
"nix2container": "nix2container",
"nix_2_18": "nix_2_18",
"nixpkgs": "nixpkgs_2",
"nixpkgs": "nixpkgs",
"nixpkgs-regression": "nixpkgs-regression",
"pre-commit-hooks": "pre-commit-hooks"
}
+80 -133
View File
@@ -2,19 +2,8 @@
description = "Lix: A modern, delicious implementation of the Nix package manager";
inputs = {
nixpkgs.url = "github:NixOS/nixpkgs/nixos-25.05-small";
nixpkgs.url = "github:NixOS/nixpkgs/nixos-24.11-small";
nixpkgs-regression.url = "github:NixOS/nixpkgs/215d4d0fd80ca5163643b03a33fde804a29cc1e2";
# Required because Nix 2.18 is not in Nixpkgs ≥ 25.05 anymore.
nix_2_18 = {
url = "github:NixOS/nix/2.18.9";
# NOTE(Raito): this is not possible because patches on libseccomp does not apply anymore on this Nix.
# Let's keep the latest known nixpkgs useable with Nix 2.18 for our tests.
# inputs.nixpkgs.follows = "nixpkgs";
inputs.nixpkgs-regression.follows = "nixpkgs-regression";
inputs.flake-compat.follows = "flake-compat";
};
pre-commit-hooks = {
url = "github:cachix/git-hooks.nix";
flake = false;
@@ -36,7 +25,6 @@
nixpkgs-regression,
pre-commit-hooks,
nix2container,
nix_2_18,
flake-compat,
}:
@@ -48,7 +36,6 @@
sgr = builtins.fromJSON ''"\u001b["'';
freezePage = "https://wiki.lix.systems/books/lix-contributors/page/freezes-and-recommended-contributions";
codebaseOverview = "https://wiki.lix.systems/books/lix-contributors/page/codebase-overview";
gerritWiki = "https://wiki.lix.systems/books/lix-contributors/page/gerrit";
contribNotice = builtins.toFile "lix-contrib-notice" ''
Hey there!
@@ -66,10 +53,6 @@
and we'd like to work together with all contributors as much as possible.
Lix is a collaborative project :)
If you want to submit a patch and you never used gerrit before, please
check our gerrit wiki section:
${sgr}32m${gerritWiki}${sgr}0m
You can open an issue at https://git.lix.systems/lix-project/lix/issues
or chat with us on Matrix: #space:lix.systems.
@@ -109,7 +92,8 @@
"armv7l-linux"
"riscv64-linux"
"aarch64-linux"
"x86_64-freebsd"
# FIXME: still broken in 24.05: fails to build rustc(??) due to missing -lstdc++ dep
# "x86_64-freebsd"
# FIXME: broken dev shell due to python
# "x86_64-netbsd"
];
@@ -175,11 +159,6 @@
{
nixStable = prev.nix;
# Nix 2.18 has been removed from Nixpkgs ≥ 25.05, so we need to reintroduce it ourselves for our tests.
nixVersions = prev.nixVersions // {
nix_2_18 = nix_2_18.outputs.packages.${currentStdenv.hostPlatform.system}.default;
};
# Forward from the previous stage as we dont want it to pick the lowdown override
nixUnstable = prev.nixUnstable;
@@ -218,21 +197,6 @@
lix-clang-tidy = final.callPackage ./subprojects/lix-clang-tidy { };
nix-eval-jobs = final.callPackage ./subprojects/nix-eval-jobs {
srcDir = ./subprojects/nix-eval-jobs;
};
# HACK: We need nix-prefetch-git for fetchCargoVendor for Rust stuff,
# so it can't use Lix, or we infrec:
# lix -> Rust stuff -> fetchCargoVendor -> nix-prefetch-git -> nix (lix)
# This will eventually become a problem upstream, but until then,
# apply some duct tape and pray.
nix-prefetch-git =
if (lib.functionArgs prev.nix-prefetch-git.override) ? "nix" then
prev.nix-prefetch-git.override { nix = prev.nix; }
else
prev.nix-prefetch-git;
# Export the patched version of boehmgc that Lix uses into the overlay
# for consumers of this flake.
boehmgc-nix = final.nix.passthru.boehmgc-nix;
@@ -282,9 +246,6 @@
# Perl bindings for various platforms.
perlBindings = forAllSystems (system: nixpkgsFor.${system}.native.nix.passthru.perl-bindings);
# nix-eval-jobs can be built against this Lix.
nix-eval-jobs = forAllSystems (system: nixpkgsFor.${system}.native.nix-eval-jobs);
# Binary tarball for various platforms, containing a Nix store
# with the closure of 'nix' package.
binaryTarball = forAllSystems (system: nixpkgsFor.${system}.native.nix.passthru.binaryTarball);
@@ -316,100 +277,87 @@
});
# System tests.
tests =
import ./tests/nixos {
inherit
self
lib
nixpkgs
nixpkgsFor
;
}
// {
nix-eval-jobs = forAllSystems (system: self.packages.${system}.nix-eval-jobs.tests.nix-eval-jobs);
tests = import ./tests/nixos { inherit lib nixpkgs nixpkgsFor; } // {
# This is x86_64-linux only, just because we have significantly
# cheaper x86_64-linux compute in CI.
# It is clangStdenv because clang's sanitizers are nicer.
asanBuild = self.packages.x86_64-linux.nix-clangStdenv.override {
# Improve caching of non-code changes by not changing the
# derivation name every single time, since this will never be seen
# by users anyway.
versionSuffix = "";
sanitize = [
"address"
"undefined"
];
# it is very hard to make *every* CI build use this option such
# that we don't wind up building Lix twice, so we do it here where
# we are already doing so.
werror = true;
};
# This is x86_64-linux only, just because we have significantly
# cheaper x86_64-linux compute in CI.
# It is clangStdenv because clang's sanitizers are nicer.
asanBuild = self.packages.x86_64-linux.nix-clangStdenv.override {
# Improve caching of non-code changes by not changing the
# derivation name every single time, since this will never be seen
# by users anyway.
# Although this might be nicer to do with pre-commit, that would
# require adding 12MB of nodejs to the dev shell, whereas building it
# in CI with Nix avoids that at a cost of slower feedback on rarely
# touched files.
jsSyntaxCheck =
let
nixpkgs = nixpkgsFor.x86_64-linux.native;
inherit (nixpkgs) pkgs;
docSources = lib.fileset.toSource {
root = ./doc;
fileset = lib.fileset.fileFilter (f: f.hasExt "js") ./doc;
};
in
pkgs.runCommand "js-syntax-check" { } ''
find ${docSources} -type f -print -exec ${pkgs.nodejs-slim}/bin/node --check '{}' ';'
touch $out
'';
# clang-tidy run against the Lix codebase using the Lix clang-tidy plugin
clang-tidy =
let
nixpkgs = nixpkgsFor.x86_64-linux.native;
inherit (nixpkgs) pkgs;
in
pkgs.callPackage ./package.nix {
# Required since we don't support gcc stdenv
stdenv = pkgs.clangStdenv;
versionSuffix = "";
sanitize = [
"address"
"undefined"
];
# it is very hard to make *every* CI build use this option such
# that we don't wind up building Lix twice, so we do it here where
# we are already doing so.
werror = true;
lintInsteadOfBuild = true;
};
# Although this might be nicer to do with pre-commit, that would
# require adding 12MB of nodejs to the dev shell, whereas building it
# in CI with Nix avoids that at a cost of slower feedback on rarely
# touched files.
jsSyntaxCheck =
let
nixpkgs = nixpkgsFor.x86_64-linux.native;
inherit (nixpkgs) pkgs;
docSources = lib.fileset.toSource {
root = ./doc;
fileset = lib.fileset.fileFilter (f: f.hasExt "js") ./doc;
};
in
pkgs.runCommand "js-syntax-check" { } ''
find ${docSources} -type f -print -exec ${pkgs.nodejs-slim}/bin/node --check '{}' ';'
touch $out
'';
# Make sure that nix-env still produces the exact same result
# on a particular version of Nixpkgs.
evalNixpkgs =
with nixpkgsFor.x86_64-linux.native;
runCommand "eval-nixos" { buildInputs = [ nix ]; } ''
type -p nix-env
# Note: we're filtering out nixos-install-tools because https://github.com/NixOS/nixpkgs/pull/153594#issuecomment-1020530593.
time nix-env --store dummy:// -f ${nixpkgs-regression} -qaP --drv-path | sort | grep -v nixos-install-tools > packages
[[ $(sha1sum < packages | cut -c1-40) = 402242fca90874112b34718b8199d844e8b03d12 ]]
mkdir $out
'';
# clang-tidy run against the Lix codebase using the Lix clang-tidy plugin
clang-tidy =
let
nixpkgs = nixpkgsFor.x86_64-linux.native;
inherit (nixpkgs) pkgs;
in
pkgs.callPackage ./package.nix {
# Required since we don't support gcc stdenv
stdenv = pkgs.clangStdenv;
versionSuffix = "";
lintInsteadOfBuild = true;
};
# Make sure that nix-env still produces the exact same result
# on a particular version of Nixpkgs.
evalNixpkgs =
with nixpkgsFor.x86_64-linux.native;
runCommand "eval-nixos" { buildInputs = [ nix ]; } ''
type -p nix-env
# Note: we're filtering out nixos-install-tools because https://github.com/NixOS/nixpkgs/pull/153594#issuecomment-1020530593.
time nix-env --store dummy:// -f ${nixpkgs-regression} -qaP --drv-path | sort | grep -v nixos-install-tools > packages
[[ $(sha1sum < packages | cut -c1-40) = 402242fca90874112b34718b8199d844e8b03d12 ]]
mkdir $out
'';
nixpkgsLibTests = forAllSystems (
system:
let
inherit (self.packages.${system}) nix;
pkgs = nixpkgsFor.${system}.native;
testWithNix = import (nixpkgs + "/lib/tests/test-with-nix.nix") { inherit pkgs lib nix; };
in
pkgs.symlinkJoin {
name = "nixpkgs-lib-tests";
paths =
[ testWithNix ]
# NOTE: nixpkgs 25.05 is being ... *creative*, and requires this dance to override
# the evaluator used for the test. it will break again in the future, don't worry.
++ lib.optionals pkgs.stdenv.isLinux [
((pkgs.callPackage "${nixpkgs}/ci/eval" { nixVersions.latest = nix; }).attrpathsSuperset {
evalSystem = system;
})
];
}
);
};
nixpkgsLibTests = forAllSystems (
system:
let
inherit (self.packages.${system}) nix;
pkgs = nixpkgsFor.${system}.native;
testWithNix = import (nixpkgs + "/lib/tests/test-with-nix.nix") { inherit pkgs lib nix; };
in
pkgs.symlinkJoin {
name = "nixpkgs-lib-tests";
paths =
[ testWithNix ]
# NOTE: nixpkgs 24.11 is being ... *creative*, and requires this dance to override
# the evaluator used for the test. it will break again in the future, don't worry.
++ lib.optionals pkgs.stdenv.isLinux [
(pkgs.callPackage "${nixpkgs}/ci/eval" { nixVersions.nix_2_24 = nix; }).attrpathsSuperset
];
}
);
};
pre-commit = forAvailableSystems (
system:
@@ -457,7 +405,6 @@
binaryTarball = self.hydraJobs.binaryTarball.${system};
perlBindings = self.hydraJobs.perlBindings.${system};
nix-eval-jobs = self.hydraJobs.nix-eval-jobs.${system};
nixpkgsLibTests = self.hydraJobs.tests.nixpkgsLibTests.${system};
rl-next = self.hydraJobs.rl-next.${system}.user;
# Will be empty attr set on i686-linux, and filtered out by forAvailableSystems.
@@ -475,7 +422,7 @@
inherit (nixpkgsFor.${system}.native) nix;
default = nix;
inherit (nixpkgsFor.${system}.native) lix-clang-tidy nix-eval-jobs;
inherit (nixpkgsFor.${system}.native) lix-clang-tidy;
}
// (
lib.optionalAttrs (builtins.elem system linux64BitSystems) {
+7 -24
View File
@@ -8,47 +8,30 @@ list:
clean:
rm -rf build
# Prepare meson for building with extra options
setup-custom *OPTIONS:
# Prepare meson for building
setup *OPTIONS:
meson setup build --prefix="$PWD/outputs/out" $mesonFlags {{ OPTIONS }}
# Prepare meson for building
setup: (setup-custom)
# Build lix with extra options
build-custom *OPTIONS:
meson compile -C build {{ OPTIONS }}
# Build lix
build: (build-custom)
build *OPTIONS:
meson compile -C build {{ OPTIONS }}
alias compile := build
# Install lix for local development with extra options
install-custom *OPTIONS: (build-custom OPTIONS)
# Install lix for local development
install *OPTIONS: (build OPTIONS)
meson install -C build
# Install lix for local development
install: (install-custom)
# Run tests (usually requires `install`) with extra options
# Run tests
test *OPTIONS:
meson test -C build --print-errorlogs {{ OPTIONS }}
# Run unit tests only
test-unit *OPTIONS: (test "--suite" "check")
# Run integration tests only
test-integration *OPTIONS: install (test "--suite" "installcheck")
alias clang-tidy := lint
# Lint with `clang-tidy`
lint:
ninja -C build clang-tidy
alias clang-tidy-fix := lint-fix
# Fix lints with `clang-tidy-fix`
lint-fix:
ninja -C build clang-tidy-fix
+21 -32
View File
@@ -53,7 +53,7 @@ static bool allSupportedLocally(Store & store, const std::set<std::string>& requ
return true;
}
static int main_build_remote(AsyncIoRoot & aio, std::string programName, Strings argv)
static int main_build_remote(std::string programName, Strings argv)
{
{
logger = makeJSONLogger(*logger);
@@ -84,12 +84,12 @@ static int main_build_remote(AsyncIoRoot & aio, std::string programName, Strings
initPlugins();
auto store = aio.blockOn(openStore());
auto store = openStore();
/* It would be more appropriate to use $XDG_RUNTIME_DIR, since
that gets cleared on reboot, but it wouldn't work on macOS. */
auto currentLoadName = "/current-load";
if (auto localStore = store.try_cast_shared<LocalFSStore>())
if (auto localStore = store.dynamic_pointer_cast<LocalFSStore>())
currentLoad = std::string { localStore->config().stateDir } + currentLoadName;
else
currentLoad = settings.nixStateDir + currentLoadName;
@@ -243,11 +243,11 @@ static int main_build_remote(AsyncIoRoot & aio, std::string programName, Strings
Activity act(*logger, lvlTalkative, actUnknown, fmt("connecting to '%s'", bestMachine->storeUri));
sshStore = aio.blockOn(bestMachine->openStore());
aio.blockOn(sshStore->connect());
sshStore = bestMachine->openStore();
sshStore->connect();
storeUri = bestMachine->storeUri;
} catch (std::exception & e) { // NOLINT(lix-foreign-exceptions)
} catch (std::exception & e) {
auto msg = chomp(drainFD(5, false));
printError("cannot build on '%s': %s%s",
bestMachine->storeUri, e.what(),
@@ -285,19 +285,12 @@ connected:
{
Activity act(*logger, lvlTalkative, actUnknown, fmt("copying dependencies to '%s'", storeUri));
aio.blockOn(copyPaths(
*store,
*sshStore,
store->parseStorePathSet(inputs),
NoRepair,
NoCheckSigs,
substitute
));
copyPaths(*store, *sshStore, store->parseStorePathSet(inputs), NoRepair, NoCheckSigs, substitute);
}
uploadLock.reset();
auto drv = aio.blockOn(store->readDerivation(*drvPath));
auto drv = store->readDerivation(*drvPath);
std::optional<BuildResult> optResult;
@@ -305,7 +298,7 @@ connected:
// stores), we assume we are. This is necessary for backwards
// compat.
bool trustedOrLegacy = ({
std::optional trusted = aio.blockOn(sshStore->isTrustedClient());
std::optional trusted = sshStore->isTrustedClient();
!trusted || *trusted;
});
@@ -326,34 +319,32 @@ connected:
// output ids, which break CA derivations
if (!drv.inputDrvs.map.empty())
drv.inputSrcs = store->parseStorePathSet(inputs);
optResult = aio.blockOn(sshStore->buildDerivation(*drvPath, (const BasicDerivation &) drv));
optResult = sshStore->buildDerivation(*drvPath, (const BasicDerivation &) drv);
auto & result = *optResult;
if (!result.success())
throw Error("build of '%s' on '%s' failed: %s", store->printStorePath(*drvPath), storeUri, result.errorMsg);
} else {
aio.blockOn(copyClosure(
*store, *sshStore, StorePathSet{*drvPath}, NoRepair, NoCheckSigs, substitute
));
auto res = aio.blockOn(sshStore->buildPathsWithResults({
copyClosure(*store, *sshStore, StorePathSet {*drvPath}, NoRepair, NoCheckSigs, substitute);
auto res = sshStore->buildPathsWithResults({
DerivedPath::Built {
.drvPath = makeConstantStorePathRef(*drvPath),
.outputs = OutputsSpec::All {},
}
}));
});
// One path to build should produce exactly one build result
assert(res.size() == 1);
optResult = std::move(res[0]);
}
auto outputHashes = aio.blockOn(staticOutputHashes(*store, drv));
auto outputHashes = staticOutputHashes(*store, drv);
std::set<Realisation> missingRealisations;
StorePathSet missingPaths;
if (experimentalFeatureSettings.isEnabled(Xp::CaDerivations) && !drv.type().hasKnownOutputPaths()) {
for (auto & outputName : wantedOutputs) {
auto thisOutputHash = outputHashes.at(outputName);
auto thisOutputId = DrvOutput{ thisOutputHash, outputName };
if (!aio.blockOn(store->queryRealisation(thisOutputId))) {
if (!store->queryRealisation(thisOutputId)) {
debug("missing output %s", outputName);
assert(optResult);
auto & result = *optResult;
@@ -368,34 +359,32 @@ connected:
auto outputPaths = drv.outputsAndOptPaths(*store);
for (auto & [outputName, hopefullyOutputPath] : outputPaths) {
assert(hopefullyOutputPath.second);
if (!aio.blockOn(store->isValidPath(*hopefullyOutputPath.second)))
if (!store->isValidPath(*hopefullyOutputPath.second))
missingPaths.insert(*hopefullyOutputPath.second);
}
}
if (!missingPaths.empty()) {
Activity act(*logger, lvlTalkative, actUnknown, fmt("copying outputs from '%s'", storeUri));
if (auto localStore = store.try_cast_shared<LocalStore>())
if (auto localStore = store.dynamic_pointer_cast<LocalStore>())
for (auto & path : missingPaths)
localStore->locksHeld.insert(store->printStorePath(path)); /* FIXME: ugly */
aio.blockOn(
copyPaths(*sshStore, *store, missingPaths, NoRepair, NoCheckSigs, NoSubstitute)
);
copyPaths(*sshStore, *store, missingPaths, NoRepair, NoCheckSigs, NoSubstitute);
}
// XXX: Should be done as part of `copyPaths`
for (auto & realisation : missingRealisations) {
// Should hold, because if the feature isn't enabled the set
// of missing realisations should be empty
experimentalFeatureSettings.require(Xp::CaDerivations);
aio.blockOn(store->registerDrvOutput(realisation));
store->registerDrvOutput(realisation);
}
return 0;
}
}
void registerLegacyBuildRemote() {
LegacyCommandRegistry::add("build-remote", main_build_remote);
void registerBuildRemote() {
LegacyCommands::add("build-remote", main_build_remote);
}
}
+1 -1
View File
@@ -3,6 +3,6 @@
namespace nix {
void registerLegacyBuildRemote();
void registerBuildRemote();
}
+3 -8
View File
@@ -1,7 +1,5 @@
#include "dotgraph.hh"
#include "lix/libstore/store-api.hh"
#include "lix/libutil/async.hh"
#include "lix/libutil/result.hh"
#include <iostream>
@@ -43,8 +41,8 @@ static std::string makeNode(std::string_view id, std::string_view label,
}
kj::Promise<Result<void>> printDotGraph(ref<Store> store, StorePathSet && roots)
try {
void printDotGraph(ref<Store> store, StorePathSet && roots)
{
StorePathSet workList(std::move(roots));
StorePathSet doneSet;
@@ -57,7 +55,7 @@ try {
cout << makeNode(std::string(path.to_string()), path.name(), "#ff0000");
for (auto & p : TRY_AWAIT(store->queryPathInfo(path))->references) {
for (auto & p : store->queryPathInfo(path)->references) {
if (p != path) {
workList.insert(p);
cout << makeEdge(std::string(p.to_string()), std::string(path.to_string()));
@@ -66,9 +64,6 @@ try {
}
cout << "}\n";
co_return result::success();
} catch (...) {
co_return result::current_exception();
}
+1 -1
View File
@@ -5,6 +5,6 @@
namespace nix {
kj::Promise<Result<void>> printDotGraph(ref<Store> store, StorePathSet && roots);
void printDotGraph(ref<Store> store, StorePathSet && roots);
}
+3 -8
View File
@@ -1,8 +1,6 @@
#include "graphml.hh"
#include "lix/libstore/store-api.hh"
#include "lix/libstore/derivations.hh"
#include "lix/libutil/async.hh"
#include "lix/libutil/result.hh"
#include <iostream>
@@ -48,8 +46,8 @@ static std::string makeNode(const ValidPathInfo & info)
}
kj::Promise<Result<void>> printGraphML(ref<Store> store, StorePathSet && roots)
try {
void printGraphML(ref<Store> store, StorePathSet && roots)
{
StorePathSet workList(std::move(roots));
StorePathSet doneSet;
std::pair<StorePathSet::iterator, bool> ret;
@@ -69,7 +67,7 @@ try {
ret = doneSet.insert(path);
if (ret.second == false) continue;
auto info = TRY_AWAIT(store->queryPathInfo(path));
auto info = store->queryPathInfo(path);
cout << makeNode(*info);
for (auto & p : info->references) {
@@ -83,9 +81,6 @@ try {
cout << "</graph>\n";
cout << "</graphml>\n";
co_return result::success();
} catch (...) {
co_return result::current_exception();
}
+1 -1
View File
@@ -5,6 +5,6 @@
namespace nix {
kj::Promise<Result<void>> printGraphML(ref<Store> store, StorePathSet && roots);
void printGraphML(ref<Store> store, StorePathSet && roots);
}
+43 -47
View File
@@ -1,10 +1,14 @@
#include <cstring>
#include <fstream>
#include <iostream>
#include <filesystem>
#include <regex>
#include <sstream>
#include <vector>
#include <map>
#include <nlohmann/json.hpp>
#include "lix/libstore/parsed-derivations.hh"
#include "lix/libstore/store-api.hh"
#include "lix/libstore/local-fs-store.hh"
@@ -14,11 +18,11 @@
#include "lix/libmain/shared.hh"
#include "lix/libstore/path-with-outputs.hh"
#include "lix/libexpr/eval.hh"
#include "lix/libexpr/eval-inline.hh"
#include "lix/libexpr/get-drvs.hh"
#include "lix/libcmd/common-eval-args.hh"
#include "lix/libexpr/attr-path.hh"
#include "lix/libcmd/legacy.hh"
#include "lix/libutil/regex.hh"
#include "lix/libutil/shlex.hh"
#include "nix-build.hh"
#include "lix/libstore/temporary-dir.hh"
@@ -29,10 +33,10 @@ namespace nix {
using namespace std::string_literals;
static void main_nix_build(AsyncIoRoot & aio, std::string programName, Strings argv)
static void main_nix_build(std::string programName, Strings argv)
{
auto dryRun = false;
auto runEnv = std::regex_search(programName, regex::parse("nix-shell$"));
auto runEnv = std::regex_search(programName, std::regex("nix-shell$"));
auto pure = false;
auto fromArgs = false;
auto packages = false;
@@ -69,7 +73,7 @@ static void main_nix_build(AsyncIoRoot & aio, std::string programName, Strings a
script = argv.front();
try {
auto lines = tokenizeString<Strings>(readFile(script), "\n");
if (std::regex_search(lines.front(), regex::parse("^#!"))) {
if (std::regex_search(lines.front(), std::regex("^#!"))) {
lines.pop_front();
inShebang = true;
savedArgs = {std::next(argv.begin()), argv.end()};
@@ -77,7 +81,7 @@ static void main_nix_build(AsyncIoRoot & aio, std::string programName, Strings a
for (auto line : lines) {
line = chomp(line);
std::smatch match;
if (std::regex_match(line, match, regex::parse("^#!\\s*nix-shell\\s+(.*)$")))
if (std::regex_match(line, match, std::regex("^#!\\s*nix-shell\\s+(.*)$")))
for (const auto & word : shell_split(match[1].str()))
argv.push_back(word);
}
@@ -90,7 +94,7 @@ static void main_nix_build(AsyncIoRoot & aio, std::string programName, Strings a
using LegacyArgs::LegacyArgs;
};
MyArgs myArgs(aio, myName, [&](Strings::iterator & arg, const Strings::iterator & end) {
MyArgs myArgs(myName, [&](Strings::iterator & arg, const Strings::iterator & end) {
if (*arg == "--help") {
showManPage(myName);
}
@@ -149,14 +153,14 @@ static void main_nix_build(AsyncIoRoot & aio, std::string programName, Strings a
// executes it unless it contains the string "perl" or "indir",
// or (undocumented) argv[0] does not contain "perl". Exploit
// the latter by doing "exec -a".
if (std::regex_search(interpreter, regex::parse("perl")))
if (std::regex_search(interpreter, std::regex("perl")))
execArgs = "-a PERL";
std::ostringstream joined;
for (const auto & i : savedArgs)
joined << shellEscape(i) << ' ';
if (std::regex_search(interpreter, regex::parse("ruby"))) {
if (std::regex_search(interpreter, std::regex("ruby"))) {
// Hack for Ruby. Ruby also examines the shebang. It tries to
// read the shebang to understand which packages to read from. Since
// this is handled via nix-shell -p, we wrap our ruby script execution
@@ -191,12 +195,12 @@ static void main_nix_build(AsyncIoRoot & aio, std::string programName, Strings a
if (outLink.empty())
outLink = (Path) tmpDir + "/result";
auto store = aio.blockOn(openStore());
auto evalStore = myArgs.evalStoreUrl ? aio.blockOn(openStore(*myArgs.evalStoreUrl)) : store;
auto store = openStore();
auto evalStore = myArgs.evalStoreUrl ? openStore(*myArgs.evalStoreUrl) : store;
auto evaluator = std::make_unique<Evaluator>(aio, myArgs.searchPath, evalStore, store);
auto evaluator = std::make_unique<Evaluator>(myArgs.searchPath, evalStore, store);
evaluator->repair = myArgs.repair;
auto state = evaluator->begin(aio);
auto state = evaluator->begin();
if (myArgs.repair) buildMode = bmRepair;
auto autoArgs = myArgs.getAutoArgs(*evaluator);
@@ -245,15 +249,15 @@ static void main_nix_build(AsyncIoRoot & aio, std::string programName, Strings a
} catch (Error & e) {};
auto [path, outputNames] = parsePathWithOutputs(absolute);
if (evalStore->isStorePath(path) && path.ends_with(".drv"))
drvs.push_back(aio.blockOn(DrvInfo::create(evalStore, absolute)));
drvs.push_back(DrvInfo(evalStore, absolute));
else
/* If we're in a #! script, interpret filenames
relative to the script. */
exprs.push_back(evaluator->parseExprFromFile(
evaluator->paths.resolveExprPath(aio.blockOn(lookupFileArg(
evaluator->paths.resolveExprPath(lookupFileArg(
*evaluator,
inShebang && !packages ? absPath(i, absPath(dirOf(script))) : i
)).unwrap())
))
));
}
}
@@ -271,13 +275,11 @@ static void main_nix_build(AsyncIoRoot & aio, std::string programName, Strings a
return false;
}
bool add = false;
if (v.type() == nFunction) {
if (auto pattern = dynamic_cast<AttrsPattern *>(v.lambda.fun->pattern.get())) {
for (auto & i : pattern->formals) {
if (evaluator->symbols[i.name] == "inNixShell") {
add = true;
break;
}
if (v.type() == nFunction && v.lambda.fun->hasFormals()) {
for (auto & i : v.lambda.fun->formals->formals) {
if (evaluator->symbols[i.name] == "inNixShell") {
add = true;
break;
}
}
}
@@ -291,7 +293,7 @@ static void main_nix_build(AsyncIoRoot & aio, std::string programName, Strings a
takesNixShellAttr(vRoot) ? *autoArgsWithInNixShell : *autoArgs,
vRoot
).first);
state->forceValue(v, noPos);
state->forceValue(v, v.determinePos(noPos));
getDerivations(
*state,
v,
@@ -310,17 +312,14 @@ static void main_nix_build(AsyncIoRoot & aio, std::string programName, Strings a
fetch binary cache data. */
uint64_t downloadSize, narSize;
StorePathSet willBuild, willSubstitute, unknown;
aio.blockOn(store->queryMissing(paths,
willBuild, willSubstitute, unknown, downloadSize, narSize));
store->queryMissing(paths,
willBuild, willSubstitute, unknown, downloadSize, narSize);
if (settings.printMissing) {
aio.blockOn(printMissing(
ref<Store>(store), willBuild, willSubstitute, unknown, downloadSize, narSize
));
}
if (settings.printMissing)
printMissing(ref<Store>(store), willBuild, willSubstitute, unknown, downloadSize, narSize);
if (!dryRun)
aio.blockOn(store->buildPaths(paths, buildMode, evalStore));
store->buildPaths(paths, buildMode, evalStore);
};
if (runEnv) {
@@ -328,7 +327,7 @@ static void main_nix_build(AsyncIoRoot & aio, std::string programName, Strings a
throw UsageError("nix-shell requires a single derivation");
auto & drvInfo = drvs.front();
auto drv = aio.blockOn(evalStore->derivationFromPath(drvInfo.requireDrvPath(*state)));
auto drv = evalStore->derivationFromPath(drvInfo.requireDrvPath(*state));
std::vector<DerivedPath> pathsToBuild;
RealisedPath::Set pathsToCopy;
@@ -389,7 +388,7 @@ static void main_nix_build(AsyncIoRoot & aio, std::string programName, Strings a
const auto & inputDrv = inputDrv0;
if (std::all_of(envExclude.cbegin(), envExclude.cend(),
[&](const std::string & exclude) {
return !std::regex_search(store->printStorePath(inputDrv), regex::parse(exclude));
return !std::regex_search(store->printStorePath(inputDrv), std::regex(exclude));
}))
{
accumDerivedPath(makeConstantStorePathRef(inputDrv), inputNode);
@@ -406,13 +405,12 @@ static void main_nix_build(AsyncIoRoot & aio, std::string programName, Strings a
if (dryRun) return;
if (shellDrv) {
auto shellDrvOutputs =
aio.blockOn(store->queryPartialDerivationOutputMap(shellDrv.value(), &*evalStore));
auto shellDrvOutputs = store->queryPartialDerivationOutputMap(shellDrv.value(), &*evalStore);
shell = store->printStorePath(shellDrvOutputs.at("out").value()) + "/bin/bash";
}
if (experimentalFeatureSettings.isEnabled(Xp::CaDerivations)) {
auto resolvedDrv = aio.blockOn(drv.tryResolve(*store));
auto resolvedDrv = drv.tryResolve(*store);
assert(resolvedDrv && "Successfully resolved the derivation");
drv = *resolvedDrv;
}
@@ -458,11 +456,10 @@ static void main_nix_build(AsyncIoRoot & aio, std::string programName, Strings a
std::function<void(const StorePath &, const DerivedPathMap<StringSet>::ChildNode &)> accumInputClosure;
accumInputClosure = [&](const StorePath & inputDrv, const DerivedPathMap<StringSet>::ChildNode & inputNode) {
auto outputs =
aio.blockOn(store->queryPartialDerivationOutputMap(inputDrv, &*evalStore));
auto outputs = store->queryPartialDerivationOutputMap(inputDrv, &*evalStore);
for (auto & i : inputNode.value) {
auto o = outputs.at(i);
aio.blockOn(store->computeFSClosure(*o, inputs));
store->computeFSClosure(*o, inputs);
}
for (const auto & [outputName, childNode] : inputNode.childMap)
accumInputClosure(*outputs.at(outputName), childNode);
@@ -473,7 +470,7 @@ static void main_nix_build(AsyncIoRoot & aio, std::string programName, Strings a
ParsedDerivation parsedDrv(drvInfo.requireDrvPath(*state), drv);
if (auto structAttrs = aio.blockOn(parsedDrv.prepareStructuredAttrs(*store, inputs))) {
if (auto structAttrs = parsedDrv.prepareStructuredAttrs(*store, inputs)) {
auto json = structAttrs.value();
structuredAttrsRC = writeStructuredAttrsShell(json);
@@ -606,17 +603,16 @@ static void main_nix_build(AsyncIoRoot & aio, std::string programName, Strings a
if (counter)
drvPrefix += fmt("-%d", counter + 1);
auto builtOutputs =
aio.blockOn(store->queryPartialDerivationOutputMap(drvPath, &*evalStore));
auto builtOutputs = store->queryPartialDerivationOutputMap(drvPath, &*evalStore);
auto maybeOutputPath = builtOutputs.at(outputName);
assert(maybeOutputPath);
auto outputPath = *maybeOutputPath;
if (auto store2 = store.try_cast_shared<LocalFSStore>()) {
if (auto store2 = store.dynamic_pointer_cast<LocalFSStore>()) {
std::string symlink = drvPrefix;
if (outputName != "out") symlink += "-" + outputName;
aio.blockOn(store2->addPermRoot(outputPath, absPath(symlink)));
store2->addPermRoot(outputPath, absPath(symlink));
}
outPaths.push_back(outputPath);
@@ -629,9 +625,9 @@ static void main_nix_build(AsyncIoRoot & aio, std::string programName, Strings a
}
}
void registerLegacyNixBuildAndNixShell() {
LegacyCommandRegistry::add("nix-build", main_nix_build);
LegacyCommandRegistry::add("nix-shell", main_nix_build);
void registerNixBuildAndNixShell() {
LegacyCommands::add("nix-build", main_nix_build);
LegacyCommands::add("nix-shell", main_nix_build);
}
}
+1 -1
View File
@@ -3,6 +3,6 @@
namespace nix {
void registerLegacyNixBuildAndNixShell();
void registerNixBuildAndNixShell();
}
+18 -31
View File
@@ -7,8 +7,6 @@
#include "lix/libfetchers/fetchers.hh"
#include "lix/libexpr/eval-settings.hh" // for defexpr
#include "lix/libstore/temporary-dir.hh"
#include "lix/libutil/async.hh"
#include "lix/libutil/regex.hh"
#include "lix/libutil/users.hh"
#include "nix-channel.hh"
@@ -31,10 +29,10 @@ static void readChannels()
for (const auto & line : tokenizeString<std::vector<std::string>>(channelsFile, "\n")) {
chomp(line);
if (std::regex_search(line, regex::parse("^\\s*\\#")))
if (std::regex_search(line, std::regex("^\\s*\\#")))
continue;
auto split = tokenizeString<std::vector<std::string>>(line, " ");
auto url = std::regex_replace(split[0], regex::parse("/*$"), "");
auto url = std::regex_replace(split[0], std::regex("/*$"), "");
auto name = split.size() > 1 ? split[1] : std::string(baseNameOf(url));
channels[name] = url;
}
@@ -53,9 +51,9 @@ static void writeChannels()
// Adds a channel.
static void addChannel(const std::string & url, const std::string & name)
{
if (!regex_search(url, regex::parse("^(file|http|https)://")))
if (!regex_search(url, std::regex("^(file|http|https)://")))
throw Error("invalid channel URL '%1%'", url);
if (!regex_search(name, regex::parse("^[a-zA-Z0-9_][a-zA-Z0-9_\\.-]*$")))
if (!regex_search(name, std::regex("^[a-zA-Z0-9_][a-zA-Z0-9_\\.-]*$")))
throw Error("invalid channel identifier '%1%'", name);
readChannels();
channels[name] = url;
@@ -77,11 +75,11 @@ static void removeChannel(const std::string & name)
static Path nixDefExpr;
// Fetch Nix expressions and binary cache URLs from the subscribed channels.
static void update(AsyncIoRoot & aio, const StringSet & channelNames)
static void update(const StringSet & channelNames)
{
readChannels();
auto store = aio.blockOn(openStore());
auto store = openStore();
auto [fd, unpackChannelPath] = createTempFile();
writeFull(fd.get(),
@@ -102,7 +100,7 @@ static void update(AsyncIoRoot & aio, const StringSet & channelNames)
auto cname = name;
std::smatch match;
auto urlBase = std::string(baseNameOf(url));
if (std::regex_search(urlBase, match, regex::parse("(-\\d.*)$")))
if (std::regex_search(urlBase, match, std::regex("(-\\d.*)$")))
cname = cname + match.str(1);
std::string extraAttrs;
@@ -116,17 +114,12 @@ static void update(AsyncIoRoot & aio, const StringSet & channelNames)
// We want to download the url to a file to see if it's a tarball while also checking if we
// got redirected in the process, so that we can grab the various parts of a nix channel
// definition from a consistent location if the redirect changes mid-download.
auto result = aio.blockOn(fetchers::downloadFile(
store,
url,
std::string(baseNameOf(url)),
false
));
auto result = fetchers::downloadFile(store, url, std::string(baseNameOf(url)), false);
auto filename = store->toRealPath(result.storePath);
url = result.effectiveUrl;
bool unpacked = false;
if (std::regex_search(filename, regex::parse("\\.tar\\.(gz|bz2|xz)$"))) {
if (std::regex_search(filename, std::regex("\\.tar\\.(gz|bz2|xz)$"))) {
runProgram(settings.nixBinDir + "/nix-build", false, { "--no-out-link", "--expr", "import " + unpackChannelPath +
"{ name = \"" + cname + "\"; channelName = \"" + name + "\"; src = builtins.storePath \"" + filename + "\"; }" });
unpacked = true;
@@ -134,17 +127,11 @@ static void update(AsyncIoRoot & aio, const StringSet & channelNames)
if (!unpacked) {
// Download the channel tarball.
std::optional<fetchers::DownloadFileResult> exprs;
try {
exprs = aio.blockOn(fetchers::downloadFile(
store, url + "/nixexprs.tar.xz", "nixexprs.tar.xz", false
));
filename = store->toRealPath(fetchers::downloadFile(store, url + "/nixexprs.tar.xz", "nixexprs.tar.xz", false).storePath);
} catch (FileTransferError & e) {
exprs = aio.blockOn(fetchers::downloadFile(
store, url + "/nixexprs.tar.bz2", "nixexprs.tar.bz2", false
));
filename = store->toRealPath(fetchers::downloadFile(store, url + "/nixexprs.tar.bz2", "nixexprs.tar.bz2", false).storePath);
}
filename = store->toRealPath(exprs->storePath);
}
// Regardless of where it came from, add the expression representing this channel to accumulated expression
exprs.push_back("f: f { name = \"" + cname + "\"; channelName = \"" + name + "\"; src = builtins.storePath \"" + filename + "\"; " + extraAttrs + " }");
@@ -175,7 +162,7 @@ static void update(AsyncIoRoot & aio, const StringSet & channelNames)
replaceSymlink(profile, channelLink);
}
static int main_nix_channel(AsyncIoRoot & aio, std::string programName, Strings argv)
static int main_nix_channel(std::string programName, Strings argv)
{
{
// Figure out the name of the `.nix-channels' file to use
@@ -197,7 +184,7 @@ static int main_nix_channel(AsyncIoRoot & aio, std::string programName, Strings
cRollback
} cmd = cNone;
std::vector<std::string> args;
LegacyArgs(aio, programName, [&](Strings::iterator & arg, const Strings::iterator & end) {
LegacyArgs(programName, [&](Strings::iterator & arg, const Strings::iterator & end) {
if (*arg == "--help") {
showManPage("nix-channel");
} else if (*arg == "--version") {
@@ -235,8 +222,8 @@ static int main_nix_channel(AsyncIoRoot & aio, std::string programName, Strings
name = args[1];
} else {
name = baseNameOf(url);
name = std::regex_replace(name, regex::parse("-unstable$"), "");
name = std::regex_replace(name, regex::parse("-stable$"), "");
name = std::regex_replace(name, std::regex("-unstable$"), "");
name = std::regex_replace(name, std::regex("-stable$"), "");
}
addChannel(url, name);
}
@@ -254,7 +241,7 @@ static int main_nix_channel(AsyncIoRoot & aio, std::string programName, Strings
std::cout << channel.first << ' ' << channel.second << '\n';
break;
case cUpdate:
update(aio, StringSet(args.begin(), args.end()));
update(StringSet(args.begin(), args.end()));
break;
case cListGenerations:
if (!args.empty())
@@ -279,8 +266,8 @@ static int main_nix_channel(AsyncIoRoot & aio, std::string programName, Strings
}
}
void registerLegacyNixChannel() {
LegacyCommandRegistry::add("nix-channel", main_nix_channel);
void registerNixChannel() {
LegacyCommands::add("nix-channel", main_nix_channel);
}
}
+1 -1
View File
@@ -3,6 +3,6 @@
namespace nix {
void registerLegacyNixChannel();
void registerNixChannel();
}
+8 -8
View File
@@ -7,9 +7,9 @@
#include "lix/libstore/globals.hh"
#include "lix/libcmd/legacy.hh"
#include "lix/libutil/signals.hh"
#include "lix/libutil/types.hh"
#include "nix-collect-garbage.hh"
#include <iostream>
#include <cerrno>
namespace nix {
@@ -22,7 +22,7 @@ bool dryRun = false;
* Of course, this makes rollbacks to before this point in time
* impossible. */
static void removeOldGenerations(std::string dir, NeverAsync = {})
void removeOldGenerations(std::string dir)
{
if (access(dir.c_str(), R_OK) != 0) return;
@@ -56,14 +56,14 @@ static void removeOldGenerations(std::string dir, NeverAsync = {})
}
}
static int main_nix_collect_garbage(AsyncIoRoot & aio, std::string programName, Strings argv)
static int main_nix_collect_garbage(std::string programName, Strings argv)
{
{
bool removeOld = false;
GCOptions options;
LegacyArgs(aio, programName, [&](Strings::iterator & arg, const Strings::iterator & end) {
LegacyArgs(programName, [&](Strings::iterator & arg, const Strings::iterator & end) {
if (*arg == "--help")
showManPage("nix-collect-garbage");
else if (*arg == "--version")
@@ -94,11 +94,11 @@ static int main_nix_collect_garbage(AsyncIoRoot & aio, std::string programName,
} else {
options.action = GCOptions::gcReturnDead;
}
auto store = aio.blockOn(openStore());
auto store = openStore();
auto & gcStore = require<GcStore>(*store);
GCResults results;
PrintFreed freed(true, results);
aio.blockOn(gcStore.collectGarbage(options, results));
gcStore.collectGarbage(options, results);
if (dryRun) {
// Only print results for dry run; when !dryRun, paths will be printed as they're deleted.
@@ -111,8 +111,8 @@ static int main_nix_collect_garbage(AsyncIoRoot & aio, std::string programName,
}
}
void registerLegacyNixCollectGarbage() {
LegacyCommandRegistry::add("nix-collect-garbage", main_nix_collect_garbage);
void registerNixCollectGarbage() {
LegacyCommands::add("nix-collect-garbage", main_nix_collect_garbage);
}
}
+1 -1
View File
@@ -3,6 +3,6 @@
namespace nix {
void registerLegacyNixCollectGarbage();
void registerNixCollectGarbage();
}
+7 -7
View File
@@ -5,7 +5,7 @@
namespace nix {
static int main_nix_copy_closure(AsyncIoRoot & aio, std::string programName, Strings argv)
static int main_nix_copy_closure(std::string programName, Strings argv)
{
{
auto gzip = false;
@@ -16,7 +16,7 @@ static int main_nix_copy_closure(AsyncIoRoot & aio, std::string programName, Str
std::string sshHost;
PathSet storePaths;
LegacyArgs(aio, programName, [&](Strings::iterator & arg, const Strings::iterator & end) {
LegacyArgs(programName, [&](Strings::iterator & arg, const Strings::iterator & end) {
if (*arg == "--help")
showManPage("nix-copy-closure");
else if (*arg == "--version")
@@ -48,21 +48,21 @@ static int main_nix_copy_closure(AsyncIoRoot & aio, std::string programName, Str
throw UsageError("no host name specified");
auto remoteUri = "ssh://" + sshHost + (gzip ? "?compress=true" : "");
auto to = aio.blockOn(toMode ? openStore(remoteUri) : openStore());
auto from = aio.blockOn(toMode ? openStore() : openStore(remoteUri));
auto to = toMode ? openStore(remoteUri) : openStore();
auto from = toMode ? openStore() : openStore(remoteUri);
RealisedPath::Set storePaths2;
for (auto & path : storePaths)
storePaths2.insert(from->followLinksToStorePath(path));
aio.blockOn(copyClosure(*from, *to, storePaths2, NoRepair, NoCheckSigs, useSubstitutes));
copyClosure(*from, *to, storePaths2, NoRepair, NoCheckSigs, useSubstitutes);
return 0;
}
}
void registerLegacyNixCopyClosure() {
LegacyCommandRegistry::add("nix-copy-closure", main_nix_copy_closure);
void registerNixCopyClosure() {
LegacyCommands::add("nix-copy-closure", main_nix_copy_closure);
}
}
+1 -1
View File
@@ -3,6 +3,6 @@
namespace nix {
void registerLegacyNixCopyClosure();
void registerNixCopyClosure();
}
+47 -60
View File
@@ -28,6 +28,7 @@
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <nlohmann/json.hpp>
using std::cout;
@@ -56,7 +57,6 @@ struct InstallSourceInfo
struct Globals
{
AsyncIoRoot & aio;
InstallSourceInfo instSource;
Path profile;
std::shared_ptr<Evaluator> state;
@@ -229,8 +229,8 @@ static std::strong_ordering comparePriorities(EvalState & state, DrvInfo & drv1,
static bool isPrebuilt(EvalState & state, DrvInfo & elem)
{
auto path = elem.queryOutPath(state);
if (state.aio.blockOn(state.ctx.store->isValidPath(path))) return true;
return state.aio.blockOn(state.ctx.store->querySubstitutablePaths({path})).count(path);
if (state.ctx.store->isValidPath(path)) return true;
return state.ctx.store->querySubstitutablePaths({path}).count(path);
}
@@ -447,8 +447,7 @@ static void queryInstSources(EvalState & state,
if (path.isDerivation()) {
elem.setDrvPath(path);
auto outputs =
state.aio.blockOn(state.ctx.store->queryDerivationOutputMap(path));
auto outputs = state.ctx.store->queryDerivationOutputMap(path);
elem.setOutPath(outputs.at("out"));
if (name.size() >= drvExtension.size() &&
std::string(name, name.size() - drvExtension.size()) == drvExtension)
@@ -503,7 +502,7 @@ static void printMissing(EvalState & state, DrvInfos & elems)
.path = i.queryOutPath(state),
});
state.aio.blockOn(printMissing(state.ctx.store, targets));
printMissing(state.ctx.store, targets);
}
@@ -512,20 +511,13 @@ static bool keep(EvalState & state, DrvInfo & drv)
return drv.queryMetaBool(state, "keep", false);
}
static void setMetaFlag(EvalState & state, DrvInfo & drv,
const std::string & name, const std::string & value)
{
auto v = state.ctx.mem.allocValue();
v->mkString(value);
drv.setMeta(state, name, v);
}
static void installDerivations(Globals & globals,
const Strings & args, const Path & profile, std::optional<int> priority)
const Strings & args, const Path & profile)
{
debug("installing derivations");
auto state = globals.state->begin(globals.aio);
auto state = globals.state->begin();
/* Get the set of user environment elements to be installed. */
DrvInfos newElems, newElemsTmp;
@@ -547,11 +539,6 @@ static void installDerivations(Globals & globals,
newNames.insert(DrvName(i.queryName(*state)).name);
}
if (priority) {
for (auto & drv : newElems) {
setMetaFlag(*state, drv, "priority", std::to_string((priority.value())));
}
}
while (true) {
auto lockToken = optimisticLockProfile(profile);
@@ -589,7 +576,6 @@ static void installDerivations(Globals & globals,
static void opInstall(Globals & globals, Strings opFlags, Strings opArgs)
{
std::optional<int> priority;
for (Strings::iterator i = opFlags.begin(); i != opFlags.end(); ) {
auto arg = *i++;
if (parseInstallSourceOptions(globals, i, opFlags, arg)) ;
@@ -597,17 +583,10 @@ static void opInstall(Globals & globals, Strings opFlags, Strings opArgs)
globals.preserveInstalled = true;
else if (arg == "--remove-all" || arg == "-r")
globals.removeAll = true;
else if (arg == "--priority") {
if (i == opFlags.end())
throw UsageError("'%1%' requires an argument", arg);
priority = string2Int<int>(*i++);
if (!priority)
throw UsageError("'--priority' requires an integer argument");
}
else throw UsageError("unknown flag '%1%'", arg);
}
installDerivations(globals, opArgs, globals.profile, priority);
installDerivations(globals, opArgs, globals.profile);
}
@@ -619,7 +598,7 @@ static void upgradeDerivations(Globals & globals,
{
debug("upgrading derivations");
auto state = globals.state->begin(globals.aio);
auto state = globals.state->begin();
/* Upgrade works as follows: we take all currently installed
derivations, and for any derivation matching any selector, look
@@ -724,6 +703,15 @@ static void opUpgrade(Globals & globals, Strings opFlags, Strings opArgs)
}
static void setMetaFlag(EvalState & state, DrvInfo & drv,
const std::string & name, const std::string & value)
{
auto v = state.ctx.mem.allocValue();
v->mkString(value);
drv.setMeta(state, name, v);
}
static void opSetFlag(Globals & globals, Strings opFlags, Strings opArgs)
{
if (opFlags.size() > 0)
@@ -736,7 +724,7 @@ static void opSetFlag(Globals & globals, Strings opFlags, Strings opArgs)
std::string flagValue = *arg++;
DrvNames selectors = drvNamesFromArgs(Strings(arg, opArgs.end()));
auto state = globals.state->begin(globals.aio);
auto state = globals.state->begin();
while (true) {
std::string lockToken = optimisticLockProfile(globals.profile);
@@ -766,9 +754,9 @@ static void opSetFlag(Globals & globals, Strings opFlags, Strings opArgs)
static void opSet(Globals & globals, Strings opFlags, Strings opArgs)
{
auto state = globals.state->begin(globals.aio);
auto state = globals.state->begin();
auto store2 = globals.state->store.try_cast_shared<LocalFSStore>();
auto store2 = globals.state->store.dynamic_pointer_cast<LocalFSStore>();
if (!store2) throw Error("--set is not supported for this Nix store");
for (Strings::iterator i = opFlags.begin(); i != opFlags.end(); ) {
@@ -799,17 +787,15 @@ static void opSet(Globals & globals, Strings opFlags, Strings opArgs)
.path = drv.queryOutPath(*state),
}),
};
globals.aio.blockOn(printMissing(globals.state->store, paths));
printMissing(globals.state->store, paths);
if (globals.dryRun) return;
globals.aio.blockOn(
globals.state->store->buildPaths(paths, globals.state->repair ? bmRepair : bmNormal)
);
globals.state->store->buildPaths(paths, globals.state->repair ? bmRepair : bmNormal);
debug("switching to new user environment");
Path generation = globals.aio.blockOn(createGeneration(
Path generation = createGeneration(
*store2,
globals.profile,
drv.queryOutPath(*state)));
drv.queryOutPath(*state));
switchLink(globals.profile, generation);
}
@@ -817,7 +803,7 @@ static void opSet(Globals & globals, Strings opFlags, Strings opArgs)
static void uninstallDerivations(Globals & globals, Strings & selectors,
Path & profile)
{
auto state = globals.state->begin(globals.aio);
auto state = globals.state->begin();
while (true) {
auto lockToken = optimisticLockProfile(profile);
@@ -960,14 +946,15 @@ static VersionDiff compareVersionAgainstSet(
static void queryJSON(EvalState & state, Globals & globals, std::vector<DrvInfo> & elems, bool printOutPath, bool printDrvPath, bool printMeta)
{
JSON topObj = JSON::object();
using nlohmann::json;
json topObj = json::object();
for (auto & i : elems) {
try {
if (i.hasFailed()) continue;
auto drvName = DrvName(i.queryName(state));
JSON &pkgObj = topObj[i.attrPath];
json &pkgObj = topObj[i.attrPath];
pkgObj = {
{"name", drvName.fullName},
{"pname", drvName.name},
@@ -978,8 +965,8 @@ static void queryJSON(EvalState & state, Globals & globals, std::vector<DrvInfo>
{
DrvInfo::Outputs outputs = i.queryOutputs(state, printOutPath);
JSON &outputObj = pkgObj["outputs"];
outputObj = JSON::object();
json &outputObj = pkgObj["outputs"];
outputObj = json::object();
for (auto & j : outputs) {
if (j.second)
outputObj[j.first] = globals.state->store->printStorePath(*j.second);
@@ -994,8 +981,8 @@ static void queryJSON(EvalState & state, Globals & globals, std::vector<DrvInfo>
}
if (printMeta) {
JSON &metaObj = pkgObj["meta"];
metaObj = JSON::object();
json &metaObj = pkgObj["meta"];
metaObj = json::object();
StringSet metaNames = i.queryMetaNames(state);
for (auto & j : metaNames) {
Value * v = i.queryMeta(state, j);
@@ -1022,7 +1009,7 @@ static void queryJSON(EvalState & state, Globals & globals, std::vector<DrvInfo>
static void opQuery(Globals & globals, Strings opFlags, Strings opArgs)
{
auto & store { *globals.state->store };
auto state = globals.state->begin(globals.aio);
auto state = globals.state->begin();
Strings remaining;
std::string attrPath;
@@ -1112,8 +1099,8 @@ static void opQuery(Globals & globals, Strings opFlags, Strings opArgs)
printMsg(lvlTalkative, "skipping derivation named '%s' which gives an assertion failure", i.queryName(*state));
i.setFailed();
}
validPaths = globals.aio.blockOn(store.queryValidPaths(paths));
substitutablePaths = globals.aio.blockOn(store.querySubstitutablePaths(paths));
validPaths = store.queryValidPaths(paths);
substitutablePaths = store.querySubstitutablePaths(paths);
}
@@ -1369,7 +1356,8 @@ static void opListGenerations(Globals & globals, Strings opFlags, Strings opArgs
if (opArgs.size() != 0)
throw UsageError("no arguments expected");
PathLock lock = lockProfile(globals.profile);
PathLocks lock;
lockProfile(lock, globals.profile);
auto [gens, curGen] = findGenerations(globals.profile);
@@ -1424,7 +1412,7 @@ static void opVersion(Globals & globals, Strings opFlags, Strings opArgs)
}
static int main_nix_env(AsyncIoRoot & aio, std::string programName, Strings argv)
static int main_nix_env(std::string programName, Strings argv)
{
{
Strings opFlags, opArgs;
@@ -1433,7 +1421,7 @@ static int main_nix_env(AsyncIoRoot & aio, std::string programName, Strings argv
bool showHelp = false;
std::string file;
Globals globals{aio, {}, {}, {}, {}, {}, {}, {}, {}};
Globals globals;
globals.instSource.type = srcUnknown;
globals.instSource.systemFilter = "*";
@@ -1463,7 +1451,7 @@ static int main_nix_env(AsyncIoRoot & aio, std::string programName, Strings argv
using LegacyArgs::LegacyArgs;
};
MyArgs myArgs(aio, programName, [&](Strings::iterator & arg, const Strings::iterator & end) {
MyArgs myArgs(programName, [&](Strings::iterator & arg, const Strings::iterator & end) {
Operation oldOp = op;
if (*arg == "--help")
@@ -1532,8 +1520,7 @@ static int main_nix_env(AsyncIoRoot & aio, std::string programName, Strings argv
opFlags.push_back(*arg);
/* FIXME: hacky */
if (*arg == "--from-profile" ||
(op == opQuery && (*arg == "--attr" || *arg == "-A")) ||
(op == opInstall && (*arg == "--priority")))
(op == opQuery && (*arg == "--attr" || *arg == "-A")))
opFlags.push_back(getArg(*arg, arg, end));
}
else
@@ -1550,14 +1537,14 @@ static int main_nix_env(AsyncIoRoot & aio, std::string programName, Strings argv
if (showHelp) showManPage("nix-env" + opName);
if (!op) throw UsageError("no operation specified");
auto store = aio.blockOn(openStore());
auto store = openStore();
globals.state = std::make_shared<Evaluator>(aio, myArgs.searchPath, store);
globals.state = std::make_shared<Evaluator>(myArgs.searchPath, store);
globals.state->repair = myArgs.repair;
globals.instSource.nixExprPath = std::make_shared<SourcePath>(
file != ""
? aio.blockOn(lookupFileArg(*globals.state, file)).unwrap()
? lookupFileArg(*globals.state, file)
: CanonPath(nixExprPath));
globals.instSource.autoArgs = myArgs.getAutoArgs(*globals.state);
@@ -1576,8 +1563,8 @@ static int main_nix_env(AsyncIoRoot & aio, std::string programName, Strings argv
}
}
void registerLegacyNixEnv() {
LegacyCommandRegistry::add("nix-env", main_nix_env);
void registerNixEnv() {
LegacyCommands::add("nix-env", main_nix_env);
}
}
+1 -1
View File
@@ -3,6 +3,6 @@
namespace nix {
void registerLegacyNixEnv();
void registerNixEnv();
}
+20 -19
View File
@@ -2,6 +2,7 @@
#include "lix/libexpr/print-ambiguous.hh"
#include "lix/libmain/shared.hh"
#include "lix/libexpr/eval.hh"
#include "lix/libexpr/eval-inline.hh"
#include "lix/libexpr/get-drvs.hh"
#include "lix/libexpr/attr-path.hh"
#include "lix/libexpr/value-to-xml.hh"
@@ -12,6 +13,7 @@
#include "lix/libcmd/legacy.hh"
#include "nix-instantiate.hh"
#include <map>
#include <iostream>
@@ -29,7 +31,7 @@ void processExpr(EvalState & state, const Strings & attrPaths,
bool evalOnly, OutputKind output, bool location, Expr & e)
{
if (parseOnly) {
std::cout << e.toJSON(state.ctx.symbols).dump(2);
e.show(state.ctx.symbols, std::cout);
std::cout << "\n";
return;
}
@@ -39,7 +41,7 @@ void processExpr(EvalState & state, const Strings & attrPaths,
for (auto & i : attrPaths) {
Value & v(*findAlongAttrPath(state, i, autoArgs, vRoot).first);
state.forceValue(v, noPos);
state.forceValue(v, v.determinePos(noPos));
NixStringContext context;
if (evalOnly) {
@@ -47,11 +49,11 @@ void processExpr(EvalState & state, const Strings & attrPaths,
if (autoArgs.empty())
vRes = v;
else
state.autoCallFunction(autoArgs, v, vRes, noPos);
state.autoCallFunction(autoArgs, v, vRes);
if (output == okXML)
printValueAsXML(state, strict, location, vRes, std::cout, context, noPos);
else if (output == okJSON) {
printValueAsJSON(state, strict, vRes, noPos, std::cout, context);
printValueAsJSON(state, strict, vRes, v.determinePos(noPos), std::cout, context);
std::cout << std::endl;
} else {
if (strict) state.forceValueDeep(vRes);
@@ -76,9 +78,9 @@ void processExpr(EvalState & state, const Strings & attrPaths,
else {
Path rootName = absPath(gcRoot);
if (++rootNr > 1) rootName += "-" + std::to_string(rootNr);
auto store2 = state.ctx.store.try_cast_shared<LocalFSStore>();
auto store2 = state.ctx.store.dynamic_pointer_cast<LocalFSStore>();
if (store2)
drvPathS = state.aio.blockOn(store2->addPermRoot(drvPath, rootName));
drvPathS = store2->addPermRoot(drvPath, rootName);
}
std::cout << fmt("%s%s\n", drvPathS, (outputName != "out" ? "!" + outputName : ""));
}
@@ -87,7 +89,7 @@ void processExpr(EvalState & state, const Strings & attrPaths,
}
static int main_nix_instantiate(AsyncIoRoot & aio, std::string programName, Strings argv)
static int main_nix_instantiate(std::string programName, Strings argv)
{
{
Strings files;
@@ -107,7 +109,7 @@ static int main_nix_instantiate(AsyncIoRoot & aio, std::string programName, Stri
using LegacyArgs::LegacyArgs;
};
MyArgs myArgs(aio, programName, [&](Strings::iterator & arg, const Strings::iterator & end) {
MyArgs myArgs(programName, [&](Strings::iterator & arg, const Strings::iterator & end) {
if (*arg == "--help")
showManPage("nix-instantiate");
else if (*arg == "--version")
@@ -152,11 +154,11 @@ static int main_nix_instantiate(AsyncIoRoot & aio, std::string programName, Stri
if (evalOnly && !wantsReadWrite)
settings.readOnlyMode = true;
auto store = aio.blockOn(openStore());
auto evalStore = myArgs.evalStoreUrl ? aio.blockOn(openStore(*myArgs.evalStoreUrl)) : store;
auto store = openStore();
auto evalStore = myArgs.evalStoreUrl ? openStore(*myArgs.evalStoreUrl) : store;
auto evaluator = std::make_unique<Evaluator>(aio, myArgs.searchPath, evalStore, store);
auto state = evaluator->begin(aio);
auto evaluator = std::make_unique<Evaluator>(myArgs.searchPath, evalStore, store);
auto state = evaluator->begin();
evaluator->repair = myArgs.repair;
Bindings & autoArgs = *myArgs.getAutoArgs(*evaluator);
@@ -165,7 +167,7 @@ static int main_nix_instantiate(AsyncIoRoot & aio, std::string programName, Stri
if (findFile) {
for (auto & i : files) {
auto p = aio.blockOn(evaluator->paths.findFile(i)).unwrap();
auto p = evaluator->paths.findFile(i);
std::cout << p.canonical().abs() << std::endl;
}
return 0;
@@ -179,10 +181,9 @@ static int main_nix_instantiate(AsyncIoRoot & aio, std::string programName, Stri
files.push_back("./default.nix");
for (auto & i : files) {
Expr & e = fromArgs ? evaluator->parseExprFromString(i, CanonPath::fromCwd())
: evaluator->parseExprFromFile(evaluator->paths.resolveExprPath(
aio.blockOn(lookupFileArg(*evaluator, i)).unwrap()
));
Expr & e = fromArgs
? evaluator->parseExprFromString(i, CanonPath::fromCwd())
: evaluator->parseExprFromFile(evaluator->paths.resolveExprPath(lookupFileArg(*evaluator, i)));
processExpr(*state, attrPaths, parseOnly, strict, autoArgs,
evalOnly, outputKind, xmlOutputSourceLocation, e);
}
@@ -193,8 +194,8 @@ static int main_nix_instantiate(AsyncIoRoot & aio, std::string programName, Stri
}
}
void registerLegacyNixInstantiate() {
LegacyCommandRegistry::add("nix-instantiate", main_nix_instantiate);
void registerNixInstantiate() {
LegacyCommands::add("nix-instantiate", main_nix_instantiate);
}
}
+1 -1
View File
@@ -3,6 +3,6 @@
namespace nix {
void registerLegacyNixInstantiate();
void registerNixInstantiate();
}
+134 -181
View File
@@ -1,18 +1,16 @@
#include "lix/libutil/archive.hh"
#include "lix/libstore/derivations.hh"
#include "dotgraph.hh"
#include "lix/libutil/async-io.hh"
#include "lix/libutil/async.hh"
#include "lix/libutil/exit.hh"
#include "lix/libstore/globals.hh"
#include "lix/libstore/build-result.hh" // IWYU pragma: keep
#include "lix/libstore/build-result.hh"
#include "lix/libstore/store-cast.hh"
#include "lix/libstore/gc-store.hh"
#include "lix/libstore/log-store.hh"
#include "lix/libstore/local-store.hh"
#include "lix/libutil/monitor-fd.hh"
#include "lix/libstore/serve-protocol.hh"
#include "lix/libstore/serve-protocol-impl.hh" // IWYU pragma: keep
#include "lix/libstore/serve-protocol-impl.hh"
#include "lix/libmain/shared.hh"
#include "graphml.hh"
#include "lix/libcmd/legacy.hh"
@@ -34,7 +32,7 @@ using std::cin;
using std::cout;
typedef void (* Operation) (AsyncIoRoot & aio, Strings opFlags, Strings opArgs);
typedef void (* Operation) (Strings opFlags, Strings opArgs);
static Path gcRoot;
@@ -47,32 +45,30 @@ ref<LocalStore> ensureLocalStore()
{
auto store2 = std::dynamic_pointer_cast<LocalStore>(store);
if (!store2) throw Error("you don't have sufficient rights to use this command");
return ref<LocalStore>::unsafeFromPtr(store2);
return ref<LocalStore>(store2);
}
static kj::Promise<Result<StorePath>> useDeriver(const StorePath & path)
try {
if (path.isDerivation()) co_return path;
auto info = TRY_AWAIT(store->queryPathInfo(path));
static StorePath useDeriver(const StorePath & path)
{
if (path.isDerivation()) return path;
auto info = store->queryPathInfo(path);
if (!info->deriver)
throw Error("deriver of path '%s' is not known", store->printStorePath(path));
co_return *info->deriver;
} catch (...) {
co_return result::current_exception();
return *info->deriver;
}
/* Realise the given path. For a derivation that means build it; for
other paths it means ensure their validity. */
static kj::Promise<Result<PathSet>> realisePath(StorePathWithOutputs path, bool build = true)
try {
static PathSet realisePath(StorePathWithOutputs path, bool build = true)
{
auto store2 = std::dynamic_pointer_cast<LocalFSStore>(store);
if (path.path.isDerivation()) {
if (build) TRY_AWAIT(store->buildPaths({path.toDerivedPath()}));
auto outputPaths = TRY_AWAIT(store->queryDerivationOutputMap(path.path));
Derivation drv = TRY_AWAIT(store->derivationFromPath(path.path));
if (build) store->buildPaths({path.toDerivedPath()});
auto outputPaths = store->queryDerivationOutputMap(path.path);
Derivation drv = store->derivationFromPath(path.path);
rootNr++;
/* FIXME: Encode this empty special case explicitly in the type. */
@@ -95,17 +91,17 @@ try {
Path rootName = gcRoot;
if (rootNr > 1) rootName += "-" + std::to_string(rootNr);
if (i->first != "out") rootName += "-" + i->first;
retPath = TRY_AWAIT(store2->addPermRoot(outPath, rootName));
retPath = store2->addPermRoot(outPath, rootName);
}
}
outputs.insert(retPath);
}
co_return outputs;
return outputs;
}
else {
if (build) TRY_AWAIT(store->ensurePath(path.path));
else if (!TRY_AWAIT(store->isValidPath(path.path)))
if (build) store->ensurePath(path.path);
else if (!store->isValidPath(path.path))
throw Error("path '%s' does not exist and cannot be created", store->printStorePath(path.path));
if (store2) {
if (gcRoot == "")
@@ -114,18 +110,16 @@ try {
Path rootName = gcRoot;
rootNr++;
if (rootNr > 1) rootName += "-" + std::to_string(rootNr);
co_return PathSet{TRY_AWAIT(store2->addPermRoot(path.path, rootName))};
return {store2->addPermRoot(path.path, rootName)};
}
}
co_return PathSet{store->printStorePath(path.path)};
return {store->printStorePath(path.path)};
}
} catch (...) {
co_return result::current_exception();
}
/* Realise the given paths. */
static void opRealise(AsyncIoRoot & aio, Strings opFlags, Strings opArgs)
static void opRealise(Strings opFlags, Strings opArgs)
{
bool dryRun = false;
BuildMode buildMode = bmNormal;
@@ -144,9 +138,9 @@ static void opRealise(AsyncIoRoot & aio, Strings opFlags, Strings opArgs)
uint64_t downloadSize, narSize;
StorePathSet willBuild, willSubstitute, unknown;
aio.blockOn(store->queryMissing(
store->queryMissing(
toDerivedPaths(paths),
willBuild, willSubstitute, unknown, downloadSize, narSize));
willBuild, willSubstitute, unknown, downloadSize, narSize);
/* Filter out unknown paths from `paths`. */
if (ignoreUnknown) {
@@ -157,20 +151,17 @@ static void opRealise(AsyncIoRoot & aio, Strings opFlags, Strings opArgs)
unknown = StorePathSet();
}
if (settings.printMissing) {
aio.blockOn(printMissing(
ref<Store>::unsafeFromPtr(store), willBuild, willSubstitute, unknown, downloadSize, narSize
));
}
if (settings.printMissing)
printMissing(ref<Store>(store), willBuild, willSubstitute, unknown, downloadSize, narSize);
if (dryRun) return;
/* Build all paths at the same time to exploit parallelism. */
aio.blockOn(store->buildPaths(toDerivedPaths(paths), buildMode));
store->buildPaths(toDerivedPaths(paths), buildMode);
if (!ignoreUnknown)
for (auto & i : paths) {
auto paths2 = aio.blockOn(realisePath(i, false));
auto paths2 = realisePath(i, false);
if (!noOutput)
for (auto & j : paths2)
cout << fmt("%1%\n", j);
@@ -179,24 +170,18 @@ static void opRealise(AsyncIoRoot & aio, Strings opFlags, Strings opArgs)
/* Add files to the Nix store and print the resulting paths. */
static void opAdd(AsyncIoRoot & aio, Strings opFlags, Strings opArgs)
static void opAdd(Strings opFlags, Strings opArgs)
{
if (!opFlags.empty()) throw UsageError("unknown flag");
for (auto & i : opArgs) {
cout << fmt(
"%s\n",
store->printStorePath(aio.blockOn(
store->addToStoreRecursive(std::string(baseNameOf(i)), *prepareDump(i))
))
);
}
for (auto & i : opArgs)
cout << fmt("%s\n", store->printStorePath(store->addToStore(std::string(baseNameOf(i)), i)));
}
/* Preload the output of a fixed-output derivation into the Nix
store. */
static void opAddFixed(AsyncIoRoot & aio, Strings opFlags, Strings opArgs)
static void opAddFixed(Strings opFlags, Strings opArgs)
{
auto method = FileIngestionMethod::Flat;
@@ -210,19 +195,13 @@ static void opAddFixed(AsyncIoRoot & aio, Strings opFlags, Strings opArgs)
HashType hashAlgo = parseHashType(opArgs.front());
opArgs.pop_front();
for (auto & i : opArgs) {
std::cout << fmt(
"%s\n",
store->printStorePath(
aio.blockOn(store->addToStoreSlow(baseNameOf(i), i, method, hashAlgo)).path
)
);
}
for (auto & i : opArgs)
std::cout << fmt("%s\n", store->printStorePath(store->addToStoreSlow(baseNameOf(i), i, method, hashAlgo).path));
}
/* Hack to support caching in `nix-prefetch-url'. */
static void opPrintFixedPath(AsyncIoRoot & aio, Strings opFlags, Strings opArgs)
static void opPrintFixedPath(Strings opFlags, Strings opArgs)
{
auto method = FileIngestionMethod::Flat;
@@ -246,31 +225,29 @@ static void opPrintFixedPath(AsyncIoRoot & aio, Strings opFlags, Strings opArgs)
}
static kj::Promise<Result<StorePathSet>> maybeUseOutputs(const StorePath & storePath, bool useOutput, bool forceRealise)
try {
if (forceRealise) TRY_AWAIT(realisePath({storePath}));
static StorePathSet maybeUseOutputs(const StorePath & storePath, bool useOutput, bool forceRealise)
{
if (forceRealise) realisePath({storePath});
if (useOutput && storePath.isDerivation()) {
auto drv = TRY_AWAIT(store->derivationFromPath(storePath));
auto drv = store->derivationFromPath(storePath);
StorePathSet outputs;
if (forceRealise)
co_return TRY_AWAIT(store->queryDerivationOutputs(storePath));
return store->queryDerivationOutputs(storePath);
for (auto & i : drv.outputsAndOptPaths(*store)) {
if (!i.second.second)
throw UsageError("Cannot use output path of floating content-addressed derivation until we know what it is (e.g. by building it)");
outputs.insert(*i.second.second);
}
co_return outputs;
return outputs;
}
else co_return StorePathSet{storePath};
} catch (...) {
co_return result::current_exception();
else return {storePath};
}
/* Some code to print a tree representation of a derivation dependency
graph. Topological sorting is used to keep the tree relatively
flat. */
static void printTree(AsyncIoRoot & aio, const StorePath & path,
static void printTree(const StorePath & path,
const std::string & firstPad, const std::string & tailPad, StorePathSet & done)
{
if (!done.insert(path).second) {
@@ -280,18 +257,18 @@ static void printTree(AsyncIoRoot & aio, const StorePath & path,
cout << fmt("%s%s\n", firstPad, store->printStorePath(path));
auto info = aio.blockOn(store->queryPathInfo(path));
auto info = store->queryPathInfo(path);
/* Topologically sort under the relation A < B iff A \in
closure(B). That is, if derivation A is an (possibly indirect)
input of B, then A is printed first. This has the effect of
flattening the tree, preventing deeply nested structures. */
auto sorted = aio.blockOn(store->topoSortPaths(info->references));
auto sorted = store->topoSortPaths(info->references);
reverse(sorted.begin(), sorted.end());
for (const auto &[n, i] : enumerate(sorted)) {
bool last = n + 1 == sorted.size();
printTree(aio, i,
printTree(i,
tailPad + (last ? treeLast : treeConn),
tailPad + (last ? treeNull : treeLine),
done);
@@ -300,7 +277,7 @@ static void printTree(AsyncIoRoot & aio, const StorePath & path,
/* Perform various sorts of queries. */
static void opQuery(AsyncIoRoot & aio, Strings opFlags, Strings opArgs)
static void opQuery(Strings opFlags, Strings opArgs)
{
enum QueryType
{ qOutputs, qRequisites, qReferences, qReferrers
@@ -351,7 +328,7 @@ static void opQuery(AsyncIoRoot & aio, Strings opFlags, Strings opArgs)
case qOutputs: {
for (auto & i : opArgs) {
auto outputs = aio.blockOn(maybeUseOutputs(store->followLinksToStorePath(i), true, forceRealise));
auto outputs = maybeUseOutputs(store->followLinksToStorePath(i), true, forceRealise);
for (auto & outputPath : outputs)
cout << fmt("%1%\n", store->printStorePath(outputPath));
}
@@ -364,26 +341,23 @@ static void opQuery(AsyncIoRoot & aio, Strings opFlags, Strings opArgs)
case qReferrersClosure: {
StorePathSet paths;
for (auto & i : opArgs) {
auto ps = aio.blockOn(maybeUseOutputs(store->followLinksToStorePath(i), useOutput, forceRealise));
auto ps = maybeUseOutputs(store->followLinksToStorePath(i), useOutput, forceRealise);
for (auto & j : ps) {
if (query == qRequisites) {
aio.blockOn(store->computeFSClosure(j, paths, false, includeOutputs));
}
if (query == qRequisites) store->computeFSClosure(j, paths, false, includeOutputs);
else if (query == qReferences) {
for (auto & p : aio.blockOn(store->queryPathInfo(j))->references)
for (auto & p : store->queryPathInfo(j)->references)
paths.insert(p);
}
else if (query == qReferrers) {
StorePathSet tmp;
aio.blockOn(store->queryReferrers(j, tmp));
store->queryReferrers(j, tmp);
for (auto & i : tmp)
paths.insert(i);
}
else if (query == qReferrersClosure)
aio.blockOn(store->computeFSClosure(j, paths, true));
else if (query == qReferrersClosure) store->computeFSClosure(j, paths, true);
}
}
auto sorted = aio.blockOn(store->topoSortPaths(paths));
auto sorted = store->topoSortPaths(paths);
for (StorePaths::reverse_iterator i = sorted.rbegin();
i != sorted.rend(); ++i)
cout << fmt("%s\n", store->printStorePath(*i));
@@ -392,7 +366,7 @@ static void opQuery(AsyncIoRoot & aio, Strings opFlags, Strings opArgs)
case qDeriver:
for (auto & i : opArgs) {
auto info = aio.blockOn(store->queryPathInfo(store->followLinksToStorePath(i)));
auto info = store->queryPathInfo(store->followLinksToStorePath(i));
cout << fmt("%s\n", info->deriver ? store->printStorePath(*info->deriver) : "unknown-deriver");
}
break;
@@ -400,13 +374,12 @@ static void opQuery(AsyncIoRoot & aio, Strings opFlags, Strings opArgs)
case qValidDerivers: {
StorePathSet result;
for (auto & i : opArgs) {
auto derivers =
aio.blockOn(store->queryValidDerivers(store->followLinksToStorePath(i)));
for (const auto & i : derivers) {
auto derivers = store->queryValidDerivers(store->followLinksToStorePath(i));
for (const auto &i: derivers) {
result.insert(i);
}
}
auto sorted = aio.blockOn(store->topoSortPaths(result));
auto sorted = store->topoSortPaths(result);
for (StorePaths::reverse_iterator i = sorted.rbegin();
i != sorted.rend(); ++i)
cout << fmt("%s\n", store->printStorePath(*i));
@@ -415,8 +388,8 @@ static void opQuery(AsyncIoRoot & aio, Strings opFlags, Strings opArgs)
case qBinding:
for (auto & i : opArgs) {
auto path = aio.blockOn(useDeriver(store->followLinksToStorePath(i)));
Derivation drv = aio.blockOn(store->derivationFromPath(path));
auto path = useDeriver(store->followLinksToStorePath(i));
Derivation drv = store->derivationFromPath(path);
StringPairs::iterator j = drv.env.find(bindingName);
if (j == drv.env.end())
throw Error("derivation '%s' has no environment binding named '%s'",
@@ -428,8 +401,8 @@ static void opQuery(AsyncIoRoot & aio, Strings opFlags, Strings opArgs)
case qHash:
case qSize:
for (auto & i : opArgs) {
for (auto & j : aio.blockOn(maybeUseOutputs(store->followLinksToStorePath(i), useOutput, forceRealise))) {
auto info = aio.blockOn(store->queryPathInfo(j));
for (auto & j : maybeUseOutputs(store->followLinksToStorePath(i), useOutput, forceRealise)) {
auto info = store->queryPathInfo(j);
if (query == qHash) {
assert(info->narHash.type == HashType::SHA256);
cout << fmt("%s\n", info->narHash.to_string(Base::Base32, true));
@@ -442,25 +415,25 @@ static void opQuery(AsyncIoRoot & aio, Strings opFlags, Strings opArgs)
case qTree: {
StorePathSet done;
for (auto & i : opArgs)
printTree(aio, store->followLinksToStorePath(i), "", "", done);
printTree(store->followLinksToStorePath(i), "", "", done);
break;
}
case qGraph: {
StorePathSet roots;
for (auto & i : opArgs)
for (auto & j : aio.blockOn(maybeUseOutputs(store->followLinksToStorePath(i), useOutput, forceRealise)))
for (auto & j : maybeUseOutputs(store->followLinksToStorePath(i), useOutput, forceRealise))
roots.insert(j);
aio.blockOn(printDotGraph(ref<Store>::unsafeFromPtr(store), std::move(roots)));
printDotGraph(ref<Store>(store), std::move(roots));
break;
}
case qGraphML: {
StorePathSet roots;
for (auto & i : opArgs)
for (auto & j : aio.blockOn(maybeUseOutputs(store->followLinksToStorePath(i), useOutput, forceRealise)))
for (auto & j : maybeUseOutputs(store->followLinksToStorePath(i), useOutput, forceRealise))
roots.insert(j);
aio.blockOn(printGraphML(ref<Store>::unsafeFromPtr(store), std::move(roots)));
printGraphML(ref<Store>(store), std::move(roots));
break;
}
@@ -473,15 +446,15 @@ static void opQuery(AsyncIoRoot & aio, Strings opFlags, Strings opArgs)
case qRoots: {
StorePathSet args;
for (auto & i : opArgs)
for (auto & p : aio.blockOn(maybeUseOutputs(store->followLinksToStorePath(i), useOutput, forceRealise)))
for (auto & p : maybeUseOutputs(store->followLinksToStorePath(i), useOutput, forceRealise))
args.insert(p);
StorePathSet referrers;
aio.blockOn(store->computeFSClosure(
args, referrers, true, settings.gcKeepOutputs, settings.gcKeepDerivations));
store->computeFSClosure(
args, referrers, true, settings.gcKeepOutputs, settings.gcKeepDerivations);
auto & gcStore = require<GcStore>(*store);
Roots roots = aio.blockOn(gcStore.findRoots(false));
Roots roots = gcStore.findRoots(false);
for (auto & [target, links] : roots)
if (referrers.find(target) != referrers.end())
for (auto & link : links)
@@ -495,13 +468,13 @@ static void opQuery(AsyncIoRoot & aio, Strings opFlags, Strings opArgs)
}
static void opPrintEnv(AsyncIoRoot & aio, Strings opFlags, Strings opArgs)
static void opPrintEnv(Strings opFlags, Strings opArgs)
{
if (!opFlags.empty()) throw UsageError("unknown flag");
if (opArgs.size() != 1) throw UsageError("'--print-env' requires one derivation store path");
Path drvPath = opArgs.front();
Derivation drv = aio.blockOn(store->derivationFromPath(store->parseStorePath(drvPath)));
Derivation drv = store->derivationFromPath(store->parseStorePath(drvPath));
/* Print each environment variable in the derivation in a format
* that can be sourced by the shell. */
@@ -521,7 +494,7 @@ static void opPrintEnv(AsyncIoRoot & aio, Strings opFlags, Strings opArgs)
}
static void opReadLog(AsyncIoRoot & aio, Strings opFlags, Strings opArgs)
static void opReadLog(Strings opFlags, Strings opArgs)
{
if (!opFlags.empty()) throw UsageError("unknown flag");
@@ -531,7 +504,7 @@ static void opReadLog(AsyncIoRoot & aio, Strings opFlags, Strings opArgs)
for (auto & i : opArgs) {
auto path = logStore.followLinksToStorePath(i);
auto log = aio.blockOn(logStore.getBuildLog(path));
auto log = logStore.getBuildLog(path);
if (!log)
throw Error("build log of derivation '%s' is not available", logStore.printStorePath(path));
std::cout << *log;
@@ -539,23 +512,20 @@ static void opReadLog(AsyncIoRoot & aio, Strings opFlags, Strings opArgs)
}
static void opDumpDB(AsyncIoRoot & aio, Strings opFlags, Strings opArgs)
static void opDumpDB(Strings opFlags, Strings opArgs)
{
if (!opFlags.empty()) throw UsageError("unknown flag");
if (!opArgs.empty()) {
for (auto & i : opArgs) {
cout << aio.blockOn(
store->makeValidityRegistration({store->followLinksToStorePath(i)}, true, true)
);
}
for (auto & i : opArgs)
cout << store->makeValidityRegistration({store->followLinksToStorePath(i)}, true, true);
} else {
for (auto & i : aio.blockOn(store->queryAllValidPaths()))
cout << aio.blockOn(store->makeValidityRegistration({i}, true, true));
for (auto & i : store->queryAllValidPaths())
cout << store->makeValidityRegistration({i}, true, true);
}
}
static void registerValidity(AsyncIoRoot & aio, bool reregister, bool hashGiven, bool canonicalise)
static void registerValidity(bool reregister, bool hashGiven, bool canonicalise)
{
ValidPathInfos infos;
@@ -565,7 +535,7 @@ static void registerValidity(AsyncIoRoot & aio, bool reregister, bool hashGiven,
auto hashResultOpt = !hashGiven ? std::optional<HashResult> { {Hash::dummy, -1} } : std::nullopt;
auto info = decodeValidPathInfo(*store, cin, hashResultOpt);
if (!info) break;
if (!aio.blockOn(store->isValidPath(info->path)) || reregister) {
if (!store->isValidPath(info->path) || reregister) {
/* !!! races */
if (canonicalise)
canonicalisePathMetaData(store->printStorePath(info->path), {});
@@ -578,20 +548,20 @@ static void registerValidity(AsyncIoRoot & aio, bool reregister, bool hashGiven,
}
}
aio.blockOn(ensureLocalStore()->registerValidPaths(infos));
ensureLocalStore()->registerValidPaths(infos);
}
static void opLoadDB(AsyncIoRoot & aio, Strings opFlags, Strings opArgs)
static void opLoadDB(Strings opFlags, Strings opArgs)
{
if (!opFlags.empty()) throw UsageError("unknown flag");
if (!opArgs.empty())
throw UsageError("no arguments expected");
registerValidity(aio, true, true, false);
registerValidity(true, true, false);
}
static void opRegisterValidity(AsyncIoRoot & aio, Strings opFlags, Strings opArgs)
static void opRegisterValidity(Strings opFlags, Strings opArgs)
{
bool reregister = false; // !!! maybe this should be the default
bool hashGiven = false;
@@ -603,11 +573,11 @@ static void opRegisterValidity(AsyncIoRoot & aio, Strings opFlags, Strings opArg
if (!opArgs.empty()) throw UsageError("no arguments expected");
registerValidity(aio, reregister, hashGiven, true);
registerValidity(reregister, hashGiven, true);
}
static void opCheckValidity(AsyncIoRoot & aio, Strings opFlags, Strings opArgs)
static void opCheckValidity(Strings opFlags, Strings opArgs)
{
bool printInvalid = false;
@@ -617,7 +587,7 @@ static void opCheckValidity(AsyncIoRoot & aio, Strings opFlags, Strings opArgs)
for (auto & i : opArgs) {
auto path = store->followLinksToStorePath(i);
if (!aio.blockOn(store->isValidPath(path))) {
if (!store->isValidPath(path)) {
if (printInvalid)
cout << fmt("%s\n", store->printStorePath(path));
else
@@ -627,7 +597,7 @@ static void opCheckValidity(AsyncIoRoot & aio, Strings opFlags, Strings opArgs)
}
static void opGC(AsyncIoRoot & aio, Strings opFlags, Strings opArgs)
static void opGC(Strings opFlags, Strings opArgs)
{
bool printRoots = false;
GCOptions options;
@@ -649,7 +619,7 @@ static void opGC(AsyncIoRoot & aio, Strings opFlags, Strings opArgs)
auto & gcStore = require<GcStore>(*store);
if (printRoots) {
Roots roots = aio.blockOn(gcStore.findRoots(false));
Roots roots = gcStore.findRoots(false);
std::set<std::pair<Path, StorePath>> roots2;
// Transpose and sort the roots.
for (auto & [target, links] : roots)
@@ -661,7 +631,7 @@ static void opGC(AsyncIoRoot & aio, Strings opFlags, Strings opArgs)
else {
PrintFreed freed(options.action == GCOptions::gcDeleteDead, results);
aio.blockOn(gcStore.collectGarbage(options, results));
gcStore.collectGarbage(options, results);
if (options.action != GCOptions::gcDeleteDead)
for (auto & i : results.paths)
@@ -673,37 +643,28 @@ static void opGC(AsyncIoRoot & aio, Strings opFlags, Strings opArgs)
/* Remove paths from the Nix store if possible (i.e., if they do not
have any remaining referrers and are not reachable from any GC
roots). */
static void opDelete(AsyncIoRoot & aio, Strings opFlags, Strings opArgs)
static void opDelete(Strings opFlags, Strings opArgs)
{
GCOptions options;
options.action = GCOptions::gcDeleteSpecific;
bool deleteClosure = false;
for (auto & i : opFlags)
if (i == "--ignore-liveness") options.ignoreLiveness = true;
else if (i == "--skip-live") options.action = GCOptions::gcTryDeleteSpecific;
else if (i == "--delete-closure") deleteClosure = true;
else throw UsageError("unknown flag '%1%'", i);
for (auto & arg : opArgs) {
StorePath path = store->followLinksToStorePath(arg);
if (deleteClosure) {
aio.blockOn(store->computeFSClosure(path, options.pathsToDelete));
} else {
options.pathsToDelete.insert(path);
}
}
for (auto & i : opArgs)
options.pathsToDelete.insert(store->followLinksToStorePath(i));
auto & gcStore = require<GcStore>(*store);
GCResults results;
PrintFreed freed(true, results);
aio.blockOn(gcStore.collectGarbage(options, results));
gcStore.collectGarbage(options, results);
}
/* Dump a path as a Nix archive. The archive is written to stdout */
static void opDump(AsyncIoRoot & aio, Strings opFlags, Strings opArgs)
static void opDump(Strings opFlags, Strings opArgs)
{
if (!opFlags.empty()) throw UsageError("unknown flag");
if (opArgs.size() != 1) throw UsageError("only one argument allowed");
@@ -716,7 +677,7 @@ static void opDump(AsyncIoRoot & aio, Strings opFlags, Strings opArgs)
/* Restore a value from a Nix archive. The archive is read from stdin. */
static void opRestore(AsyncIoRoot & aio, Strings opFlags, Strings opArgs)
static void opRestore(Strings opFlags, Strings opArgs)
{
if (!opFlags.empty()) throw UsageError("unknown flag");
if (opArgs.size() != 1) throw UsageError("only one argument allowed");
@@ -726,7 +687,7 @@ static void opRestore(AsyncIoRoot & aio, Strings opFlags, Strings opArgs)
}
static void opExport(AsyncIoRoot & aio, Strings opFlags, Strings opArgs)
static void opExport(Strings opFlags, Strings opArgs)
{
for (auto & i : opFlags)
throw UsageError("unknown flag '%1%'", i);
@@ -737,12 +698,12 @@ static void opExport(AsyncIoRoot & aio, Strings opFlags, Strings opArgs)
paths.insert(store->followLinksToStorePath(i));
FdSink sink(STDOUT_FILENO);
aio.blockOn(store->exportPaths(paths, sink));
store->exportPaths(paths, sink);
sink.flush();
}
static void opImport(AsyncIoRoot & aio, Strings opFlags, Strings opArgs)
static void opImport(Strings opFlags, Strings opArgs)
{
for (auto & i : opFlags)
throw UsageError("unknown flag '%1%'", i);
@@ -750,7 +711,7 @@ static void opImport(AsyncIoRoot & aio, Strings opFlags, Strings opArgs)
if (!opArgs.empty()) throw UsageError("no arguments expected");
FdSource source(STDIN_FILENO);
auto paths = aio.blockOn(store->importPaths(source, NoCheckSigs));
auto paths = store->importPaths(source, NoCheckSigs);
for (auto & i : paths)
cout << fmt("%s\n", store->printStorePath(i)) << std::flush;
@@ -758,7 +719,7 @@ static void opImport(AsyncIoRoot & aio, Strings opFlags, Strings opArgs)
/* Initialise the Nix databases. */
static void opInit(AsyncIoRoot & aio, Strings opFlags, Strings opArgs)
static void opInit(Strings opFlags, Strings opArgs)
{
if (!opFlags.empty()) throw UsageError("unknown flag");
if (!opArgs.empty())
@@ -769,7 +730,7 @@ static void opInit(AsyncIoRoot & aio, Strings opFlags, Strings opArgs)
/* Verify the consistency of the Nix environment. */
static void opVerify(AsyncIoRoot & aio, Strings opFlags, Strings opArgs)
static void opVerify(Strings opFlags, Strings opArgs)
{
if (!opArgs.empty())
throw UsageError("no arguments expected");
@@ -782,7 +743,7 @@ static void opVerify(AsyncIoRoot & aio, Strings opFlags, Strings opArgs)
else if (i == "--repair") repair = Repair;
else throw UsageError("unknown flag '%1%'", i);
if (aio.blockOn(store->verifyStore(checkContents, repair))) {
if (store->verifyStore(checkContents, repair)) {
warn("not all store errors were fixed");
throw Exit(1);
}
@@ -790,7 +751,7 @@ static void opVerify(AsyncIoRoot & aio, Strings opFlags, Strings opArgs)
/* Verify whether the contents of the given store path have not changed. */
static void opVerifyPath(AsyncIoRoot & aio, Strings opFlags, Strings opArgs)
static void opVerifyPath(Strings opFlags, Strings opArgs)
{
if (!opFlags.empty())
throw UsageError("no flags expected");
@@ -800,15 +761,15 @@ static void opVerifyPath(AsyncIoRoot & aio, Strings opFlags, Strings opArgs)
for (auto & i : opArgs) {
auto path = store->followLinksToStorePath(i);
printMsg(lvlTalkative, "checking path '%s'...", store->printStorePath(path));
auto info = aio.blockOn(store->queryPathInfo(path));
auto info = store->queryPathInfo(path);
HashSink sink(info->narHash.type);
aio.blockOn(store->narFromPath(path))->drainInto(sink);
sink << store->narFromPath(path);
auto current = sink.finish();
if (current.first != info->narHash) {
printError("path '%s' was modified! expected hash '%s', got '%s'",
store->printStorePath(path),
info->narHash.to_string(Base::SRI, true),
current.first.to_string(Base::SRI, true));
info->narHash.to_string(Base::Base32, true),
current.first.to_string(Base::Base32, true));
status = 1;
}
}
@@ -819,27 +780,27 @@ static void opVerifyPath(AsyncIoRoot & aio, Strings opFlags, Strings opArgs)
/* Repair the contents of the given path by redownloading it using a
substituter (if available). */
static void opRepairPath(AsyncIoRoot & aio, Strings opFlags, Strings opArgs)
static void opRepairPath(Strings opFlags, Strings opArgs)
{
if (!opFlags.empty())
throw UsageError("no flags expected");
for (auto & i : opArgs)
aio.blockOn(store->repairPath(store->followLinksToStorePath(i)));
store->repairPath(store->followLinksToStorePath(i));
}
/* Optimise the disk space usage of the Nix store by hard-linking
files with the same contents. */
static void opOptimise(AsyncIoRoot & aio, Strings opFlags, Strings opArgs)
static void opOptimise(Strings opFlags, Strings opArgs)
{
if (!opArgs.empty() || !opFlags.empty())
throw UsageError("no arguments expected");
aio.blockOn(store->optimiseStore());
store->optimiseStore();
}
/* Serve the nix store in a way usable by a restricted ssh user. */
static void opServe(AsyncIoRoot & aio, Strings opFlags, Strings opArgs)
static void opServe(Strings opFlags, Strings opArgs)
{
bool writeAllowed = false;
for (auto & i : opFlags)
@@ -912,13 +873,13 @@ static void opServe(AsyncIoRoot & aio, Strings opFlags, Strings opArgs)
auto paths = ServeProto::Serialise<StorePathSet>::read(*store, rconn);
if (lock && writeAllowed)
for (auto & path : paths)
aio.blockOn(store->addTempRoot(path));
store->addTempRoot(path);
if (substitute && writeAllowed) {
aio.blockOn(store->substitutePaths(paths));
store->substitutePaths(paths);
}
auto valid = aio.blockOn(store->queryValidPaths(paths));
auto valid = store->queryValidPaths(paths);
out << ServeProto::write(*store, wconn, valid);
break;
}
@@ -928,7 +889,7 @@ static void opServe(AsyncIoRoot & aio, Strings opFlags, Strings opArgs)
// !!! Maybe we want a queryPathInfos?
for (auto & i : paths) {
try {
auto info = aio.blockOn(store->queryPathInfo(i));
auto info = store->queryPathInfo(i);
out << store->printStorePath(info->path);
out << ServeProto::write(*store, wconn, static_cast<const UnkeyedValidPathInfo &>(*info));
} catch (InvalidPath &) {
@@ -939,22 +900,19 @@ static void opServe(AsyncIoRoot & aio, Strings opFlags, Strings opArgs)
}
case ServeProto::Command::DumpStorePath:
aio.blockOn(store->narFromPath(store->parseStorePath(readString(in))))
->drainInto(out);
out << store->narFromPath(store->parseStorePath(readString(in)));
break;
case ServeProto::Command::ImportPaths: {
if (!writeAllowed) throw Error("importing paths is not allowed");
aio.blockOn(store->importPaths(in, NoCheckSigs)); // FIXME: should we skip sig checking?
store->importPaths(in, NoCheckSigs); // FIXME: should we skip sig checking?
out << 1; // indicate success
break;
}
case ServeProto::Command::ExportPaths: {
readInt(in); // obsolete
aio.blockOn(store->exportPaths(
ServeProto::Serialise<StorePathSet>::read(*store, rconn), out
));
store->exportPaths(ServeProto::Serialise<StorePathSet>::read(*store, rconn), out);
break;
}
@@ -970,7 +928,7 @@ static void opServe(AsyncIoRoot & aio, Strings opFlags, Strings opArgs)
try {
MonitorFdHup monitor(in.fd);
aio.blockOn(store->buildPaths(toDerivedPaths(paths)));
store->buildPaths(toDerivedPaths(paths));
out << 0;
} catch (Error & e) {
assert(e.info().status);
@@ -990,7 +948,7 @@ static void opServe(AsyncIoRoot & aio, Strings opFlags, Strings opArgs)
getBuildSettings();
MonitorFdHup monitor(in.fd);
auto status = aio.blockOn(store->buildDerivation(drvPath, drv));
auto status = store->buildDerivation(drvPath, drv);
out << ServeProto::write(*store, wconn, status);
break;
@@ -999,12 +957,8 @@ static void opServe(AsyncIoRoot & aio, Strings opFlags, Strings opArgs)
case ServeProto::Command::QueryClosure: {
bool includeOutputs = readInt(in);
StorePathSet closure;
aio.blockOn(store->computeFSClosure(
ServeProto::Serialise<StorePathSet>::read(*store, rconn),
closure,
false,
includeOutputs
));
store->computeFSClosure(ServeProto::Serialise<StorePathSet>::read(*store, rconn),
closure, false, includeOutputs);
out << ServeProto::write(*store, wconn, closure);
break;
}
@@ -1029,9 +983,8 @@ static void opServe(AsyncIoRoot & aio, Strings opFlags, Strings opArgs)
throw Error("narInfo is too old and missing the narSize field");
SizedSource sizedSource(in, info.narSize);
AsyncSourceInputStream stream{sizedSource};
aio.blockOn(store->addToStore(info, stream, NoRepair, NoCheckSigs));
store->addToStore(info, sizedSource, NoRepair, NoCheckSigs);
// consume all the data that has been sent before continuing.
sizedSource.drainAll();
@@ -1050,7 +1003,7 @@ static void opServe(AsyncIoRoot & aio, Strings opFlags, Strings opArgs)
}
static void opGenerateBinaryCacheKey(AsyncIoRoot & aio, Strings opFlags, Strings opArgs)
static void opGenerateBinaryCacheKey(Strings opFlags, Strings opArgs)
{
for (auto & i : opFlags)
throw UsageError("unknown flag '%1%'", i);
@@ -1069,7 +1022,7 @@ static void opGenerateBinaryCacheKey(AsyncIoRoot & aio, Strings opFlags, Strings
}
static void opVersion(AsyncIoRoot & aio, Strings opFlags, Strings opArgs)
static void opVersion(Strings opFlags, Strings opArgs)
{
printVersion("nix-store");
}
@@ -1078,7 +1031,7 @@ static void opVersion(AsyncIoRoot & aio, Strings opFlags, Strings opArgs)
/* Scan the arguments; find the operation, set global flags, put all
other flags in a list, and put all other arguments in another
list. */
static int main_nix_store(AsyncIoRoot & aio, std::string programName, Strings argv)
static int main_nix_store(std::string programName, Strings argv)
{
{
Strings opFlags, opArgs;
@@ -1087,7 +1040,7 @@ static int main_nix_store(AsyncIoRoot & aio, std::string programName, Strings ar
std::string opName;
bool showHelp = false;
LegacyArgs(aio, programName, [&](Strings::iterator & arg, const Strings::iterator & end) {
LegacyArgs(programName, [&](Strings::iterator & arg, const Strings::iterator & end) {
Operation oldOp = op;
if (*arg == "--help")
@@ -1215,16 +1168,16 @@ static int main_nix_store(AsyncIoRoot & aio, std::string programName, Strings ar
if (!op) throw UsageError("no operation specified");
if (op != opDump && op != opRestore) /* !!! hack */
store = aio.blockOn(openStore());
store = openStore();
op(aio, std::move(opFlags), std::move(opArgs));
op(std::move(opFlags), std::move(opArgs));
return 0;
}
}
void registerLegacyNixStore() {
LegacyCommandRegistry::add("nix-store", main_nix_store);
void registerNixStore() {
LegacyCommands::add("nix-store", main_nix_store);
}
}
+1 -1
View File
@@ -3,6 +3,6 @@
namespace nix {
void registerLegacyNixStore();
void registerNixStore();
}
+15 -12
View File
@@ -3,8 +3,10 @@
#include "lix/libstore/store-api.hh"
#include "lix/libstore/path-with-outputs.hh"
#include "lix/libstore/local-fs-store.hh"
#include "lix/libstore/globals.hh"
#include "lix/libmain/shared.hh"
#include "lix/libexpr/eval.hh"
#include "lix/libexpr/eval-inline.hh"
#include "lix/libstore/profiles.hh"
#include "lix/libexpr/print-ambiguous.hh"
@@ -26,9 +28,9 @@ bool createUserEnv(EvalState & state, DrvInfos & elems,
drvsToBuild.push_back({*drvPath});
debug("building user environment dependencies");
state.aio.blockOn(state.ctx.store->buildPaths(
state.ctx.store->buildPaths(
toDerivedPaths(drvsToBuild),
state.ctx.repair ? bmRepair : bmNormal));
state.ctx.repair ? bmRepair : bmNormal);
/* Construct the whole top level derivation. */
StorePathSet references;
@@ -64,8 +66,8 @@ bool createUserEnv(EvalState & state, DrvInfos & elems,
/* This is only necessary when installing store paths, e.g.,
`nix-env -i /nix/store/abcd...-foo'. */
state.aio.blockOn(state.ctx.store->addTempRoot(*j.second));
state.aio.blockOn(state.ctx.store->ensurePath(*j.second));
state.ctx.store->addTempRoot(*j.second);
state.ctx.store->ensurePath(*j.second);
references.insert(*j.second);
}
@@ -90,8 +92,8 @@ bool createUserEnv(EvalState & state, DrvInfos & elems,
environment. */
std::ostringstream str;
printAmbiguous(manifest, state.ctx.symbols, str, nullptr, std::numeric_limits<int>::max());
auto manifestFile = state.aio.blockOn(state.ctx.store->addTextToStore("env-manifest.nix",
str.str(), references));
auto manifestFile = state.ctx.store->addTextToStore("env-manifest.nix",
str.str(), references);
/* Get the environment builder expression. */
Value envBuilder;
@@ -112,7 +114,7 @@ bool createUserEnv(EvalState & state, DrvInfos & elems,
/* Evaluate it. */
debug("evaluating user environment builder");
state.forceValue(topLevel, noPos);
state.forceValue(topLevel, topLevel.determinePos(noPos));
NixStringContext context;
Attr & aDrvPath(*topLevel.attrs->find(state.ctx.s.drvPath));
auto topLevelDrv = state.coerceToStorePath(aDrvPath.pos, *aDrvPath.value, context, "");
@@ -123,15 +125,16 @@ bool createUserEnv(EvalState & state, DrvInfos & elems,
debug("building user environment");
std::vector<StorePathWithOutputs> topLevelDrvs;
topLevelDrvs.push_back({topLevelDrv});
state.aio.blockOn(state.ctx.store->buildPaths(
state.ctx.store->buildPaths(
toDerivedPaths(topLevelDrvs),
state.ctx.repair ? bmRepair : bmNormal));
state.ctx.repair ? bmRepair : bmNormal);
/* Switch the current user environment to the output path. */
auto store2 = state.ctx.store.try_cast_shared<LocalFSStore>();
auto store2 = state.ctx.store.dynamic_pointer_cast<LocalFSStore>();
if (store2) {
PathLock lock = lockProfile(profile);
PathLocks lock;
lockProfile(lock, profile);
Path lockTokenCur = optimisticLockProfile(profile);
if (lockToken != lockTokenCur) {
@@ -140,7 +143,7 @@ bool createUserEnv(EvalState & state, DrvInfos & elems,
}
debug("switching to new user environment");
Path generation = state.aio.blockOn(createGeneration(*store2, profile, topLevelOut));
Path generation = createGeneration(*store2, profile, topLevelOut);
switchLink(profile, generation);
}
+34 -60
View File
@@ -1,11 +1,10 @@
#include "lix/libcmd/built-path.hh"
#include "lix/libstore/derivations.hh"
#include "lix/libstore/store-api.hh"
#include "lix/libutil/async.hh"
#include "lix/libutil/json.hh"
#include "lix/libutil/result.hh"
#include <kj/async.h>
#include <nlohmann/json.hpp>
#include <optional>
namespace nix {
@@ -81,68 +80,49 @@ SingleDerivedPath SingleBuiltPath::discardOutputPath() const
);
}
kj::Promise<Result<JSON>> BuiltPath::Built::toJSON(const Store & store) const
try {
JSON res;
res["drvPath"] = TRY_AWAIT(drvPath->toJSON(store));
nlohmann::json BuiltPath::Built::toJSON(const Store & store) const
{
nlohmann::json res;
res["drvPath"] = drvPath->toJSON(store);
for (const auto & [outputName, outputPath] : outputs) {
res["outputs"][outputName] = store.printStorePath(outputPath);
}
co_return res;
} catch (...) {
co_return result::current_exception();
return res;
}
kj::Promise<Result<JSON>> SingleBuiltPath::Built::toJSON(const Store & store) const
try {
JSON res;
res["drvPath"] = TRY_AWAIT(drvPath->toJSON(store));
nlohmann::json SingleBuiltPath::Built::toJSON(const Store & store) const
{
nlohmann::json res;
res["drvPath"] = drvPath->toJSON(store);
auto & [outputName, outputPath] = output;
res["output"] = outputName;
res["outputPath"] = store.printStorePath(outputPath);
co_return res;
} catch (...) {
co_return result::current_exception();
return res;
}
kj::Promise<Result<JSON>> SingleBuiltPath::toJSON(const Store & store) const
try {
co_return TRY_AWAIT(std::visit([&](const auto & buildable) {
nlohmann::json SingleBuiltPath::toJSON(const Store & store) const
{
return std::visit([&](const auto & buildable) {
return buildable.toJSON(store);
}, raw()));
} catch (...) {
co_return result::current_exception();
}, raw());
}
kj::Promise<Result<JSON>> BuiltPath::toJSON(const Store & store) const
try {
co_return TRY_AWAIT(std::visit([&](const auto & buildable) {
nlohmann::json BuiltPath::toJSON(const Store & store) const
{
return std::visit([&](const auto & buildable) {
return buildable.toJSON(store);
}, raw()));
} catch (...) {
co_return result::current_exception();
}, raw());
}
kj::Promise<Result<RealisedPath::Set>> BuiltPath::toRealisedPaths(Store & store) const
try {
RealisedPath::Set BuiltPath::toRealisedPaths(Store & store) const
{
RealisedPath::Set res;
auto handlers = overloaded{
// NOLINTNEXTLINE(cppcoreguidelines-avoid-capturing-lambda-coroutines)
[&](const BuiltPath::Opaque & p) -> kj::Promise<Result<void>> {
try {
res.insert(p.path);
return {result::success()};
} catch (...) {
return {result::current_exception()};
}
},
// NOLINTNEXTLINE(cppcoreguidelines-avoid-capturing-lambda-coroutines)
[&](const BuiltPath::Built & p) -> kj::Promise<Result<void>> {
try {
auto drvHashes = TRY_AWAIT(
staticOutputHashes(store, TRY_AWAIT(store.readDerivation(p.drvPath->outPath())))
);
std::visit(
overloaded{
[&](const BuiltPath::Opaque & p) { res.insert(p.path); },
[&](const BuiltPath::Built & p) {
auto drvHashes =
staticOutputHashes(store, store.readDerivation(p.drvPath->outPath()));
for (auto& [outputName, outputPath] : p.outputs) {
if (experimentalFeatureSettings.isEnabled(
Xp::CaDerivations)) {
@@ -151,8 +131,8 @@ try {
throw Error(
"the derivation '%s' has unrealised output '%s' (derived-path.cc/toRealisedPaths)",
store.printStorePath(p.drvPath->outPath()), outputName);
auto thisRealisation = TRY_AWAIT(store.queryRealisation(
DrvOutput{*drvOutput, outputName}));
auto thisRealisation = store.queryRealisation(
DrvOutput{*drvOutput, outputName});
assert(thisRealisation); // Weve built it, so we must
// have the realisation
res.insert(*thisRealisation);
@@ -160,16 +140,10 @@ try {
res.insert(outputPath);
}
}
co_return result::success();
} catch (...) {
co_return result::current_exception();
}
},
},
};
TRY_AWAIT(std::visit(handlers, raw()));
co_return res;
} catch (...) {
co_return result::current_exception();
raw());
return res;
}
}
+5 -7
View File
@@ -2,8 +2,6 @@
///@file
#include "lix/libstore/derived-path.hh"
#include "lix/libstore/realisation.hh"
#include "lix/libutil/result.hh"
#include <kj/async.h>
namespace nix {
@@ -17,7 +15,7 @@ struct SingleBuiltPathBuilt {
std::string to_string(const Store & store) const;
static SingleBuiltPathBuilt parse(const Store & store, std::string_view, std::string_view);
kj::Promise<Result<JSON>> toJSON(const Store & store) const;
nlohmann::json toJSON(const Store & store) const;
DECLARE_CMP(SingleBuiltPathBuilt);
};
@@ -45,7 +43,7 @@ struct SingleBuiltPath : built_path::detail::SingleBuiltPathRaw {
SingleDerivedPath discardOutputPath() const;
static SingleBuiltPath parse(const Store & store, std::string_view);
kj::Promise<Result<JSON>> toJSON(const Store & store) const;
nlohmann::json toJSON(const Store & store) const;
};
static inline ref<SingleBuiltPath> staticDrv(StorePath drvPath)
@@ -64,7 +62,7 @@ struct BuiltPathBuilt {
std::string to_string(const Store & store) const;
static BuiltPathBuilt parse(const Store & store, std::string_view, std::string_view);
kj::Promise<Result<JSON>> toJSON(const Store & store) const;
nlohmann::json toJSON(const Store & store) const;
DECLARE_CMP(BuiltPathBuilt);
};
@@ -92,9 +90,9 @@ struct BuiltPath : built_path::detail::BuiltPathRaw {
}
StorePathSet outPaths() const;
kj::Promise<Result<RealisedPath::Set>> toRealisedPaths(Store & store) const;
RealisedPath::Set toRealisedPaths(Store & store) const;
kj::Promise<Result<JSON>> toJSON(const Store & store) const;
nlohmann::json toJSON(const Store & store) const;
};
typedef std::vector<BuiltPath> BuiltPaths;
+11 -14
View File
@@ -3,7 +3,6 @@
#include "lix/libcmd/cmd-profiles.hh"
#include "lix/libcmd/built-path.hh"
#include "lix/libstore/builtins/buildenv.hh"
#include "lix/libutil/async-io.hh"
#include "lix/libutil/logging.hh"
#include "lix/libstore/names.hh"
#include "lix/libstore/store-api.hh"
@@ -111,7 +110,7 @@ ProfileManifest::ProfileManifest(EvalState & state, const Path & profile)
auto manifestPath = profile + "/manifest.json";
if (pathExists(manifestPath)) {
auto json = json::parse(readFile(manifestPath), "a profile manifest");
auto json = nlohmann::json::parse(readFile(manifestPath));
auto version = json.value("version", 0);
std::string sUrl;
@@ -198,15 +197,15 @@ void ProfileManifest::addElement(ProfileElement element)
addElement(finalName, std::move(element));
}
JSON ProfileManifest::toJSON(Store & store) const
nlohmann::json ProfileManifest::toJSON(Store & store) const
{
auto es = JSON::object();
auto es = nlohmann::json::object();
for (auto & [name, element] : elements) {
auto paths = JSON::array();
auto paths = nlohmann::json::array();
for (auto & path : element.storePaths) {
paths.push_back(store.printStorePath(path));
}
JSON obj;
nlohmann::json obj;
obj["storePaths"] = paths;
obj["active"] = element.active;
obj["priority"] = element.priority;
@@ -218,14 +217,14 @@ JSON ProfileManifest::toJSON(Store & store) const
}
es[name] = obj;
}
JSON json;
nlohmann::json json;
json["version"] = 3;
json["elements"] = es;
return json;
}
kj::Promise<Result<StorePath>> ProfileManifest::build(ref<Store> store)
try {
StorePath ProfileManifest::build(ref<Store> store)
{
auto tempDir = createTempDir();
StorePathSet references;
@@ -267,12 +266,10 @@ try {
};
info.narSize = sink.s.size();
AsyncStringInputStream source(sink.s);
TRY_AWAIT(store->addToStore(info, source));
StringSource source(sink.s);
store->addToStore(info, source);
co_return std::move(info.path);
} catch (...) {
co_return result::current_exception();
return std::move(info.path);
}
void ProfileManifest::printDiff(
+6 -3
View File
@@ -6,11 +6,14 @@
#include "lix/libexpr/flake/flakeref.hh"
#include "lix/libexpr/get-drvs.hh"
#include "lix/libutil/types.hh"
#include "lix/libutil/json-fwd.hh"
#include "lix/libutil/url.hh"
#include "lix/libutil/url-name.hh"
#include <string>
#include <set>
#include <nlohmann/json.hpp>
namespace nix
{
@@ -59,9 +62,9 @@ struct ProfileManifest
ProfileManifest(EvalState & state, const Path & profile);
JSON toJSON(Store & store) const;
nlohmann::json toJSON(Store & store) const;
kj::Promise<Result<StorePath>> build(ref<Store> store);
StorePath build(ref<Store> store);
void addElement(std::string_view nameCandidate, ProfileElement element);
void addElement(ProfileElement element);
+24 -36
View File
@@ -2,34 +2,29 @@
#include "lix/libstore/store-api.hh"
#include "lix/libstore/local-fs-store.hh"
#include "lix/libstore/derivations.hh"
#include "lix/libexpr/nixexpr.hh"
#include "lix/libstore/profiles.hh"
#include "lix/libcmd/repl.hh"
#include <nlohmann/json.hpp>
extern char * * environ __attribute__((weak));
namespace nix {
CommandRegistry::CommandMap * CommandRegistry::commands = nullptr;
RegisterCommand::Commands * RegisterCommand::commands = nullptr;
nix::CommandMap CommandRegistry::getCommandsFor(const std::vector<std::string> & prefix)
nix::Commands RegisterCommand::getCommandsFor(const std::vector<std::string> & prefix)
{
if (!CommandRegistry::commands) {
CommandRegistry::commands = new CommandMap;
}
nix::CommandMap res;
for (auto & [name, command] : *CommandRegistry::commands) {
nix::Commands res;
for (auto & [name, command] : *RegisterCommand::commands)
if (name.size() == prefix.size() + 1) {
bool equal = true;
for (size_t i = 0; i < prefix.size(); ++i) {
if (name[i] != prefix[i]) {
equal = false;
}
}
if (equal) {
for (size_t i = 0; i < prefix.size(); ++i)
if (name[i] != prefix[i]) equal = false;
if (equal)
res.insert_or_assign(name[prefix.size()], command);
}
}
}
return res;
}
@@ -41,12 +36,12 @@ ref<Store> StoreCommand::getStore()
{
if (!_store)
_store = createStore();
return *_store;
return ref<Store>(_store);
}
ref<Store> StoreCommand::createStore()
{
return aio().blockOn(openStore());
return openStore();
}
void StoreCommand::run()
@@ -73,7 +68,7 @@ CopyCommand::CopyCommand()
ref<Store> CopyCommand::createStore()
{
return srcUri.empty() ? StoreCommand::createStore() : aio().blockOn(openStore(srcUri));
return srcUri.empty() ? StoreCommand::createStore() : openStore(srcUri);
}
ref<Store> CopyCommand::getDstStore()
@@ -81,7 +76,7 @@ ref<Store> CopyCommand::getDstStore()
if (srcUri.empty() && dstUri.empty())
throw UsageError("you must pass '--from' and/or '--to'");
return aio().blockOn(dstUri.empty() ? openStore() : openStore(dstUri));
return dstUri.empty() ? openStore() : openStore(dstUri);
}
EvalCommand::EvalCommand()
@@ -103,21 +98,21 @@ EvalCommand::~EvalCommand()
ref<Store> EvalCommand::getEvalStore()
{
if (!evalStore)
evalStore = evalStoreUrl ? aio().blockOn(openStore(*evalStoreUrl)) : getStore();
return *evalStore;
evalStore = evalStoreUrl ? openStore(*evalStoreUrl) : getStore();
return ref<Store>(evalStore);
}
ref<eval_cache::CachingEvaluator> EvalCommand::getEvaluator()
{
if (!evalState) {
evalState = std::allocate_shared<eval_cache::CachingEvaluator>(
TraceableAllocator<EvalState>(), aio(), searchPath, getEvalStore(), getStore(),
TraceableAllocator<EvalState>(), searchPath, getEvalStore(), getStore(),
startReplOnEvalErrors ? AbstractNixRepl::runSimple : nullptr
);
evalState->repair = repair;
}
return ref<eval_cache::CachingEvaluator>::unsafeFromPtr(evalState);
return ref<eval_cache::CachingEvaluator>(evalState);
}
MixOperateOnOptions::MixOperateOnOptions()
@@ -164,17 +159,10 @@ void BuiltPathsCommand::run(ref<Store> store, Installables && installables)
if (installables.size())
throw UsageError("'--all' does not expect arguments");
// XXX: Only uses opaque paths, ignores all the realisations
for (auto & p : aio().blockOn(store->queryAllValidPaths()))
for (auto & p : store->queryAllValidPaths())
paths.emplace_back(BuiltPath::Opaque{p});
} else {
paths = Installable::toBuiltPaths(
*getEvaluator()->begin(aio()),
getEvalStore(),
store,
realiseMode,
operateOn,
installables
);
paths = Installable::toBuiltPaths(*getEvaluator()->begin(), getEvalStore(), store, realiseMode, operateOn, installables);
if (recursive) {
// XXX: This only computes the store path closure, ignoring
// intermediate realisations
@@ -183,7 +171,7 @@ void BuiltPathsCommand::run(ref<Store> store, Installables && installables)
auto rootFromThis = root.outPaths();
pathsRoots.insert(rootFromThis.begin(), rootFromThis.end());
}
aio().blockOn(store->computeFSClosure(pathsRoots, pathsClosure));
store->computeFSClosure(pathsRoots, pathsClosure);
for (auto & path : pathsClosure)
paths.emplace_back(BuiltPath::Opaque{path});
}
@@ -204,7 +192,7 @@ void StorePathsCommand::run(ref<Store> store, BuiltPaths && paths)
for (auto & p : builtPath.outPaths())
storePaths.insert(p);
auto sorted = aio().blockOn(store->topoSortPaths(storePaths));
auto sorted = store->topoSortPaths(storePaths);
std::reverse(sorted.begin(), sorted.end());
run(store, std::move(sorted));
@@ -232,11 +220,11 @@ MixProfile::MixProfile()
void MixProfile::updateProfile(const StorePath & storePath)
{
if (!profile) return;
auto store = getStore().try_cast_shared<LocalFSStore>();
auto store = getStore().dynamic_pointer_cast<LocalFSStore>();
if (!store) throw Error("'--profile' is not supported for this Nix store");
auto profile2 = absPath(*profile);
switchLink(profile2,
aio().blockOn(createGeneration(*store, profile2, storePath)));
createGeneration(*store, profile2, storePath));
}
void MixProfile::updateProfile(const BuiltPaths & buildables)
+15 -48
View File
@@ -7,7 +7,6 @@
#include "lix/libcmd/common-eval-args.hh"
#include "lix/libstore/path.hh"
#include "lix/libexpr/flake/lockfile.hh"
#include "lix/libutil/async.hh"
#include <optional>
@@ -46,7 +45,7 @@ struct StoreCommand : virtual Command
virtual void run(ref<Store>) = 0;
private:
std::optional<ref<Store>> _store;
std::shared_ptr<Store> _store;
};
/**
@@ -81,7 +80,7 @@ struct EvalCommand : virtual StoreCommand, MixEvalArgs
virtual ref<eval_cache::CachingEvaluator> getEvaluator();
private:
std::optional<ref<Store>> evalStore;
std::shared_ptr<Store> evalStore;
std::shared_ptr<eval_cache::CachingEvaluator> evalState;
};
@@ -262,60 +261,31 @@ struct StorePathCommand : public StorePathsCommand
/**
* A helper class for registering \ref Command commands globally.
*/
struct CommandRegistry
struct RegisterCommand
{
using CommandMap = std::map<
std::vector<std::string>,
std::function<ref<Command>(AsyncIoRoot & aio)>
>;
static CommandMap * commands;
typedef std::map<std::vector<std::string>, std::function<ref<Command>()>> Commands;
static Commands * commands;
static void add(std::vector<std::string> && name,
std::function<ref<Command>(AsyncIoRoot & aio)> command)
RegisterCommand(std::vector<std::string> && name,
std::function<ref<Command>()> command)
{
if (!commands) {
commands = new CommandMap;
}
if (!commands) commands = new Commands;
commands->emplace(name, command);
}
static nix::CommandMap getCommandsFor(const std::vector<std::string> & prefix);
};
template<typename Base>
class MixAio : public Base
{
private:
AsyncIoRoot & aio_;
public:
template<typename... Args>
MixAio(AsyncIoRoot & aio, Args &&... args)
: Base(std::forward<Args>(args)...)
, aio_(aio)
{
}
AsyncIoRoot & aio() override
{
return aio_;
}
static nix::Commands getCommandsFor(const std::vector<std::string> & prefix);
};
template<class T>
static void registerCommand(const std::string & name)
static RegisterCommand registerCommand(const std::string & name)
{
CommandRegistry::add({name}, [](AsyncIoRoot & aio) {
return make_ref<MixAio<T>>(aio);
});
return RegisterCommand({name}, [](){ return make_ref<T>(); });
}
template<class T>
static void registerCommand2(std::vector<std::string> && name)
static RegisterCommand registerCommand2(std::vector<std::string> && name)
{
CommandRegistry::add(std::move(name), [](AsyncIoRoot & aio) {
return make_ref<MixAio<T>>(aio);
});
return RegisterCommand(std::move(name), [](){ return make_ref<T>(); });
}
struct MixProfile : virtual StoreCommand
@@ -360,9 +330,7 @@ void completeFlakeInputPath(
const std::vector<FlakeRef> & flakeRefs,
std::string_view prefix);
void completeFlakeRef(
AsyncIoRoot & aio, AddCompletions & completions, ref<Store> store, std::string_view prefix
);
void completeFlakeRef(AddCompletions & completions, ref<Store> store, std::string_view prefix);
void completeFlakeRefWithFragment(
AddCompletions & completions,
@@ -373,11 +341,10 @@ void completeFlakeRefWithFragment(
const Strings & defaultFlakeAttrPaths,
std::string_view prefix);
kj::Promise<Result<void>> printClosureDiff(
void printClosureDiff(
ref<Store> store,
const StorePath & beforePath,
const StorePath & afterPath,
bool json,
std::string_view indent);
}
+12 -19
View File
@@ -8,14 +8,12 @@
#include "lix/libexpr/flake/flakeref.hh"
#include "lix/libstore/store-api.hh"
#include "lix/libcmd/command.hh"
#include "lix/libutil/async.hh"
#include "lix/libutil/regex.hh"
#include <regex>
namespace nix {
static std::regex const identifierRegex = regex::parse("^[A-Za-z_][A-Za-z0-9_'-]*$");
static std::regex const identifierRegex("^[A-Za-z_][A-Za-z0-9_'-]*$");
static void warnInvalidNixIdentifier(const std::string & name)
{
std::smatch match;
@@ -156,7 +154,7 @@ MixEvalArgs::MixEvalArgs()
fetchers::overrideRegistry(from.input, to.input, extraAttrs);
}},
.completer = {[&](AddCompletions & completions, size_t, std::string_view prefix) {
completeFlakeRef(aio(), completions, aio().blockOn(openStore()), prefix);
completeFlakeRef(completions, openStore(), prefix);
}}
});
@@ -189,35 +187,30 @@ Bindings * MixEvalArgs::getAutoArgs(Evaluator & state)
return res.finish();
}
kj::Promise<Result<EvalPaths::PathResult<SourcePath, ThrownError>>>
lookupFileArg(Evaluator & state, std::string_view fileArg)
try {
SourcePath lookupFileArg(Evaluator & state, std::string_view fileArg)
{
if (EvalSettings::isPseudoUrl(fileArg)) {
auto const url = EvalSettings::resolvePseudoUrl(fileArg);
auto const downloaded = TRY_AWAIT(fetchers::downloadTarball(
auto const downloaded = fetchers::downloadTarball(
state.store,
url,
/* name */ "source",
/* locked */ false
));
);
StorePath const storePath = downloaded.tree.storePath;
co_return SourcePath(CanonPath(state.store->toRealPath(storePath)));
return CanonPath(state.store->toRealPath(storePath));
} else if (fileArg.starts_with("flake:")) {
experimentalFeatureSettings.require(Xp::Flakes);
static constexpr size_t FLAKE_LEN = std::string_view("flake:").size();
auto flakeRef = parseFlakeRef(std::string(fileArg.substr(FLAKE_LEN)), {}, true, false);
auto storePath = TRY_AWAIT(TRY_AWAIT(flakeRef.resolve(state.store)).fetchTree(state.store))
.first.storePath;
co_return SourcePath(CanonPath(state.store->toRealPath(storePath)));
} else if (fileArg.size() > 2 && fileArg.at(0) == '<' && fileArg.at(fileArg.size() - 1) == '>')
{
auto storePath = flakeRef.resolve(state.store).fetchTree(state.store).first.storePath;
return CanonPath(state.store->toRealPath(storePath));
} else if (fileArg.size() > 2 && fileArg.at(0) == '<' && fileArg.at(fileArg.size() - 1) == '>') {
Path p(fileArg.substr(1, fileArg.size() - 2));
co_return TRY_AWAIT(state.paths.findFile(p));
return state.paths.findFile(p);
} else {
co_return SourcePath(CanonPath::fromCwd(fileArg));
return CanonPath::fromCwd(fileArg);
}
} catch (...) {
co_return result::current_exception();
}
}
+1 -3
View File
@@ -1,7 +1,6 @@
#pragma once
///@file
#include "lix/libexpr/eval-error.hh"
#include "lix/libexpr/eval.hh"
#include "lix/libutil/args.hh"
#include "lix/libmain/common-args.hh"
@@ -50,7 +49,6 @@ private:
*
* @exception nix::ThrownError for failed search path lookup. Probably others.
*/
kj::Promise<Result<EvalPaths::PathResult<SourcePath, ThrownError>>>
lookupFileArg(Evaluator & state, std::string_view fileArg);
SourcePath lookupFileArg(Evaluator & state, std::string_view fileArg);
}
+2
View File
@@ -7,6 +7,8 @@
#include "lix/libexpr/get-drvs.hh"
#include "lix/libexpr/flake/flake.hh"
#include <nlohmann/json.hpp>
namespace nix {
InstallableAttrPath::InstallableAttrPath(
+2
View File
@@ -8,6 +8,8 @@
#include "lix/libcmd/common-eval-args.hh"
#include "lix/libexpr/eval.hh"
#include <nlohmann/json.hpp>
namespace nix {
class InstallableAttrPath : public InstallableValue
+13
View File
@@ -1,11 +1,24 @@
#include "lix/libstore/globals.hh"
#include "lix/libcmd/installable-flake.hh"
#include "lix/libcmd/installable-derived-path.hh"
#include "lix/libstore/outputs-spec.hh"
#include "lix/libcmd/command.hh"
#include "lix/libexpr/attr-path.hh"
#include "lix/libcmd/common-eval-args.hh"
#include "lix/libstore/derivations.hh"
#include "lix/libexpr/eval-inline.hh"
#include "lix/libexpr/eval.hh"
#include "lix/libexpr/get-drvs.hh"
#include "lix/libstore/store-api.hh"
#include "lix/libmain/shared.hh"
#include "lix/libexpr/flake/flake.hh"
#include "lix/libexpr/eval-cache.hh"
#include "lix/libutil/url.hh"
#include "lix/libfetchers/registry.hh"
#include "lix/libstore/build-result.hh"
#include <regex>
#include <queue>
#include <nlohmann/json.hpp>
+3 -7
View File
@@ -1,7 +1,6 @@
#include "lix/libcmd/installable-value.hh"
#include "lix/libexpr/eval-cache.hh"
#include "lix/libfetchers/fetch-to-store.hh"
#include "lix/libutil/archive.hh"
namespace nix {
@@ -37,10 +36,10 @@ InstallableValue & InstallableValue::require(Installable & installable)
ref<InstallableValue> InstallableValue::require(ref<Installable> installable)
{
auto castedInstallable = installable.try_cast<InstallableValue>();
auto castedInstallable = installable.dynamic_pointer_cast<InstallableValue>();
if (!castedInstallable)
throw nonValueInstallable(*installable);
return *castedInstallable;
return ref { castedInstallable };
}
std::optional<DerivedPathWithInfo> InstallableValue::trySinglePathToDerivedPaths(
@@ -48,10 +47,7 @@ std::optional<DerivedPathWithInfo> InstallableValue::trySinglePathToDerivedPaths
)
{
if (v.type() == nPath) {
auto storePath = state.aio.blockOn(fetchToStoreRecursive(
*evaluator->store,
*prepareDump(state.ctx.paths.checkSourcePath(v.path()).canonical().abs())
));
auto storePath = fetchToStore(*evaluator->store, state.ctx.paths.checkSourcePath(v.path()));
return {{
.path = DerivedPath::Opaque {
.path = std::move(storePath),
+46 -59
View File
@@ -3,7 +3,6 @@
#include "lix/libcmd/installable-derived-path.hh"
#include "lix/libcmd/installable-attr-path.hh"
#include "lix/libcmd/installable-flake.hh"
#include "lix/libutil/async.hh"
#include "lix/libutil/logging.hh"
#include "lix/libstore/outputs-spec.hh"
#include "lix/libcmd/command.hh"
@@ -20,6 +19,8 @@
#include "lix/libstore/build-result.hh"
#include <nlohmann/json.hpp>
namespace nix {
void completeFlakeInputPath(
@@ -85,11 +86,9 @@ MixFlakeOptions::MixFlakeOptions()
}},
.completer = {[&](AddCompletions & completions, size_t n, std::string_view prefix) {
if (n == 0) {
completeFlakeInputPath(
completions, *getEvaluator()->begin(aio()), getFlakeRefsForCompletion(), prefix
);
completeFlakeInputPath(completions, *getEvaluator()->begin(), getFlakeRefsForCompletion(), prefix);
} else if (n == 1) {
completeFlakeRef(aio(), completions, getEvaluator()->store, prefix);
completeFlakeRef(completions, getEvaluator()->store, prefix);
}
}}
});
@@ -122,7 +121,7 @@ MixFlakeOptions::MixFlakeOptions()
.category = category,
.labels = {"flake-url"},
.handler = {[&](std::string flakeRef) {
auto evalState = getEvaluator()->begin(aio());
auto evalState = getEvaluator()->begin();
auto flake = flake::lockFlake(
*evalState,
parseFlakeRef(flakeRef, absPath(".")),
@@ -138,7 +137,7 @@ MixFlakeOptions::MixFlakeOptions()
}
}},
.completer = {[&](AddCompletions & completions, size_t, std::string_view prefix) {
completeFlakeRef(aio(), completions, getEvaluator()->store, prefix);
completeFlakeRef(completions, getEvaluator()->store, prefix);
}}
});
}
@@ -183,8 +182,8 @@ MixReadOnlyOption::MixReadOnlyOption()
Strings SourceExprCommand::getDefaultFlakeAttrPaths()
{
return {
"packages." + evalSettings.getCurrentSystem() + ".default",
"defaultPackage." + evalSettings.getCurrentSystem()
"packages." + settings.thisSystem.get() + ".default",
"defaultPackage." + settings.thisSystem.get()
};
}
@@ -193,17 +192,17 @@ Strings SourceExprCommand::getDefaultFlakeAttrPathPrefixes()
return {
// As a convenience, look for the attribute in
// 'outputs.packages'.
"packages." + evalSettings.getCurrentSystem() + ".",
"packages." + settings.thisSystem.get() + ".",
// As a temporary hack until Nixpkgs is properly converted
// to provide a clean 'packages' set, look in 'legacyPackages'.
"legacyPackages." + evalSettings.getCurrentSystem() + "."
"legacyPackages." + settings.thisSystem.get() + "."
};
}
Args::CompleterClosure SourceExprCommand::getCompleteInstallable()
{
return [this](AddCompletions & completions, size_t, std::string_view prefix) {
completeInstallable(*getEvaluator()->begin(aio()), completions, prefix);
completeInstallable(*getEvaluator()->begin(), completions, prefix);
};
}
@@ -215,9 +214,9 @@ void SourceExprCommand::completeInstallable(EvalState & state, AddCompletions &
auto evaluator = getEvaluator();
Expr & e = evaluator->parseExprFromFile(state.ctx.paths.resolveExprPath(
state.aio.blockOn(lookupFileArg(*evaluator, *file)).unwrap()
));
Expr & e = evaluator->parseExprFromFile(
state.ctx.paths.resolveExprPath(lookupFileArg(*evaluator, *file))
);
Value root;
state.eval(e, root);
@@ -239,7 +238,7 @@ void SourceExprCommand::completeInstallable(EvalState & state, AddCompletions &
Value &v1(*v);
state.forceValue(v1, pos);
Value v2;
state.autoCallFunction(*autoArgs, v1, v2, pos);
state.autoCallFunction(*autoArgs, v1, v2);
if (v2.type() == nAttrs) {
for (auto & i : *v2.attrs) {
@@ -281,7 +280,7 @@ void completeFlakeRefWithFragment(
try {
auto hash = prefix.find('#');
if (hash == std::string::npos) {
completeFlakeRef(evalState.aio, completions, evaluator->store, prefix);
completeFlakeRef(completions, evaluator->store, prefix);
} else {
completions.setType(AddCompletions::Type::Attrs);
@@ -348,9 +347,7 @@ void completeFlakeRefWithFragment(
}
}
void completeFlakeRef(
AsyncIoRoot & aio, AddCompletions & completions, ref<Store> store, std::string_view prefix
)
void completeFlakeRef(AddCompletions & completions, ref<Store> store, std::string_view prefix)
{
if (!experimentalFeatureSettings.isEnabled(Xp::Flakes))
return;
@@ -361,7 +358,7 @@ void completeFlakeRef(
Args::completeDir(completions, 0, prefix);
/* Look for registry entries that match the prefix. */
for (auto & registry : aio.blockOn(fetchers::getRegistries(store))) {
for (auto & registry : fetchers::getRegistries(store)) {
for (auto & entry : registry->entries) {
auto from = entry.from.to_string();
if (!prefix.starts_with("flake:") && from.starts_with("flake:")) {
@@ -384,18 +381,16 @@ DerivedPathWithInfo Installable::toDerivedPath(EvalState & state)
return std::move(buildables[0]);
}
static kj::Promise<Result<StorePath>> getDeriver(
static StorePath getDeriver(
ref<Store> store,
const Installable & i,
const StorePath & drvPath)
try {
auto derivers = TRY_AWAIT(store->queryValidDerivers(drvPath));
{
auto derivers = store->queryValidDerivers(drvPath);
if (derivers.empty())
throw Error("'%s' does not have a known deriver", i.what());
// FIXME: use all derivers?
co_return *derivers.begin();
} catch (...) {
co_return result::current_exception();
return *derivers.begin();
}
ref<eval_cache::EvalCache> openEvalCache(
@@ -457,7 +452,7 @@ Installables SourceExprCommand::parseInstallables(
state.eval(e, *vFile);
}
else if (file)
state.evalFile(state.aio.blockOn(lookupFileArg(*evaluator, *file)).unwrap(), *vFile);
state.evalFile(lookupFileArg(*evaluator, *file), *vFile);
else {
auto & e = evaluator->parseExprFromString(*expr, CanonPath::fromCwd());
state.eval(e, *vFile);
@@ -524,34 +519,28 @@ ref<Installable> SourceExprCommand::parseInstallable(
return installables.front();
}
static kj::Promise<Result<SingleBuiltPath>> getBuiltPath(ref<Store> evalStore, ref<Store> store, const SingleDerivedPath & b)
try {
auto handlers = overloaded{
[&](const SingleDerivedPath::Opaque & bo) -> kj::Promise<Result<SingleBuiltPath>> {
return {SingleBuiltPath::Opaque { bo.path }};
},
// NOLINTNEXTLINE(cppcoreguidelines-avoid-capturing-lambda-coroutines)
[&](const SingleDerivedPath::Built & bfd) -> kj::Promise<Result<SingleBuiltPath>> {
try {
auto drvPath = TRY_AWAIT(getBuiltPath(evalStore, store, *bfd.drvPath));
static SingleBuiltPath getBuiltPath(ref<Store> evalStore, ref<Store> store, const SingleDerivedPath & b)
{
return std::visit(
overloaded{
[&](const SingleDerivedPath::Opaque & bo) -> SingleBuiltPath {
return SingleBuiltPath::Opaque { bo.path };
},
[&](const SingleDerivedPath::Built & bfd) -> SingleBuiltPath {
auto drvPath = getBuiltPath(evalStore, store, *bfd.drvPath);
// Resolving this instead of `bfd` will yield the same result, but avoid duplicative work.
SingleDerivedPath::Built truncatedBfd {
.drvPath = makeConstantStorePathRef(drvPath.outPath()),
.output = bfd.output,
};
auto outputPath = TRY_AWAIT(resolveDerivedPath(*store, truncatedBfd, &*evalStore));
co_return SingleBuiltPath::Built {
auto outputPath = resolveDerivedPath(*store, truncatedBfd, &*evalStore);
return SingleBuiltPath::Built {
.drvPath = make_ref<SingleBuiltPath>(std::move(drvPath)),
.output = { bfd.output, outputPath },
};
} catch (...) {
co_return result::current_exception();
}
},
},
};
co_return TRY_AWAIT(std::visit(handlers, b.raw()));
} catch (...) {
co_return result::current_exception();
b.raw());
}
std::vector<BuiltPathWithResult> Installable::build(
@@ -632,17 +621,16 @@ std::vector<std::pair<ref<Installable>, BuiltPathWithResult>> Installable::build
case Realise::Nothing:
case Realise::Derivation:
state.aio.blockOn(printMissing(store, pathsToBuild, lvlError));
printMissing(store, pathsToBuild, lvlError);
for (auto & path : pathsToBuild) {
for (auto & aux : backmap[path]) {
std::visit(overloaded {
[&](const DerivedPath::Built & bfd) {
auto outputs =
state.aio.blockOn(resolveDerivedPath(*store, bfd, &*evalStore));
auto outputs = resolveDerivedPath(*store, bfd, &*evalStore);
res.push_back({aux.installable, {
.path = BuiltPath::Built {
.drvPath = make_ref<SingleBuiltPath>(state.aio.blockOn(getBuiltPath(evalStore, store, *bfd.drvPath))),
.drvPath = make_ref<SingleBuiltPath>(getBuiltPath(evalStore, store, *bfd.drvPath)),
.outputs = outputs,
},
.info = aux.info}});
@@ -660,10 +648,9 @@ std::vector<std::pair<ref<Installable>, BuiltPathWithResult>> Installable::build
case Realise::Outputs: {
if (settings.printMissing)
state.aio.blockOn(printMissing(store, pathsToBuild, lvlInfo));
printMissing(store, pathsToBuild, lvlInfo);
auto buildResults =
state.aio.blockOn(store->buildPathsWithResults(pathsToBuild, bMode, evalStore));
auto buildResults = store->buildPathsWithResults(pathsToBuild, bMode, evalStore);
throwBuildErrors(buildResults, *store);
for (auto & buildResult : buildResults) {
for (auto & aux : backmap[buildResult.path]) {
@@ -674,7 +661,7 @@ std::vector<std::pair<ref<Installable>, BuiltPathWithResult>> Installable::build
outputs.emplace(outputName, realisation.outPath);
res.push_back({aux.installable, {
.path = BuiltPath::Built {
.drvPath = make_ref<SingleBuiltPath>(state.aio.blockOn(getBuiltPath(evalStore, store, *bfd.drvPath))),
.drvPath = make_ref<SingleBuiltPath>(getBuiltPath(evalStore, store, *bfd.drvPath)),
.outputs = outputs,
},
.info = aux.info,
@@ -785,11 +772,11 @@ StorePathSet Installable::toDerivations(
bo.path.isDerivation()
? bo.path
: useDeriver
? state.aio.blockOn(getDeriver(store, *i, bo.path))
? getDeriver(store, *i, bo.path)
: throw Error("argument '%s' did not evaluate to a derivation", i->what()));
},
[&](const DerivedPath::Built & bfd) {
drvPaths.insert(state.aio.blockOn(resolveDerivedPath(*store, *bfd.drvPath)));
drvPaths.insert(resolveDerivedPath(*store, *bfd.drvPath));
},
}, b.path.raw());
@@ -855,7 +842,7 @@ std::vector<FlakeRef> InstallableCommand::getFlakeRefsForCompletion()
void InstallablesCommand::run(ref<Store> store, std::vector<std::string> && rawInstallables)
{
auto installables = parseInstallables(*getEvaluator()->begin(aio()), store, rawInstallables);
auto installables = parseInstallables(*getEvaluator()->begin(), store, rawInstallables);
run(store, std::move(installables));
}
@@ -872,7 +859,7 @@ InstallableCommand::InstallableCommand()
void InstallableCommand::run(ref<Store> store)
{
auto installable = parseInstallable(*getEvaluator()->begin(aio()), store, _installable);
auto installable = parseInstallable(*getEvaluator()->begin(), store, _installable);
run(store, std::move(installable));
}
+1
View File
@@ -3,6 +3,7 @@
#include "lix/libexpr/eval.hh"
#include "lix/libstore/path.hh"
#include "lix/libstore/outputs-spec.hh"
#include "lix/libstore/derived-path.hh"
#include "lix/libcmd/built-path.hh"
#include "lix/libstore/store-api.hh"
+1 -1
View File
@@ -2,6 +2,6 @@
namespace nix {
LegacyCommandRegistry::LegacyCommandMap * LegacyCommandRegistry::commands = 0;
LegacyCommands::Commands * LegacyCommands::commands = 0;
}
+5 -6
View File
@@ -1,7 +1,6 @@
#pragma once
///@file
#include "lix/libutil/async.hh"
#include <functional>
#include <list>
#include <map>
@@ -9,16 +8,16 @@
namespace nix {
typedef std::function<void(AsyncIoRoot &, std::string, std::list<std::string>)> MainFunction;
typedef std::function<void(std::string, std::list<std::string>)> MainFunction;
struct LegacyCommandRegistry
struct LegacyCommands
{
using LegacyCommandMap = std::map<std::string, MainFunction>;
static LegacyCommandMap * commands;
typedef std::map<std::string, MainFunction> Commands;
static Commands * commands;
static void add(const std::string & name, MainFunction fun)
{
if (!commands) commands = new LegacyCommandMap;
if (!commands) commands = new Commands;
(*commands)[name] = fun;
}
};
+1 -4
View File
@@ -21,9 +21,6 @@ std::string renderMarkdownToTerminal(std::string_view markdown)
.feat = LOWDOWN_COMMONMARK | LOWDOWN_FENCED | LOWDOWN_DEFLIST | LOWDOWN_TABLES,
.oflags = LOWDOWN_TERM_NOLINK,
};
if (!shouldANSI()) {
opts.oflags |= LOWDOWN_TERM_NOANSI;
}
auto doc = lowdown_doc_new(&opts);
if (!doc)
@@ -50,7 +47,7 @@ std::string renderMarkdownToTerminal(std::string_view markdown)
if (!rndr_res)
throw Error("allocation error while rendering Markdown");
return std::string(buf->data, buf->size);
return filterANSIEscapes(std::string(buf->data, buf->size), !shouldANSI());
}
}
+1 -2
View File
@@ -46,12 +46,12 @@ libcmd = library(
liblixexpr,
liblixfetchers,
liblixmain,
boehm,
ncurses,
editline,
lowdown,
nlohmann_json,
liblix_doc,
kj,
],
# '../..' for self references like "lix/libcmd/*.hh"
include_directories : [ '../..' ],
@@ -74,7 +74,6 @@ liblixcmd = declare_dependency(
include_directories : include_directories('../..'),
link_with : libcmd,
)
meson.override_dependency('lix-cmd', liblixcmd)
# FIXME: not using the pkg-config module because it creates way too many deps
# while meson migration is in progress, and we want to not include boost here
+95 -127
View File
@@ -12,7 +12,10 @@
#include "lix/libutil/ansicolor.hh"
#include "lix/libmain/shared.hh"
#include "lix/libutil/escape-string.hh"
#include "lix/libexpr/eval.hh"
#include "lix/libexpr/eval-cache.hh"
#include "lix/libexpr/eval-inline.hh"
#include "lix/libexpr/eval-settings.hh"
#include "lix/libexpr/attr-path.hh"
#include "lix/libutil/signals.hh"
@@ -31,7 +34,6 @@
#include "lix/libutil/signals.hh"
#include "lix/libexpr/print.hh"
#include "lix/libexpr/gc-small-vector.hh"
#include "lix/libutil/types.hh"
#include "lix/libutil/users.hh"
#if HAVE_BOEHMGC
@@ -153,15 +155,9 @@ struct NixRepl
void loadFlake(const std::string & flakeRef);
void loadFiles();
void reloadFiles();
template<typename T, typename NameFn, typename ValueFn>
void addToScope(T && things, NameFn nameFn, ValueFn valueFn);
void addAttrsToScope(Value & attrs);
void addValMapToScope(const ValMap & attrs);
void addVarToScope(const Symbol name, Value & v);
Expr & parseString(std::string s);
std::variant<std::unique_ptr<Expr>, ExprReplBindings> parseReplString(std::string s);
void evalString(std::string s, Value & v);
void loadDebugTraceEnv(const DebugTrace & dt);
@@ -248,7 +244,7 @@ NixRepl::NixRepl(const SearchPath & searchPath, nix::ref<Store> store, EvalState
void runNix(Path program, const Strings & args)
{
auto subprocessEnv = getEnv();
subprocessEnv["NIX_CONFIG"] = globalConfig.toKeyValue(true);
subprocessEnv["NIX_CONFIG"] = globalConfig.toKeyValue();
runProgram2(RunOptions {
.program = settings.nixBinDir+ "/" + program,
@@ -475,6 +471,23 @@ StringSet NixRepl::completePrefix(const std::string &prefix)
return completions;
}
// FIXME: DRY and match or use the parser
static bool isVarName(std::string_view s)
{
if (s.size() == 0) return false;
char c = s[0];
if ((c >= '0' && c <= '9') || c == '-' || c == '\'') return false;
for (auto & i : s)
if (!((i >= 'a' && i <= 'z') ||
(i >= 'A' && i <= 'Z') ||
(i >= '0' && i <= '9') ||
i == '_' || i == '-' || i == '\''))
return false;
return true;
}
StorePath NixRepl::getDerivationPath(Value & v) {
auto drvInfo = getDerivation(state, v, false);
if (!drvInfo)
@@ -482,7 +495,7 @@ StorePath NixRepl::getDerivationPath(Value & v) {
auto drvPath = drvInfo->queryDrvPath(state);
if (!drvPath)
throw Error("expression did not evaluate to a valid derivation (no 'drvPath' attribute)");
if (!state.aio.blockOn(evaluator.store->isValidPath(*drvPath)))
if (!evaluator.store->isValidPath(*drvPath))
throw Error("expression evaluated to invalid derivation '%s'", evaluator.store->printStorePath(*drvPath));
return *drvPath;
}
@@ -496,7 +509,8 @@ void NixRepl::loadDebugTraceEnv(const DebugTrace & dt)
auto vm = mapStaticEnvBindings(evaluator.symbols, *se.get(), dt.env);
// add staticenv vars.
addValMapToScope(*vm);
for (auto & [name, value] : *(vm.get()))
addVarToScope(evaluator.symbols.create(name), *value);
}
}
@@ -516,8 +530,6 @@ ProcessLineResult NixRepl::processLine(std::string line)
arg = line;
}
bool inDebugger = evaluator.debug && evaluator.debug->inDebugger;
if (command == ":?" || command == ":help") {
// FIXME: convert to Markdown, include in the 'nix repl' manpage.
std::cout
@@ -530,7 +542,6 @@ ProcessLineResult NixRepl::processLine(std::string line)
<< " :bl <expr> Build a derivation, creating GC roots in the\n"
<< " working directory\n"
<< " :e, :edit <expr> Open package or function in $EDITOR\n"
<< " :env Show env stack\n"
<< " :i <expr> Build derivation, then install result into\n"
<< " current profile\n"
<< " :l, :load <path> Load Nix expression and add it to scope\n"
@@ -549,10 +560,11 @@ ProcessLineResult NixRepl::processLine(std::string line)
<< " errors\n"
<< " :?, :help Brings up this help menu\n"
;
if (inDebugger) {
if (evaluator.debug && evaluator.debug->inDebugger) {
std::cout
<< "\n"
<< " Debug mode commands\n"
<< " :env Show env stack\n"
<< " :bt, :backtrace Show trace stack\n"
<< " :st Show current trace\n"
<< " :st <idx> Change to another trace in the stack\n"
@@ -563,9 +575,7 @@ ProcessLineResult NixRepl::processLine(std::string line)
}
else if (command == ":bt" || command == ":backtrace") {
if (!inDebugger)
throw Error("backtrace command is only available in debug mode (see %s)", "--debugger");
else if (evaluator.debug && evaluator.debug->inDebugger && (command == ":bt" || command == ":backtrace")) {
auto traces = evaluator.debug->traces();
for (const auto & [idx, i] : enumerate(traces)) {
std::cout << "\n" << ANSI_BLUE << idx << ANSI_NORMAL << ": ";
@@ -573,23 +583,17 @@ ProcessLineResult NixRepl::processLine(std::string line)
}
}
else if (command == ":env") {
if (inDebugger) {
auto traces = evaluator.debug->traces();
for (const auto & [idx, i] : enumerate(traces)) {
if (idx == debugTraceIndex) {
printEnvBindings(state, i->expr, i->env);
break;
}
else if (evaluator.debug && evaluator.debug->inDebugger && (command == ":env")) {
auto traces = evaluator.debug->traces();
for (const auto & [idx, i] : enumerate(traces)) {
if (idx == debugTraceIndex) {
printEnvBindings(state, i->expr, i->env);
break;
}
} else {
printEnvBindings(state.ctx.symbols, *staticEnv, *env, 0);
}
}
else if (command == ":st") {
if (!inDebugger)
throw Error("trace command is only available in debug mode (see %s)", "--debugger");
else if (evaluator.debug && evaluator.debug->inDebugger && (command == ":st")) {
try {
// change the DebugTrace index.
debugTraceIndex = stoi(arg);
@@ -608,17 +612,13 @@ ProcessLineResult NixRepl::processLine(std::string line)
}
}
else if (command == ":s" || command == ":step") {
if (!inDebugger)
throw Error("step command is only available in debug mode (see %s)", "--debugger");
else if (evaluator.debug && evaluator.debug->inDebugger && (command == ":s" || command == ":step")) {
// set flag to stop at next DebugTrace; exit repl.
evaluator.debug->stop = true;
return ProcessLineResult::Continue;
}
else if (command == ":c" || command == ":continue") {
if (!inDebugger)
throw Error("continue command is only available in debug mode (see %s)", "--debugger");
else if (evaluator.debug && evaluator.debug->inDebugger && (command == ":c" || command == ":continue")) {
// set flag to run to next breakpoint or end of program; exit repl.
evaluator.debug->stop = false;
return ProcessLineResult::Continue;
@@ -699,8 +699,6 @@ ProcessLineResult NixRepl::processLine(std::string line)
}
else if (command == ":log") {
if (arg.empty())
throw Error("cannot use ':log' without a specifying a derivation");
StorePath drvPath = ([&] {
auto maybeDrvPath = evaluator.store->maybeParseStorePath(arg);
if (maybeDrvPath && maybeDrvPath->isDerivation()) {
@@ -717,7 +715,7 @@ ProcessLineResult NixRepl::processLine(std::string line)
Finally roModeReset([&]() {
settings.readOnlyMode = false;
});
auto subs = state.aio.blockOn(getDefaultSubstituters());
auto subs = getDefaultSubstituters();
subs.push_front(evaluator.store);
@@ -731,7 +729,7 @@ ProcessLineResult NixRepl::processLine(std::string line)
}
auto & logSub = *logSubP;
auto log = state.aio.blockOn(logSub.getBuildLog(drvPath));
auto log = logSub.getBuildLog(drvPath);
if (log) {
printInfo("got build log for '%s' from '%s'", drvPathRaw, logSub.getUri());
logger->writeToStdout(*log);
@@ -758,21 +756,19 @@ ProcessLineResult NixRepl::processLine(std::string line)
logger->pause();
});
state.aio.blockOn(evaluator.store->buildPaths({
evaluator.store->buildPaths({
DerivedPath::Built {
.drvPath = makeConstantStorePathRef(drvPath),
.outputs = OutputsSpec::All { },
},
}));
auto drv = state.aio.blockOn(evaluator.store->readDerivation(drvPath));
});
auto drv = evaluator.store->readDerivation(drvPath);
logger->cout("\nThis derivation produced the following outputs:");
for (auto & [outputName, outputPath] :
state.aio.blockOn(evaluator.store->queryDerivationOutputMap(drvPath)))
{
auto localStore = evaluator.store.try_cast_shared<LocalFSStore>();
for (auto & [outputName, outputPath] : evaluator.store->queryDerivationOutputMap(drvPath)) {
auto localStore = evaluator.store.dynamic_pointer_cast<LocalFSStore>();
if (localStore && command == ":bl") {
std::string symlink = "repl-result-" + outputName;
state.aio.blockOn(localStore->addPermRoot(outputPath, absPath(symlink)));
localStore->addPermRoot(outputPath, absPath(symlink));
logger->cout(" ./%s -> %s", symlink, evaluator.store->printStorePath(outputPath));
} else {
logger->cout(" %s -> %s", outputName, evaluator.store->printStorePath(outputPath));
@@ -858,26 +854,23 @@ ProcessLineResult NixRepl::processLine(std::string line)
throw Error("unknown command '%1%'", command);
else {
/* A line is either a regular expression or a `var = expr` assignment */
std::variant<std::unique_ptr<Expr>, ExprReplBindings> result = parseReplString(line);
std::visit(overloaded {
[&](ExprReplBindings & b) {
for (auto & [name, e] : b.symbols) {
Value * v = state.ctx.mem.allocValue();
e->eval(state, *env, *v);
(void) e.release(); // NOLINT(bugprone-unused-return-value): leak because of thunk references
addVarToScope(name, *v);
}
},
[&](std::unique_ptr<Expr> & e) {
Value v;
e->eval(state, *env, v);
(void) e.release(); // NOLINT(bugprone-unused-return-value): leak because of thunk references
state.forceValue(v, noPos);
printValue(std::cout, v, 1);
std::cout << std::endl;
}
}, result);
size_t p = line.find('=');
std::string name;
if (p != std::string::npos &&
p < line.size() &&
line[p + 1] != '=' &&
isVarName(name = removeWhitespace(line.substr(0, p))))
{
Expr & e = parseString(line.substr(p + 1));
Value & v(*evaluator.mem.allocValue());
v.mkThunk(env, e);
addVarToScope(evaluator.symbols.create(name), v);
} else {
Value v;
evalString(line, v);
printValue(std::cout, v, 1);
std::cout << std::endl;
}
}
return ProcessLineResult::PromptAgain;
@@ -888,8 +881,8 @@ void NixRepl::loadFile(const Path & path)
loadedFiles.remove(path);
loadedFiles.push_back(path);
Value v, v2;
state.evalFile(state.aio.blockOn(lookupFileArg(evaluator, path)).unwrap(always_progresses), v);
state.autoCallFunction(*autoArgs, v, v2, noPos);
state.evalFile(lookupFileArg(evaluator, path), v);
state.autoCallFunction(*autoArgs, v, v2);
addAttrsToScope(v2);
}
@@ -966,7 +959,13 @@ void NixRepl::loadReplOverlays()
Value &newAttrs(*evaluator.mem.allocValue());
SmallValueVector<3> args = {replInitInfo(), bindingsToAttrs(), replOverlays()};
state.callFunction(*replInitFilesFunction, args.size(), args.data(), newAttrs, noPos);
state.callFunction(
*replInitFilesFunction,
args.size(),
args.data(),
newAttrs,
replInitFilesFunction->determinePos(noPos)
);
// n.b. this does in fact load the stuff into the environment twice (once
// from the superset of the environment returned by repl-overlays and once
@@ -1012,21 +1011,22 @@ Value * NixRepl::replOverlays()
if (!replInit->isLambda()) {
evaluator.errors.make<TypeError>(
"Expected `repl-overlays` entry %s to be a lambda but found %s: %s",
path,
"Expected `repl-overlays` to be a lambda but found %1%: %2%",
showType(*replInit),
ValuePrinter(state, *replInit, errorPrintOptions)
)
.atPos(replInit->determinePos(noPos))
.debugThrow();
}
if (auto attrs = dynamic_cast<AttrsPattern *>(replInit->lambda.fun->pattern.get()); attrs && !attrs->ellipsis) {
if (replInit->lambda.fun->hasFormals()
&& !replInit->lambda.fun->formals->ellipsis) {
evaluator.errors.make<TypeError>(
"Expected first argument of %1% to have %2% to allow future versions of Lix to add additional attributes to the argument",
"repl-overlays",
"..."
)
.atPos(replInit->lambda.fun->pos)
.atPos(replInit->determinePos(noPos))
.debugThrow();
}
@@ -1052,53 +1052,29 @@ Value * NixRepl::replInitInfo()
}
template<typename T, typename NameFn, typename ValueFn>
void NixRepl::addToScope(T && things, NameFn nameFn, ValueFn valueFn)
{
size_t added = 0;
for (auto && thing : things) {
if (displ + 1 >= envSize)
throw Error("environment full; cannot add more variables");
const auto name = nameFn(thing);
staticEnv->vars.emplace_back(name, displ);
env->values[displ++] = valueFn(thing);
varNames.emplace(evaluator.symbols[name]);
added++;
}
staticEnv->sort();
staticEnv->deduplicate();
if (added > 0) {
notice("Added %1% variables.", added);
}
}
void NixRepl::addAttrsToScope(Value & attrs)
{
state.forceAttrs(attrs, noPos, "while evaluating an attribute set to be merged in the global scope");
addToScope(*attrs.attrs, [](Attr & a) { return a.name; }, [](Attr & a) { return a.value; });
state.forceAttrs(attrs, attrs.determinePos(noPos), "while evaluating an attribute set to be merged in the global scope");
if (displ + attrs.attrs->size() >= envSize)
throw Error("environment full; cannot add more variables");
for (auto & i : *attrs.attrs) {
staticEnv->vars.emplace_back(i.name, displ);
env->values[displ++] = i.value;
varNames.emplace(evaluator.symbols[i.name]);
}
staticEnv->sort();
staticEnv->deduplicate();
notice("Added %1% variables.", attrs.attrs->size());
}
void NixRepl::addValMapToScope(const ValMap & attrs)
{
addToScope(
attrs,
[&](auto & val) { return evaluator.symbols.create(val.first); },
[&](auto & val) { return val.second; }
);
}
void NixRepl::addVarToScope(const Symbol name, Value & v)
{
if (displ >= envSize)
throw Error("environment full; cannot add more variables");
if (auto oldVar = staticEnv->find(name); oldVar != staticEnv->vars.end()) {
if (auto oldVar = staticEnv->find(name); oldVar != staticEnv->vars.end())
staticEnv->vars.erase(oldVar);
notice("Updated %s.", evaluator.symbols[name]);
} else {
notice("Added %s.", evaluator.symbols[name]);
}
staticEnv->vars.emplace_back(name, displ);
staticEnv->sort();
env->values[displ++] = &v;
@@ -1120,12 +1096,7 @@ Value * NixRepl::bindingsToAttrs()
Expr & NixRepl::parseString(std::string s)
{
return evaluator.parseExprFromString(std::move(s), CanonPath::fromCwd(), staticEnv, featureSettings);
}
std::variant<std::unique_ptr<Expr>, ExprReplBindings> NixRepl::parseReplString(std::string s)
{
return evaluator.parseReplInput(std::move(s), CanonPath::fromCwd(), staticEnv, featureSettings);
return evaluator.parseExprFromString(std::move(s), CanonPath::fromCwd(), staticEnv);
}
@@ -1133,7 +1104,7 @@ void NixRepl::evalString(std::string s, Value & v)
{
Expr & e = parseString(s);
e.eval(state, *env, v);
state.forceValue(v, noPos);
state.forceValue(v, v.determinePos(noPos));
}
Value * NixRepl::evalFile(SourcePath & path)
@@ -1141,7 +1112,7 @@ Value * NixRepl::evalFile(SourcePath & path)
auto & expr = evaluator.parseExprFromFile(evaluator.paths.checkSourcePath(path), staticEnv);
Value * result(evaluator.mem.allocValue());
expr.eval(state, *env, *result);
state.forceValue(*result, noPos);
state.forceValue(*result, result->determinePos(noPos));
return result;
}
@@ -1158,19 +1129,16 @@ ReplExitStatus AbstractNixRepl::run(
repl.autoArgs = autoArgs;
repl.initEnv();
repl.addValMapToScope(extraEnv);
for (auto & [name, value] : extraEnv) {
repl.addVarToScope(repl.evaluator.symbols.create(name), *value);
}
return repl.mainLoop();
}
ReplExitStatus AbstractNixRepl::runSimple(EvalState & evalState, const ValMap & extraEnv)
{
return run(
{},
evalState.aio.blockOn(openStore()),
evalState,
[] { return AnnotatedValues{}; },
extraEnv,
nullptr
{}, openStore(), evalState, [] { return AnnotatedValues{}; }, extraEnv, nullptr
);
}
+1 -2
View File
@@ -2,11 +2,10 @@
///@file
#include "lix/libexpr/eval.hh"
#include "lix/libutil/types.hh"
namespace nix {
struct AbstractNixRepl : NeverAsync
struct AbstractNixRepl
{
typedef std::vector<std::pair<Value*,std::string>> AnnotatedValues;
+11 -10
View File
@@ -1,5 +1,5 @@
#include "lix/libexpr/attr-path.hh"
#include "lix/libutil/strings.hh"
#include "lix/libexpr/eval-inline.hh"
#include "print-options.hh"
#include <algorithm>
#include <sstream>
@@ -85,7 +85,7 @@ std::pair<Value *, PosIdx> findAlongAttrPath(EvalState & state, const std::strin
/* Evaluate the expression. */
Value * vNew = state.ctx.mem.allocValue();
state.autoCallFunction(autoArgs, *v, *vNew, pos);
state.autoCallFunction(autoArgs, *v, *vNew);
v = vNew;
state.forceValue(*v, noPos);
@@ -184,14 +184,15 @@ std::pair<SourcePath, uint32_t> findPackageFilename(EvalState & state, Value & v
throw ParseError("cannot parse 'meta.position' attribute '%s'", fn);
};
auto colon = fn.rfind(':');
if (colon == std::string::npos) fail();
// parsing as int32 instead of the uint32 we return for historical reasons.
// previously this was a stoi(), and we don't know what editors would do if
// we gave them line numbers that wouldn't fit into the int32 number space.
auto lineno = string2Int<int32_t>(std::string(fn, colon + 1, std::string::npos));
if (!lineno) fail();
return {CanonPath(fn.substr(0, colon)), *lineno};
try {
auto colon = fn.rfind(':');
if (colon == std::string::npos) fail();
auto lineno = std::stoi(std::string(fn, colon + 1, std::string::npos));
return {CanonPath(fn.substr(0, colon)), lineno};
} catch (std::invalid_argument & e) {
fail();
abort();
}
}
+1
View File
@@ -4,6 +4,7 @@
#include "lix/libexpr/eval.hh"
#include <string>
#include <map>
namespace nix {
+1
View File
@@ -5,6 +5,7 @@
#include "lix/libexpr/symbol-table.hh"
#include <algorithm>
#include <optional>
namespace nix {
+1 -1
View File
@@ -74,7 +74,7 @@ Here are some examples of how to use `fetchGit`.
name in the `ref` attribute.
However, if the revision you're looking for is in a future
branch for the non-default branch you will need to specify
branch for the non-default branch you will need to specify the
the `ref` attribute as well.
```nix
+1 -11
View File
@@ -33,20 +33,10 @@ the attribute `url` and the attribute `sha256`, e.g.
```nix
with import (fetchTarball {
url = "https://github.com/NixOS/nixpkgs/archive/nixos-14.12.tar.gz";
sha256 = "sha256-QsrS6BEjO8ccwlmL1b7/5iPaemuM/Ibj9WWu7bKe98o=";
sha256 = "1jppksrfvbk5ypiqdz4cddxdl8z6zyzdb2srq8fcffr327ld5jj2";
}) {};
stdenv.mkDerivation { … }
```
The `sha256` attribute accepts Nix-style base32 sha256 hashes (e.g.
`1jppksrfvbk5ypiqdz4cddxdl8z6zyzdb2srq8fcffr327ld5jj2`), hashes
in SRI format (as shown above), and some other less common hash
algorithms and formats.
<!--
TODO: Document all the accepted hash algorithms and formats
somewhere in the manual and link to it from here.
-->
Not available in [restricted evaluation mode](@docroot@/command-ref/conf-file.md#conf-restrict-eval).
-19
View File
@@ -1,19 +0,0 @@
---
name: fetchTree
args: [spec]
experimentalFeature: flakes
renameInGlobalScope: false
---
Fetches the tree specified by the attribute set or URL `spec`.
The spec is in the form of [a flake reference](../command-ref/new-cli/nix3-flake.md#flake-references); flake references are a thin wrapper around `fetchTree`.
There are [some efforts](https://github.com/nix-community/fetchTree-spec) to document the behaviour of `fetchTree` independently of flakes, but they have not yet borne fruit as of 2025-03.
`spec` also accepts the following special attribute not documented there:
- name\
The name of the resulting store path to fetch to.
Optional; defaults to the basename of the URL.
Due to some vagaries of flake behaviour, naming the fetched input `source` may avoid some extra copying when using the resulting store path as a path input for a flake.
See <https://git.lix.systems/lix-project/lix/issues/630>.
+1 -1
View File
@@ -5,7 +5,7 @@ experimentalFeature: dynamic-derivations
---
Return the output path of a derivation, literally or using a placeholder if needed.
If the derivation has a statically-known output path (i.e. the derivation output is input-addressed, or fixed content-addressed), the output path will just be returned.
If the derivation has a statically-known output path (i.e. the derivation output is input-addressed, or fixed content-addresed), the output path will just be returned.
But if the derivation is content-addressed or if the derivation is itself not-statically produced (i.e. is the output of another derivation), a placeholder will be returned instead.
*`derivation reference`* must be a string that may contain a regular store path to a derivation, or may be a placeholder reference. If the derivation is produced by a derivation, you must explicitly select `drv.outPath`.
+4 -10
View File
@@ -5,18 +5,12 @@ args: [start, len, s]
Return the substring of *s* from character position *start*
(zero-based) up to but not including *start + len*. If *start* is
greater than the length of the string, an empty string is returned,
and if *start + len* lies beyond the end of the string or *len*
is negative, only the substring up to the end of the string is
returned. *start* must be non-negative. For example,
and if *start + len* lies beyond the end of the string, only the
substring up to the end of the string is returned. *start* must be
non-negative. For example,
```nix
builtins.substring 0 3 "nixos"
```
evaluates to `"nix"`, and
```nix
builtins.substring 3 (-1) "nixos"
```
evaluates to `"os"`.
evaluates to `"nix"`.
-2
View File
@@ -13,8 +13,6 @@ Convert the expression *e* to a string. *e* can be:
- An integer.
- A floating-point value, it will be converted to the decimal notation in the style `[-]ddd.ddd` with 6 digits appearing after the decimal point.
- A list, in which case the string representations of its elements
are joined with spaces.
+1 -1
View File
@@ -47,7 +47,7 @@ stdenv.mkDerivation (rec {
```
The builder is supposed to generate the configuration file for a
[Jetty servlet container](https://jetty.org/). A servlet
[Jetty servlet container](http://jetty.mortbay.org/). A servlet
container contains a number of servlets (`*.war` files) each
exported under a specific URI prefix. So the servlet configuration
is a list of sets containing the `path` and `war` of the servlet
@@ -6,7 +6,7 @@ Create a copy of the given string where every "derivation deep" string context e
This is the opposite of [`builtins.addDrvOutputDependencies`](#builtins-addDrvOutputDependencies).
This is unsafe because it allows us to "forget" store objects we would have otherwise referred to with the string context,
This is unsafe because it allows us to "forget" store objects we would have otherwise refered to with the string context,
whereas Nix normally tracks all dependencies consistently.
Safe operations "grow" but never "shrink" string contexts.
[`builtins.addDrvOutputDependencies`] in contrast is safe because "derivation deep" string context element always refers to the underlying derivation (among many more things).
-17
View File
@@ -1,17 +0,0 @@
---
name: unsafeGetAttrPos
args: [s, attrset]
---
`unsafeGetAttrPos` returns the position of the original definition (possibly
before operations like set merges) of the attribute named *s* from *attrset*.
If the position couldn't be determined (for example the set was produced by
builtin functions like `mapAttrs`), `null` will be returned instead, otherwise
a attrset will be returned, and it has the following attributes:
- `file` (string)
- `line` (number)
- `column` (number)
This is unsafe because it allows us to distinguish sets that compare equal but
are defined at different locations.
+12 -13
View File
@@ -2,7 +2,6 @@
#include "lix/libstore/sqlite.hh"
#include "lix/libexpr/eval.hh"
#include "lix/libstore/store-api.hh"
#include "lix/libutil/async.hh"
#include "lix/libutil/users.hh"
namespace nix::eval_cache {
@@ -46,21 +45,21 @@ struct AttrDb
state->db = SQLite(dbPath);
state->db.isCache();
state->db.exec(schema, always_progresses);
state->db.exec(schema);
state->insertAttribute = state->db.create(
state->insertAttribute.create(state->db,
"insert or replace into Attributes(parent, name, type, value) values (?, ?, ?, ?)");
state->insertAttributeWithContext = state->db.create(
state->insertAttributeWithContext.create(state->db,
"insert or replace into Attributes(parent, name, type, value, context) values (?, ?, ?, ?, ?)");
state->queryAttribute = state->db.create(
state->queryAttribute.create(state->db,
"select rowid, type, value, context from Attributes where parent = ? and name = ?");
state->queryAttributes = state->db.create(
state->queryAttributes.create(state->db,
"select name from Attributes where parent = ?");
state->txn = std::make_unique<SQLiteTxn>(state->db.beginTransaction());
state->txn = std::make_unique<SQLiteTxn>(state->db);
}
~AttrDb()
@@ -352,7 +351,7 @@ Value * EvalCache::getRootValue(EvalState & state)
ref<AttrCursor> EvalCache::getRoot()
{
return make_ref<AttrCursor>(ref<EvalCache>(*this), std::nullopt);
return make_ref<AttrCursor>(ref(shared_from_this()), std::nullopt);
}
AttrCursor::AttrCursor(
@@ -526,7 +525,7 @@ ref<AttrCursor> AttrCursor::getAttr(EvalState & state, const std::string & name)
auto p = maybeGetAttr(state, name);
if (!p)
throw Error("attribute '%s' does not exist", getAttrPathStr(state, name));
return ref<AttrCursor>::unsafeFromPtr(p);
return ref(p);
}
OrSuggestions<ref<AttrCursor>> AttrCursor::findAlongAttrPath(EvalState & state, const std::vector<std::string> & attrPath)
@@ -540,7 +539,7 @@ OrSuggestions<ref<AttrCursor>> AttrCursor::findAlongAttrPath(EvalState & state,
}
res = child;
}
return ref<AttrCursor>::unsafeFromPtr(res);
return ref(res);
}
std::string AttrCursor::getString(EvalState & state)
@@ -586,7 +585,7 @@ string_t AttrCursor::getStringWithContext(EvalState & state)
return o.path;
},
}, c.raw);
if (!state.aio.blockOn(state.ctx.store->isValidPath(path))) {
if (!state.ctx.store->isValidPath(path)) {
valid = false;
break;
}
@@ -730,11 +729,11 @@ StorePath AttrCursor::forceDerivation(EvalState & state)
{
auto aDrvPath = getAttr(state, "drvPath");
auto drvPath = state.ctx.store->parseStorePath(aDrvPath->getString(state));
if (!state.aio.blockOn(state.ctx.store->isValidPath(drvPath)) && !settings.readOnlyMode) {
if (!state.ctx.store->isValidPath(drvPath) && !settings.readOnlyMode) {
/* The eval cache contains 'drvPath', but the actual path has
been garbage-collected. So force it to be regenerated. */
aDrvPath->forceValue(state);
if (!state.aio.blockOn(state.ctx.store->isValidPath(drvPath)))
if (!state.ctx.store->isValidPath(drvPath))
throw Error("don't know how to recreate store derivation '%s'!",
state.ctx.store->printStorePath(drvPath));
}
+1
View File
@@ -1,6 +1,7 @@
#pragma once
///@file
#include "lix/libutil/sync.hh"
#include "lix/libutil/hash.hh"
#include "lix/libexpr/eval.hh"
+16 -17
View File
@@ -1,25 +1,30 @@
#include "lix/libexpr/eval-error.hh"
#include "lix/libexpr/eval.hh"
#include "lix/libexpr/value.hh"
#include "lix/libutil/types.hh"
namespace nix {
template<std::derived_from<EvalError> T>
template<class T>
EvalErrorBuilder<T> EvalErrorBuilder<T>::withExitStatus(unsigned int exitStatus) &&
{
error->withExitStatus(exitStatus);
return std::move(*this);
}
template<std::derived_from<EvalError> T>
template<class T>
EvalErrorBuilder<T> EvalErrorBuilder<T>::atPos(PosIdx pos) &&
{
error->err.pos = positions[pos];
return std::move(*this);
}
template<std::derived_from<EvalError> T>
template<class T>
EvalErrorBuilder<T> EvalErrorBuilder<T>::atPos(Value & value, PosIdx fallback) &&
{
return std::move(*this).atPos(value.determinePos(fallback));
}
template<class T>
EvalErrorBuilder<T> EvalErrorBuilder<T>::withTrace(PosIdx pos, const std::string_view text) &&
{
error->err.traces.push_front(
@@ -27,14 +32,14 @@ EvalErrorBuilder<T> EvalErrorBuilder<T>::withTrace(PosIdx pos, const std::string
return std::move(*this);
}
template<std::derived_from<EvalError> T>
template<class T>
EvalErrorBuilder<T> EvalErrorBuilder<T>::withSuggestions(Suggestions & s) &&
{
error->err.suggestions = s;
return std::move(*this);
}
template<std::derived_from<EvalError> T>
template<class T>
EvalErrorBuilder<T> EvalErrorBuilder<T>::withFrame(const Env & env, const Expr & expr) &&
{
if (debug) {
@@ -49,14 +54,14 @@ EvalErrorBuilder<T> EvalErrorBuilder<T>::withFrame(const Env & env, const Expr &
return std::move(*this);
}
template<std::derived_from<EvalError> T>
template<class T>
EvalErrorBuilder<T> EvalErrorBuilder<T>::addTrace(PosIdx pos, HintFmt hint) &&
{
error->addTrace(positions[pos], hint);
return std::move(*this);
}
template<std::derived_from<EvalError> T>
template<class T>
template<typename... Args>
EvalErrorBuilder<T>
EvalErrorBuilder<T>::addTrace(PosIdx pos, std::string_view formatString, const Args &... formatArgs) &&
@@ -66,8 +71,8 @@ EvalErrorBuilder<T>::addTrace(PosIdx pos, std::string_view formatString, const A
return std::move(*this);
}
template<std::derived_from<EvalError> T>
void EvalErrorBuilder<T>::debugThrow(NeverAsync) &&
template<class T>
void EvalErrorBuilder<T>::debugThrow() &&
{
if (debug) {
if (auto last = debug->traces().next()) {
@@ -77,13 +82,7 @@ void EvalErrorBuilder<T>::debugThrow(NeverAsync) &&
}
}
throw *error; // NOLINT(lix-foreign-exceptions): type dependent
}
template<std::derived_from<EvalError> T>
void EvalErrorBuilder<T>::throw_() &&
{
throw *error; // NOLINT(lix-foreign-exceptions): type dependent
throw *error;
}
template class EvalErrorBuilder<EvalError>;
+7 -12
View File
@@ -6,7 +6,6 @@
#include "lix/libutil/types.hh"
#include "lix/libexpr/pos-idx.hh"
#include "lix/libexpr/pos-table.hh"
#include <concepts>
namespace nix {
@@ -16,14 +15,13 @@ struct Env;
struct Expr;
struct Value;
class EvalError;
class EvalState;
template<std::derived_from<EvalError> T>
template<class T>
class EvalErrorBuilder;
class EvalError : public Error
{
template<std::derived_from<EvalError> T>
template<class T>
friend class EvalErrorBuilder;
std::shared_ptr<const DebugTrace> frame;
@@ -56,7 +54,7 @@ public:
}
};
template<std::derived_from<EvalError> T>
template<class T>
class [[nodiscard]] EvalErrorBuilder final
{
const PosTable & positions;
@@ -76,6 +74,8 @@ public:
[[gnu::noinline]] EvalErrorBuilder<T> atPos(PosIdx pos) &&;
[[gnu::noinline]] EvalErrorBuilder<T> atPos(Value & value, PosIdx fallback = noPos) &&;
[[gnu::noinline]] EvalErrorBuilder<T> withTrace(PosIdx pos, const std::string_view text) &&;
[[gnu::noinline]] EvalErrorBuilder<T> withSuggestions(Suggestions & s) &&;
@@ -89,14 +89,9 @@ public:
addTrace(PosIdx pos, std::string_view formatString, const Args &... formatArgs) &&;
/**
* Throw the underlying exception, invoking the debug state callback.
* Throw the underlying exception.
*/
[[gnu::noinline, gnu::noreturn]] void debugThrow(NeverAsync = {}) &&;
/**
* Throw the underlying exception, bypassing the debug state callback.
*/
[[gnu::noinline, gnu::noreturn]] void throw_() &&;
[[gnu::noinline, gnu::noreturn]] void debugThrow() &&;
};
}
+273 -299
View File
File diff suppressed because it is too large Load Diff
+26 -83
View File
@@ -6,8 +6,6 @@
#include "lix/libexpr/gc-alloc.hh"
#include "lix/libutil/box_ptr.hh"
#include "lix/libutil/generator.hh"
#include "lix/libutil/async.hh"
#include "lix/libutil/source-path.hh"
#include "lix/libutil/types.hh"
#include "lix/libexpr/value.hh"
#include "lix/libexpr/nixexpr.hh"
@@ -18,7 +16,6 @@
#include "lix/libexpr/repl-exit-status.hh"
#include "lix/libutil/backed-string-view.hh"
#include <concepts>
#include <map>
#include <optional>
#include <unordered_map>
@@ -39,7 +36,7 @@ namespace eval_cache {
/**
* Function that implements a primop.
*/
using PrimOpImpl = void(EvalState & state, Value ** args, Value & v);
using PrimOpImpl = void(EvalState & state, PosIdx pos, Value ** args, Value & v);
/**
* Info about a primitive operation, and its implementation
@@ -151,7 +148,7 @@ private:
const SymbolTable & symbols;
public:
std::function<ReplExitStatus(ValMap const & extraEnv, NeverAsync)> errorCallback;
std::function<ReplExitStatus(ValMap const & extraEnv)> errorCallback;
bool stop = false;
bool inDebugger = false;
std::map<const Expr *, const std::shared_ptr<const StaticEnv>> exprEnvs;
@@ -160,7 +157,7 @@ public:
explicit DebugState(
const PosTable & positions,
const SymbolTable & symbols,
std::function<ReplExitStatus(ValMap const & extraEnv, NeverAsync)> errorCallback
std::function<ReplExitStatus(ValMap const & extraEnv)> errorCallback
)
: positions(positions)
, symbols(symbols)
@@ -169,7 +166,7 @@ public:
assert(errorCallback);
}
void onEvalError(const EvalError * error, const Env & env, const Expr & expr, NeverAsync = {});
void onEvalError(const EvalError * error, const Env & env, const Expr & expr);
const std::shared_ptr<const StaticEnv> staticEnvFor(const Expr & expr) const
{
@@ -182,7 +179,7 @@ public:
class TraceFrame
{
friend struct DebugState;
template<std::derived_from<EvalError> T>
template<class T>
friend class EvalErrorBuilder;
// holds both the data for this frame *and* a deleter that pulls this frame
@@ -357,7 +354,7 @@ struct EvalErrorContext
const PosTable & positions;
DebugState * debug;
template<std::derived_from<EvalError> T, typename... Args>
template<class T, typename... Args>
[[gnu::noinline]]
EvalErrorBuilder<T> make(const Args & ... args) {
return EvalErrorBuilder<T>(positions, debug, args...);
@@ -367,16 +364,15 @@ struct EvalErrorContext
class EvalPaths
{
ref<Store> store;
/**
* Store used to build stuff.
*/
ref<Store> buildStore;
SearchPath searchPath_;
EvalErrorContext & errors;
public:
EvalPaths(
AsyncIoRoot & aio,
const ref<Store> & store,
SearchPath searchPath,
EvalErrorContext & errors
);
EvalPaths(const ref<Store> & store, const ref<Store> buildStore, SearchPath searchPath, EvalErrorContext & errors);
const SearchPath & searchPath() const { return searchPath_; }
@@ -453,38 +449,11 @@ public:
*/
Path toRealPath(const Path & path, const NixStringContext & context);
/**
* findFile wants to throw a debuggable error when the requested file
* is not found, but it can't invoke the debugger itself because it's
* async code. This wraps the result-or-error to allow it regardless.
* This happens for copyPathToStore as well, with another error type.
*/
template<typename T, typename E>
struct PathResult : private std::variant<T, EvalErrorBuilder<E>>
{
PathResult(T p) : std::variant<T, EvalErrorBuilder<E>>(std::move(p)) {}
PathResult(EvalErrorBuilder<E> e) : std::variant<T, EvalErrorBuilder<E>>(std::move(e)) {}
T unwrap(NeverAsync = {}) &&
{
return std::visit(
overloaded{
[](T & p) -> T { return std::move(p); },
[](EvalErrorBuilder<E> & e) -> T {
std::move(e).debugThrow(always_progresses);
}
},
static_cast<std::variant<T, EvalErrorBuilder<E>> &>(*this)
);
}
};
/**
* Look up a file in the search path.
*/
kj::Promise<Result<PathResult<SourcePath, ThrownError>>> findFile(const std::string_view path);
kj::Promise<Result<PathResult<SourcePath, ThrownError>>>
findFile(const SearchPath & searchPath, const std::string_view path, const PosIdx pos = noPos);
SourcePath findFile(const std::string_view path);
SourcePath findFile(const SearchPath & searchPath, const std::string_view path, const PosIdx pos = noPos);
/**
* Try to resolve a search path value (not the optinal key part)
@@ -493,10 +462,9 @@ public:
*
* If it is not found, return `std::nullopt`
*/
kj::Promise<Result<std::optional<std::string>>>
resolveSearchPathPath(const SearchPath::Path & path);
std::optional<std::string> resolveSearchPathPath(const SearchPath::Path & path);
kj::Promise<Result<PathResult<StorePath, EvalError>>> copyPathToStore(
StorePath copyPathToStore(
NixStringContext & context, const SourcePath & path, RepairFlag repair = NoRepair
);
@@ -507,6 +475,12 @@ public:
* single `NixStringContextElem::Opaque` element of that store path.
*/
void mkStorePathString(const StorePath & storePath, Value & v);
/**
* Realise the given context, and return a mapping from the placeholders
* used to construct the associated value to their final store path
*/
[[nodiscard]] StringMap realiseContext(const NixStringContext & context);
};
struct EvalStatistics
@@ -557,16 +531,10 @@ public:
*/
const ref<Store> store;
/**
* Store used to build stuff.
*/
ref<Store> buildStore;
std::unique_ptr<DebugState> debug;
EvalErrorContext errors;
Evaluator(
AsyncIoRoot & aio,
const SearchPath & _searchPath,
ref<Store> store,
std::shared_ptr<Store> buildStore = nullptr,
@@ -599,14 +567,6 @@ public:
const FeatureSettings & xpSettings = featureSettings
);
std::variant<std::unique_ptr<Expr>, ExprReplBindings>
parseReplInput(
std::string s,
const SourcePath & basePath,
std::shared_ptr<StaticEnv> & staticEnv,
const FeatureSettings & xpSettings = featureSettings
);
Expr & parseStdin();
/**
@@ -623,16 +583,6 @@ private:
std::shared_ptr<StaticEnv> & staticEnv,
const FeatureSettings & xpSettings = featureSettings);
std::variant<std::unique_ptr<Expr>, ExprReplBindings>
parse_repl(
char * text,
size_t length,
Pos::Origin origin,
const SourcePath & basePath,
std::shared_ptr<StaticEnv> & staticEnv,
const FeatureSettings & xpSettings = featureSettings
);
public:
BindingsBuilder buildBindings(size_t capacity)
{
@@ -677,7 +627,7 @@ public:
* which it is not possible to block on a promise while already running in
* a promise without doing this blocking on a different event loop/thread.
*/
box_ptr<EvalState> begin(AsyncIoRoot & aio);
box_ptr<EvalState> begin();
};
@@ -685,11 +635,10 @@ class EvalState
{
friend class Evaluator;
explicit EvalState(AsyncIoRoot & aio, Evaluator & ctx);
explicit EvalState(Evaluator & ctx);
public:
Evaluator & ctx;
AsyncIoRoot & aio;
EvalState(const EvalState &) = delete;
EvalState(EvalState &&) = delete;
@@ -717,8 +666,8 @@ public:
* type.
*/
inline bool evalBool(Env & env, Expr & e);
inline void evalAttrs(Env & env, Expr & e, Value & v);
inline void evalList(Env & env, Expr & e, Value & v);
inline bool evalBool(Env & env, Expr & e, const PosIdx pos, std::string_view errorCtx);
inline void evalAttrs(Env & env, Expr & e, Value & v, const PosIdx pos, std::string_view errorCtx);
/**
* If `v` is a thunk, enter it and overwrite `v` with the result
@@ -753,12 +702,6 @@ public:
std::string_view forceString(Value & v, NixStringContext & context, const PosIdx pos, std::string_view errorCtx);
std::string_view forceStringNoCtx(Value & v, const PosIdx pos, std::string_view errorCtx);
/**
* Realise the given context, and return a mapping from the placeholders
* used to construct the associated value to their final store path
*/
[[nodiscard]] StringMap realiseContext(const NixStringContext & context);
public:
/**
* @return true iff the value `v` denotes a derivation (i.e. a
@@ -854,7 +797,7 @@ public:
* Automatically call a function for which each argument has a
* default value or has a binding in the `args` map.
*/
void autoCallFunction(Bindings & args, Value & fun, Value & res, PosIdx pos);
void autoCallFunction(Bindings & args, Value & fun, Value & res);
void mkPos(Value & v, PosIdx pos);
+12 -13
View File
@@ -8,22 +8,21 @@ namespace nix {
class EvalState;
struct Value;
void prim_addDrvOutputDependencies(EvalState & state, Value * * args, Value & v);
void prim_fetchClosure(EvalState & state, Value * * args, Value & v);
void prim_fetchTree(EvalState & state, Value * * args, Value & v);
void prim_fetchGit(EvalState & state, Value * * args, Value & v);
void prim_fetchTarball(EvalState & state, Value * * args, Value & v);
void prim_fetchurl(EvalState & state, Value * * args, Value & v);
void prim_fromTOML(EvalState & state, Value * * args, Value & v);
void prim_getContext(EvalState & state, Value * * args, Value & v);
void prim_hasContext(EvalState & state, Value * * args, Value & v);
void prim_unsafeDiscardOutputDependency(EvalState & state, Value * * args, Value & v);
void prim_addDrvOutputDependencies(EvalState & state, const PosIdx pos, Value * * args, Value & v);
void prim_fetchClosure(EvalState & state, const PosIdx pos, Value * * args, Value & v);
void prim_fetchGit(EvalState & state, const PosIdx pos, Value * * args, Value & v);
void prim_fetchTarball(EvalState & state, const PosIdx pos, Value * * args, Value & v);
void prim_fetchurl(EvalState & state, const PosIdx pos, Value * * args, Value & v);
void prim_fromTOML(EvalState & state, const PosIdx pos, Value * * args, Value & v);
void prim_getContext(EvalState & state, const PosIdx pos, Value * * args, Value & v);
void prim_hasContext(EvalState & state, const PosIdx pos, Value * * args, Value & v);
void prim_unsafeDiscardOutputDependency(EvalState & state, const PosIdx pos, Value * * args, Value & v);
namespace flake {
void prim_flakeRefToString(EvalState & state, Value * * args, Value & v);
void prim_getFlake(EvalState & state, Value * * args, Value & v);
void prim_parseFlakeRef(EvalState & state, Value * * args, Value & v);
void prim_flakeRefToString(EvalState & state, const PosIdx pos, Value * * args, Value & v);
void prim_getFlake(EvalState & state, const PosIdx pos, Value * * args, Value & v);
void prim_parseFlakeRef(EvalState & state, const PosIdx pos, Value * * args, Value & v);
}

Some files were not shown because too many files have changed in this diff Show More