Compare commits

..
546 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
Jade Lovelace 079528098f release: merge release 2.92.0 back to mainline
This merge commit returns to the previous state prior to the release but leaves the tag in the branch history.
Release created with releng/create_release.xsh

Change-Id: I4e4650d96de82b46c35171b3a9fc1e3a6ca8e6a1
2025-01-18 13:04:46 -08:00
Jade Lovelace 9446732466 release: 2.92.0 "Bombe glacée"
Release produced with releng/create_release.xsh

Change-Id: Ife8c3ed7dc44b6ed334e53ec260fd17a931bf55f
2025-01-18 13:04:46 -08:00
Jade Lovelace b5c3c21792 release: release notes for 2.92.0
Release created with releng/create_release.xsh

Change-Id: Ie02e27b7328758727e78c85925e04629cd4d6d14
2025-01-18 13:04:35 -08:00
Jade Lovelace fc8bd88cf4 releng: fix for newer xonsh
Looks like a module rename

Change-Id: I281e41b8781fa5aa75a3bcb6e6907e582f46dcb7
2025-01-18 13:04:20 -08:00
Jade Lovelace 9b2761d66d version.json: codename and remove -dev suffix in prep for 2.92
Change-Id: I30e45aac44e7fd5bd5cfe56a9514cee95b1d4d8b
2025-01-17 19:03:49 -08:00
Jade Lovelace fe79b90055 doc: add some missing release notes for 2.92
Change-Id: I4861f8885aac53ce76322aae0387facfdd5f3e88
2025-01-17 19:03:49 -08:00
Justin ! 0d14c2b67a libmain: always print all information when calling nix --version
This commit remove a check for the log level in the `printVersion()`
function that was making `nix --version` behaving weirdly and
inconsistently compared to other `nix-*` commands.

The root cause is the following code in `lix/nix/main.cc` that change
the log level at runtime if nix is call interactively:

```cpp
if (isatty(STDERR_FILENO)) {
  verbosity = lvlNotice;
} else {
  verbosity = lvlInfo;
}
```

This should be removed, but since it has more implication it's gonna be
done in another CL.

Fix: https://git.lix.systems/lix-project/lix/issues/620
Change-Id: Id2c83c51d7ef799ee2f9b8dbdd2bfaeaf2df6188
2025-01-16 22:56:42 -05:00
Jade Lovelace 9760c00591 clang-tidy: Obliterate FixIncludes check
We have obliterated the support across the build system for these
include paths anywhere, so the codemod is now redundant and can go.

Change-Id: I54082f39752c6aac6429e1c24026211adae8221a
2025-01-16 00:12:57 -08:00
Jade Lovelace bf3ebde25f pkg-config: remove legacy include paths!
This is a breaking change for non-migrated external clients. External
users always need to use <lix/libcmd/foo.hh> type include paths now.

This is as was always planned with the include rearrangement.

Change-Id: I269be91ff9f9cc94d5d3043cf3e0bdf8db1d8edb
2025-01-16 00:12:57 -08:00
eldritch horrorsandGerrit Code Review 423d8b03c8 Merge "libutil: thread-pool: ensure threads finished on error" into main 2025-01-15 22:33:30 +00:00
Jade Lovelace acbb3cff2d Merge remote-tracking branch 'pennae/path-access' into HEAD
This fixes a bug where flakes do not actually do purity path checks
correctly.

Tested-By: Jade Lovelace <lix@jade.fyi>
Change-Id: If7d131a8e73a5874fb15cfaa0dea3b8811ba35d2
2025-01-13 17:19:54 -08:00
Maximilian BoschandGerrit Code Review 38dd196b03 Merge "libstore: fix "illegal reference specifier 'man'"-error in postgresql_14" into main 2025-01-12 11:12:12 +00:00
Jade Lovelaceandeldritch horrors d46adb45eb tests: validate that flakes do path traversal checking
Apparently we had zero test coverage of this, let's fix that.

Change-Id: I00c906daf5acfc01913562036ca88abbf63dd3d9
2025-01-11 20:42:30 +01:00
eldritch horrors 0dbfa7b26e libexpr: forbid allowed -> disallowed -> allowed links
this is more of a theoretical problem, but it does allow changing the
behavior of a flake depending on mutable machine state. it's unlikely
that this could be used to reliably do anything bad, but it does lead
to even more non-determinstic evaluation of (notionally) pure flakes.

Change-Id: I5bac7ed045046da08a36c764ab887bc9c7551542
2025-01-11 20:42:30 +01:00
Dominique Martinet 4737d8b65e libutil: thread-pool: ensure threads finished on error
This fixes segfaults with nix copy when there was an error processing
addMultipleToStore.

Running with ASAN/TSAN pointed at an use-after-free with threads from
the pool accessing the graph declared in processGraph after the function
was exiting and destructing the variables.

It turns out that if there is an error before pool.process() is called,
for example while we are still enqueueing tasks, then pool.process()
isn't called and threads are still left to run.

By creating the pool last we ensure that it is stopped first before
running other destructors even if an exception happens early.

fixes #618

Change-Id: I42a355f632aa0354df94c5d5d8cbe7ab5196c9a6
2025-01-12 04:14:28 +09:00
eldritch horrorsandJade Lovelace c948b350fb libutil: add CheckedSourcePath for accessing things
SourcePath only manipulates path names now. all accesses must go through
a checked path going forward to ensure we don't escape restriction lists
of pure and restricted evaluation. if a directory path is checked it can
safely be assumed that the directory itself is allowed, and its contents
will likewise be safe to access. it is tempting to assumed that contents
will also be fine, but that's only true if the content is not a symlink.

Change-Id: Icec3098d53fe9dce50997954ba958fe4f304d59b
2025-01-10 15:20:27 -08:00
eldritch horrorsandJade Lovelace 8db8ac9a67 always checkSource before accessing anything
like earlier, anything accessed during eval must be checked against the
list of path restrictions. this notably excludes `Pos::getSource` which
is run only from an unrestricted context (resolving line/column numbers
for expressions), but since positions require the parser to run and the
parser requires a checked input to produce positions this is not a leak

Change-Id: I337859e9c780590d4434885125a3ef70a11f6e93
2025-01-10 15:20:27 -08:00
eldritch horrorsandJade Lovelace 3f6a1e45c9 libexpr: always checkSourcePath in resolveExprPath
the purpose of resolveExprPath is to produce a parser input path. parser
input paths must be validated against the path allow list so they do not
escape the restricted/pure eval sandbox. checking the input path and any
intermediate paths during resolving makes this a lot harder to do badly.

Change-Id: Ib31b5bca63fe26a5e08458a871cdc9f92f9b6a10
2025-01-10 15:20:27 -08:00
eldritch horrorsandJade Lovelace ede0851fb4 libexpr: move resolveExprPath to EvalPaths
Change-Id: I4f8e27bb816d6498df4d73a57e10b654eb995c32
2025-01-10 15:20:27 -08:00
eldritch horrorsandJade Lovelace 5af069b248 libutil: remove SourcePath::resolveSymlinks
in pure mode it is entirely useless. in impure mode it's mostly useless
since the way in which it is used is either equivalent to not being run
at all, or is equivalent to turning the following lstat into a stat. we
add a stat method instead for all those who need final symlinks stat'd.

Change-Id: I801886d18eb34b26e62b4c05d53318c6421a69bf
2025-01-10 15:20:27 -08:00
eldritch horrorsandJade Lovelace f93af1db1f libutil: make SourcePath::path private
use canonical() to get the disk path, to_string() to get the string form.

Change-Id: I95bb6df53356f30290b487d1cca0aa2fb37249ed
2025-01-10 15:20:27 -08:00
eldritch horrorsandJade Lovelace 3acba7951a libexpr: use StorePath::to_string in string contexts
`path.abs()` does the same thing, but `to_string` communicates intent as well.

Change-Id: I9619a9f2e32317f0a1cc8e092ce8898471b980ac
2025-01-10 15:20:27 -08:00
eldritch horrorsandJade Lovelace 44255c316d libutil: remove SourcePath::getPhysicalPath
all files are physical, so this doesn't have to be optional. if it only
returns a copy of a member it's not useful either, but performance cost

Change-Id: Ib2f935ae247d96418d55bc100e04765dc586528b
2025-01-10 15:20:27 -08:00
jadeandGerrit Code Review d953e138a2 Merge "fix: macOS build broken on nixos-unstable" into main 2025-01-10 22:23:14 +00:00
eldritch horrors fafe647bc2 libstore: fix UAF in FileTransfer
after startTransfer the transfer, and thus downloadState, is gone. asan
hasn't caught this, presumably because the access is in libc somewhere.
we don't even need access to the old state; the assertion is not useful
here and clearing the previous exception is invisible to the new round.

Change-Id: I32dc1a487b96cbaabcf5061c9b1f96dbc5aaae49
2025-01-10 17:05:02 +01:00
Jade Lovelace 37e8904ba7 fix: macOS build broken on nixos-unstable
This is definitely a bug in rapidcheck but also rapidcheck is
unmaintained (author vanished) so we can't really do anything about it
on that side.

lix> FAILED: tests/unit/liblixutil-test-support.dylib.p/libutil-support_tests_hash.cc.o
lix> clang++ -Itests/unit/liblixutil-test-support.dylib.p -Itests/unit -I../tests/unit -I../tests/unit/libutil-support -I. -I.. -Ilix/libutil -Ilix/libexpr -fdiagnostics-color=always -D_LIBCPP_HARDENING_MODE=_LIBCPP_HARDENING_MODE_FAST -Wall -Winvalid-pch -Wextra -std=c++23 -O2 -g -fpch-instantiate-templates -include config.h -Wno-unused-parameter -Wno-deprecated-declarations -Wno-missing-field-initializers -Wimplicit-fallthrough -Werror=switch -Werror=switch-enum -Werror=unused-result -Wdeprecated-copy -Wignored-qualifiers -Werror=suggest-override -fsanitize=signed-integer-overflow -fsanitize-undefined-trap-on-error -ffile-prefix-map=../lix=lix -MD -MQ tests/unit/liblixutil-test-support.dylib.p/libutil-support_tests_hash.cc.o -MF tests/unit/liblixutil-test-support.dylib.p/libutil-support_tests_hash.cc.o.d -o tests/unit/liblixutil-test-support.dylib.p/libutil-support_tests_hash.cc.o -c ../tests/unit/libutil-support/tests/hash.cc
lix> In file included from ../tests/unit/libutil-support/tests/hash.cc:3:
lix> In file included from /nix/store/k8b7wc0lkr3lqxybzv458ijddpqnzlgw-rapidcheck-0-unstable-2023-12-14-dev/include/rapidcheck.h:18:
lix> In file included from /nix/store/k8b7wc0lkr3lqxybzv458ijddpqnzlgw-rapidcheck-0-unstable-2023-12-14-dev/include/rapidcheck/Gen.h:74:
lix> /nix/store/k8b7wc0lkr3lqxybzv458ijddpqnzlgw-rapidcheck-0-unstable-2023-12-14-dev/include/rapidcheck/Gen.hpp:72:22: error: calling 'current_exception' with incomplete return type 'exception_ptr'
lix>    72 |     auto exception = std::current_exception();
lix>       |                      ^~~~~~~~~~~~~~~~~~~~~~~~
lix> /nix/store/0nhi47d5ip48wprxnava6vv973zzzndf-libcxx-19.1.5-dev/include/c++/v1/__exception/operations.h:37:41: note: 'current_exception' declared here
lix>    37 | _LIBCPP_EXPORTED_FROM_ABI exception_ptr current_exception() _NOEXCEPT;
lix>       |                                         ^
lix> /nix/store/0nhi47d5ip48wprxnava6vv973zzzndf-libcxx-19.1.5-dev/include/c++/v1/__exception/operations.h:35:33: note: forward declaration of 'std::exception_ptr'
lix>    35 | class _LIBCPP_EXPORTED_FROM_ABI exception_ptr;
lix>       |                                 ^
lix> 1 error generated.

Change-Id: Id5d0c4aa8143f2469c5a636d6773a2f5a25a0268
2025-01-07 17:11:04 -08:00
piegamesandGerrit Code Review 3413ab5629 Merge "libexpr: Undeprecate overriding __findFile" into main 2025-01-07 18:39:48 +00:00
Maximilian Bosch a1c09bed7b libstore: fix "illegal reference specifier 'man'"-error in postgresql_14
Nixpkgs issues / PRs:
* https://github.com/NixOS/nixpkgs/pull/368091
* https://github.com/NixOS/nixpkgs/issues/369366

This can be triggered with the postgresql_14 derivation from nixpkgs rev
19305d94dacca226ca048b78e6de00f599c65858
(/nix/store/bxp6g57limvwiga61vdlyvhy7i8rp6wd-postgresql-14.15.drv on
x86_64-linux): for reasons unknown to me, only the `man` and `lib` outputs
are cached on cache.nixos.org:

    $ nix derivation show  /nix/store/bxp6g57limvwiga61vdlyvhy7i8rp6wd-postgresql-14.15.drv | jq '.[].outputs.[].path' -r | xargs nix path-info --store https://cache.nixos.org
    warning: The interpretation of store paths arguments ending in `.drv` recently changed. If this command is now failing try again with '/nix/store/bxp6g57limvwiga61vdlyvhy7i8rp6wd-postgresql-14.15.drv^*'
    don't know how to build these paths:
      /nix/store/m9vb40xxr6gckjzpfxnqcmjqsks2gx03-postgresql-14.15
      /nix/store/nm1415wa53iawar9axwxy0an6ximhayn-postgresql-14.15-dev
      /nix/store/v9vrvfhiw9gk8hj9895sb15fxvxnyylj-postgresql-14.15-debug
      /nix/store/zi12g1p99g2173i8093ixbqkfh9ng87b-postgresql-14.15-doc
    /nix/store/3i3fpz0xss9inampf51gp3pkx24ypxpj-postgresql-14.15-man
    /nix/store/db8797h2cp4rm1cnsqrf87apkkxwwdff-postgresql-14.15-lib
    error: path '/nix/store/m9vb40xxr6gckjzpfxnqcmjqsks2gx03-postgresql-14.15' does not exist in the store

Also, the derivation uses the `outputChecks` feature (and thus `__structuredAttrs`)
to make sure that e.g. the `out` output doesn't reference the `man`
output:

    __structuredAttrs = true;
    outputs = [ "out" "dev" "doc" "lib" "man" ];
    outputChecks.out.disallowedReferences = [ "dev" "doc" "man" ];

With all that in place, the following error was hit on all CppNix / Lix
versions currently supported when trying to build the derivation above:

    error: derivation contains an illegal reference specifier 'man'

The following happened here:

* The `man` & `lib` outputs were substituted at some point.
* When register outputs, the reference checks are made.
* `LocalDerivationGoal::checkOutputs` gets a map of all outputs that
  were built and are NOT already registered in the store. In the example
  above this means `out`, `dev`, `debug` and `doc`.
* `checkOutputs` tries to resolve the `man` output and fails to do so
  because it's a store-path that's already registered and thus not part
  of the map passed to `checkOutputs`.

Since the map passed to `checkOutputs` is used in various other places
that appear to assume that the paths aren't registered already, I didn't
write the already registered paths into it. Instead, I created a second
map that contains all already registered outputs and pass it as third
argument to `checkOutputs`. If the other lookups fail, this map will be
now checked before the "illegal reference specifier"-error is thrown.

This fixes the problem with `postgresql_14` for me.

Also wrote a small regression test that fails locally without the patch
in place.

Change-Id: Ieacca80c001fcfbebf6f5fe97e25c49d2724c3ff
2025-01-05 14:50:45 +01:00
eldritch horrors 8c1ece93cc libstore: fix download deadlock on interruption
in a daemon all calls to the logger can throw an Interrupted exception,
which so far has silently stopped the curl thread without notifying its
transfers and leaving them stuck as a result. ensuring that the loggers
can never throw Interrupted will have very unpleasant side-effects, and
throwing depending on context requires large amount of bookkeeping. for
now it is easiest to abort all transfers on Interrupted during cleanup.

the test for this is extremely sketchy because we want to hit a single,
very specifically chosen, loger call in TransferItem::finish(). the bug
was triggered by the `act.progress` further down from what we're aiming
for, but that one is much harder to select for than the debug log here.

fixes #613

Change-Id: Id72efa64dd30cbbf256d2ab2a328457a0b095c6a
2025-01-05 01:19:12 +01:00
eldritch horrors c314c5e581 benchmarks: fix config for nixos 24.11
the flake input bump broke these due to packages being moved. also fix
renamed options while we are here, before they inevitably break later.

Change-Id: If16bb0a221e63ae7394af6b8d904c5ecd8994e6f
2025-01-05 01:19:12 +01:00
eldritch horrors 6e8bc92ca5 flake: fix nixpkgs lib test
the bump to 24.11 broke this, and buildbot simply ignored it. great.

Change-Id: I9a6c177c0679beb87891c05c34272c6c437c65fa
2025-01-05 01:19:12 +01:00
piegames 539c340c66 libexpr: Undeprecate overriding __findFile
After gathering more community feedback, more non-trivial use cases for
overriding `__findFile` emerged. Unlike the use case of Tvix mentioned
in #599, these can't easily be worked around by overriding `nixPath`
instead.

This is the second fixup/partial revert for
81d5f0a7d9.
See also #599.

Change-Id: I7ca75e1a2b196c0da341969c61f4c168b5f657f9
2025-01-04 19:07:35 +01:00
Chip Bilbrey 5c7ea4f446 Use locked flake-compat in shell.nix
Change-Id: I059aa6da6365f1a5a46b506e57d9b3ab2b73d8d8
2024-12-30 07:25:23 -08:00
Yureka 92ed9fee7d fix(libstore): correctly trigger embedded sandbox shell logic
Change-Id: I8131c7a94b3cd54d7e75fcdddf2fb8d561b6e38f
2024-12-11 16:48:34 +01:00
V.andGerrit Code Review 4f5e5e9bdf Merge "refactor(libutil): add new constructor to SysError using std::error_code" into main 2024-12-11 08:44:38 +00:00
rebecca “wiggles” turnerandGerrit Code Review 06996718c3 Merge "repl: tab-complete quoted attribute names" into main 2024-12-11 03:27:15 +00:00
jadeandGerrit Code Review cb44fe7c15 Merge changes Ic890f781,I77b2de10,I3202aba0,I6ddc4296,I18776984, ... into main
* changes:
  doc: fix broken table in operators list
  terminal code eaters: implement OSC
  libexpr: significantly improve error messages for bad attr paths
  tests/functional2: add terminal code eater
  tests/functional2: fix occasional pytest haunting
  attr path parser: fix bug in not rejecting empty attr paths, add unparser
2024-12-11 02:28:11 +00:00
Ian ChamberlainandRebecca Turner 67f07e05df repl: tab-complete quoted attribute names
Attribute names containing special characters like @ or . need to be
quoted, so we need to do our own tokenization of the command line for
completion, and quote the attribute names when we provide the completion.

Fixes: https://git.lix.systems/lix-project/lix/issues/450

Change-Id: I55a30dd272880c89445d9ded49b3f2c90cb19326
2024-12-10 18:10:16 -08:00
beviuandRebecca Turner bc63b007e5 Mention support for ~/ paths in nix.conf in manual
Fixes: https://git.lix.systems/lix-project/lix/issues/497

Change-Id: I9606900d17f62359cef4fe2c8a01e5791390a870
2024-12-10 16:36:35 -08:00
Jade Lovelace f51943f171 doc: fix broken table in operators list
Fixes: https://git.lix.systems/lix-project/lix/issues/597
Change-Id: Ic890f781af10fbcab23deae5ab9c10eebfbab070
2024-12-10 15:43:31 -08:00
Jade Lovelace 5530de4673 terminal code eaters: implement OSC
This is a useful piece of functionality to being able to eat URL
hyperlinks, for instance, which is a bug that Lix has while dealing with
terminal output today.

Change-Id: I77b2de107b2525cad7ea5dea28bfba2cc78b9e6d
2024-12-10 15:43:31 -08:00
Jade Lovelace faf00ad022 libexpr: significantly improve error messages for bad attr paths
This commit makes Lix include the summarized content of the value being
indexed when it is bad.

lix/lix2 » nix eval --expr '{x.y = 2;}' 'x.y.z'
error: the value being indexed in the selection path 'x.y.z' at 'x.y' should be a set but is an integer: 2

lix/lix2 » nix eval --expr '{x.y = { a = 3; };}' 'x.y.z'
error: attribute 'z' in selection path 'x.y.z' not found inside path 'x.y', whose contents are: { a = 3; }
       Did you mean a?

lix/lix2 » nix eval --expr '{x.y = { a = 3; };}' 'x.y.1'
error: the expression selected by the selection path 'x.y.1' should be a list but is a set: { a = 3; }

Change-Id: I3202aba0e437e00b4c6d3ee287a2d9a7c6892dbf
2024-12-10 15:43:31 -08:00
Jade Lovelace c0808bd855 tests/functional2: add terminal code eater
I want this for being able to write reasonable expect-test style tests
for oneliners. We will still probably want something like insta for more
complicated test cases where you actually *want* the output in a
different file, but for now this will do.

cc: https://git.lix.systems/lix-project/lix/issues/595
Change-Id: I6ddc42963cc49177762cfca206fe9a9efe1ae65d
2024-12-10 15:43:31 -08:00
Jade Lovelace 93d5221b9b tests/functional2: fix occasional pytest haunting
I don't know what the heck the xonsh module is doing but its obviously
crimes so it has to go. It was never intended to be running here anyway.

Fixes: https://git.lix.systems/lix-project/lix/issues/593
Change-Id: I1877698469392f85884945aaa60987c68c4e0ebc
2024-12-10 13:32:28 -08:00
Jade Lovelace 21ad02c1d0 attr path parser: fix bug in not rejecting empty attr paths, add unparser
The following behaviour was previously present and has been fixed:

lix/lix2 » nix eval --expr '{x."" = 2;}' 'x.""'
{ "" = 2; }
lix/lix2 » nix eval --expr '{x."".y = 2;}' 'x."".y'
error: empty attribute name in selection path 'x."".y'

Change-Id: Iad21988f1191c33a3661c72a6b7f01a8b8b3e6eb
2024-12-10 13:32:28 -08:00
Jade Lovelace 8ddcdfb59c build: fix on x86_64-darwin
Lix requires a non-antiquated macOS SDK, and 24.11 does not yet have a
non-antiquated one as default.

Fixes: https://git.lix.systems/lix-project/lix/issues/588
Change-Id: Iad19c06d7fefe3a736cdcb39ced185e52dcfcbb8
2024-12-10 12:32:42 -08:00
Artemis TosiniandJade Lovelace 445b7c7d8a flake: Use nixpkgs 24.11
nixpkgs 24.11 changes how we access xonsh yet again
and updates clang.

Unfortunately, clang 18 produces significantly more
warnings on existing code that is challenging to fix.
Make sure that doesn't error when we're running
`-Werror` builds.

n.b. I had to change the "SSL certificate problem: self-signed
certificate" to the old error prior to the improved libcurl errors,
since what is presumably a difference in which TLS library is used has
cropped up between releases? Either way the curl error buffer is empty.
Seems like we aggressively cannot do anything about this.

Change-Id: If0141a46a8b445a0e7d6f86f939e8c8e03569bf5
2024-12-10 12:32:42 -08:00
jadeandGerrit Code Review 9a96086381 Merge "tree-wide: fix a pile of lints" into main 2024-12-10 20:29:53 +00:00
piegames 7c76053d93 libexpr: Undeprecate overriding __nixPath
It being overridable was an intended feature with good use cases, and
should not have been removed. However, this feature is generally in a
bad state and needs revisiting in the future.

Fixup for 81d5f0a7d9
Fixes #599

Change-Id: I2d93e012caa65aa795bce3a71d8e56d7052ef9df
2024-12-10 19:02:41 +01:00
vigress8 95e088de72 refactor(libutil): add new constructor to SysError using std::error_code
Change-Id: I6150d7966442551f0b6f7a0cc9739a4f9d6c4e7e
2024-12-09 20:43:40 +04:00
piegamesandGerrit Code Review 369e3f82f0 Merge changes I4771fe87,Ifc7a9516,I1f5e05ab,I63f2fbcd into main
* changes:
  libexpr: Rework error messages on ExprSelct::eval
  libexpr: Track position information in attrpaths
  libexpr: Assert: Don't print assertion in error message
  libexpr: Split ExprAttrs and ExprLet
2024-12-09 10:28:22 +00:00
piegames a2ae14bfd8 libexpr: Rework error messages on ExprSelct::eval
Calls to `show` have been removed. To counter the loss of information,
the error positions have been improved and now correctly point to the
current selector instead of the entire select expression.

Change-Id: I4771fe874af1ac15828a9863550cd4369a8f0e94
2024-12-08 20:52:18 +01:00
piegames 172515bd8b libexpr: Track position information in attrpaths
This is a pretty small change because the parser already has all
necessary information (needed for parse errors), now we want to keep it
to also provide better eval errors.

Change-Id: Ifc7a9516b9b0c8d9698f1899a6912ae91f6696ab
2024-12-08 20:51:45 +01:00
piegames cdda2454f6 libexpr: Assert: Don't print assertion in error message
The `show` functionality needs to be removed because it is deeply
flawed, and given that we already print position information in the
error message (which probably wasn't always the case in the past) the
assertion printing is redundant anyways.

Change-Id: I1f5e05ab73aaa0ec92994c2211463260fd374898
2024-12-08 20:51:44 +01:00
sethandeldritch horrors f7687fa414 libstore/linux: include sys/syscall.h
Per `seccomp(2)`, this header defines the `SYS_*` constants used

Found in https://github.com/NixOS/nixpkgs/pull/353576

Change-Id: I64ebc5513c53500b7a9012eaa664c2e59f49ceac
2024-12-08 18:49:27 +00:00
piegamesandeldritch horrors 9611018c27 libexpr: Split ExprAttrs and ExprLet
ExprLet was previously inheriting from ExprAttrs for the data, while
ignoring all
set-specific operations on it. The set specific code has now been split
off so that
let doesn't inherit it anymore:

- ExprAttrs (not an Expr), containing the attributes and the related
logic
- ExprLet : Expr, ExprAttrs
- ExprSet : Expr, ExprAttrs

Co-authored-by: eldritch horrors <pennae@lix.systems>
Change-Id: I63f2fbcd1e790b3cffb56eec1e7565ee3cdbf964
2024-12-08 12:54:38 +01:00
jadeandGerrit Code Review 2a9e560570 Merge "libcmd: munge evalSettings in the right place" into main 2024-12-06 01:25:12 +00:00
Jade Lovelace 53e0b6ecfb tree-wide: fix a pile of lints
Mostly these are bugprone-unused-local-non-trivial-variable.

Also fix instances of:
- bugprone-optional-value-conversion
- bugprone-inc-dec-in-conditions (please check this loop is correct, it
  is the only non trivial code change in here)
- bugprone-unused-return-value (well, by fixing the lint config)

There are three notable changes relating to undefined vars:
- openLogFile ignoring the result. This is because openLogFile does a
  whole bunch of mutation of member variables
- hiliteMatches: i am guessing this is because showing the derivation
  name was unhelpful and it just got changed
- canonPath in NarAccessor: canonPath inside of a thing that is supposed
  to be vfs based cannot possibly be correct, so let's delete it given
  it is unused.

Fixes: https://git.lix.systems/lix-project/lix/issues/584

Change-Id: I887adc9ff28b61f726dcfed197e6796b414c2fcf
2024-12-05 17:13:37 -08:00
eldritch horrors fb76a4d335 libcmd: munge evalSettings in the right place
these must be tampered with before the evaluator is created, *never*
after. doing it any other way leads to interesting things like #596.

fixes #596

Change-Id: Iea253ccce44b94b1243833837a3df93c795967d9
2024-12-06 00:59:53 +01:00
Jade Lovelace 9dd58224ea libexpr: rename misleading type alias Bindings::size_t
Calling a custom type size_t is incredibly sneaky and makes all the code
around this extremely context dependent.

Change-Id: Idae684781f45fe615020d8642f12a656ab2c15ad
2024-12-05 12:24:15 -08:00
eldritch horrors 61eed2c97c libexpr: fully separate evaluator and running evaluation
this finally gives us a witness type we can use to prove that a certain
call graph subtree can't be used in kj promises using only a single new
assumption: if EvalState& is never held as a reference member of a type
and instead only ever passes as an argument or held on the stack we can
be certain that anything that has access to en EvalState ref must never
be run inside a promise and, crucially, that anything that doesn't have
access to an EvalState& *can* be run inside a promise without problems.

Change-Id: I6c15ada479175ad7e6cd3e4a729a5586b3ba30d6
2024-12-05 13:30:35 +00:00
eldritch horrors 07a26fc776 libexpr associate caches and statistics with EvalContext
Change-Id: Ie4740070881bdd056b112ffd79634e30c29e8aaa
2024-12-04 23:50:31 +00:00
eldritch horrors 71c392ae0f libexpr: associate parsing and builtins with EvalContext
Change-Id: Iff56246bb344405c9acecb967423554d25fe47f5
2024-12-04 23:50:31 +00:00
eldritch horrors 7fa6e785ff libexpr: associate path config with EvalContext
Change-Id: Id25f990ab4ae9d9e76c91c07d7397689acf57eca
2024-12-04 23:50:31 +00:00
eldritch horrors 1f37fc85ac libexpr: associate memory with EvalContext
Change-Id: I791ae16cd42a674972c782a869b7d790880313ff
2024-12-04 23:50:31 +00:00
eldritch horrors 5db5eed43d libexpr: associate stores and repair flag with EvalContext
Change-Id: I5fd911415707f0b250888dcabdf6290691fcdbf3
2024-12-04 23:50:31 +00:00
eldritch horrors d30027a37a libexpr: associate error creation and debugging with EvalContext
Change-Id: I80beedd0c58361ebc67cebcee97d64879baed050
2024-12-04 23:50:31 +00:00
eldritch horrors 2f2bc90d92 libexpr: associate positions with EvalContext
Change-Id: I43e827a9119324c23a6d72cfc1b7ed4d5ff32dd2
2024-12-04 23:50:31 +00:00
eldritch horrors 014845ddf1 libexpr: associate symbols with EvalContext
Change-Id: I4ca99dc1f5183c7e7383db2b9c896c1946aadd29
2024-12-04 23:50:31 +00:00
eldritch horrors 8e74d1ceeb libexpr: extract persistent eval state to new struct
"persistent" here means state that outlives any particular evaluation,
i.e. everything that exist outside of a call to eval() and friends. do
note that this leave EvalState nearly empty, containing only things of
importance to evaluation *itself*, e.g. the call depth tracker. do not
worry about the self-reference ctx member. nothing to see here, go on.

Change-Id: Ie8d0af3f09d33902f81e0c36a1096dd9f334a537
2024-12-04 23:48:47 +00:00
eldritch horrors ce698198f0 libcmd: pass EvalState& to installables
for the same reason the eval caches should not keep them as references,
only worse: installables cannot fully lose their reference member since
some types of installable (flakes -sigh-) need access to caches held by
their member reference. passing that around as well at all times is not
feasible; passing in EvalState& all the time is quite bad enough as is.

Change-Id: I05d438623b5ef10fc22e7e256b89447e61a0657d
2024-12-03 20:38:41 +01:00
eldritch horrors 81559ea8ad treewide: add evaluator aliases for eval states
this is not necessary in any way, but it will make the following changes
smaller and easier to review. the aliases could also be added piecemeal,
but doing it here lets us lean heavily on our compilers for correctness.

(teacher notes: here the author foreshadows the shape of things to come.
not all names change, and only the names unchanged are those which will,
over time, become ever more unrecognizable. note especially nix/main.cc,
where `state` is not only cloned, but itself changes pointerness. it can
be seen as a nod to the trans community, but more realistically it is no
more than foreshadowing the future where `state` is only seen by proxy.)

Change-Id: I7732025e58df089b7f8e564fc63960cd91729d09
2024-12-03 20:38:41 +01:00
eldritch horrors 473c1bdcab libexpr: remove EvalState& from EvalCache constructor
it's not used for anything any more. it was only passed on to the AttrDb
constructor, which itself only stashed it away to an unused member field

Change-Id: I334fde751a3754e6580f573c21ae3d04be00345a
2024-12-03 20:38:41 +01:00
eldritch horrors 9ff702554d libexpr: eval caches should not have an EvalState&
and for the same reason DrvInfo shouldn't, only even more so. the eval
cache infrastructure wants to be a wrapper around evaluation, with its
lifetimes fully decoupled from the lifetime of the wrapped evaluation.
this can't not create problems when eval lifetime must become bounded.

Change-Id: Ib8eec649995b4decd7290c2266322f67d73b6b46
2024-12-03 20:38:41 +01:00
eldritch horrors 8468dc65c7 libcmd: DrvInfo should not have an EvalState*
in the future eval states will be asyncio root contexts. proving that
they are used correctly is impossible if the are referenced by things
with weakly scoped lifetimes, or entirely unscoped lifetimes, as we'd
have to deal with in the case of `DrvInfos` passed as out parameters.

Change-Id: I9ca5c46a714c6c914a77c7c7500cb4939ac9aff5
2024-12-03 20:38:41 +01:00
eldritch horrors f47cd0fc88 libcmd, libexpr: remove dead code
Change-Id: I555d81bd78770dd1b773c1e90f1a41779fccad7f
2024-12-03 20:38:41 +01:00
eldritch horrors d6a7fb5f6a cli: remove duplicated declarations
Change-Id: I78c6666cea8b6604a4fae9cb047662571bd6f99b
2024-12-03 20:38:41 +01:00
eldritch horrors a5d02bcdf9 libcmd: make a back-compat hack more robust
otherwise eval fails if getEvalState runs before parseInstallables runs.

Change-Id: Ia0d66dd6297212f7835c4a9b2db0fb4d9e4c9b9d
2024-12-03 20:38:41 +01:00
eldritch horrors 9a3e3a5560 libexpr: standardize on strings for attr cache traversal
it's all strings anyway. the db stores strings, the cli wants to
interact with attr paths as strings, so we will just use strings

Change-Id: Id9ea07d92343de77e8d47af8fec1e86ae225e9a1
2024-12-03 20:38:41 +01:00
eldritch horrors dd7d7450a5 libexpr: move eval statistics into new struct
there's no reason for these to be private. much more important things
that are now part of EvalState are not private, and having statistics
be private forces us to add otherwise unnecessary friend declarations

Change-Id: Ib37097d94a9f55c2b21969fb6c51049b1c914515
2024-12-03 20:38:41 +01:00
eldritch horrors 276a771210 libexpr: move thunk counter into eval state
this is relative to the current state. why was it made global?

Change-Id: I58b3ce0213e791a05ce9fd7a2cb2394b9454c653
2024-12-03 20:38:41 +01:00
eldritch horrors ea931d4d03 libexpr: mkThunk_ -> evalLazily
thunk creation doesn't have to be called that. thunks are more of an
implementation detail, calling it evalLazily seems a lot more clear.

Change-Id: I64bdac422eadb629002195cd6b6a89dce1b4a60f
2024-12-03 20:38:41 +01:00
eldritch horrors 32f7a93f71 libexpr: extract eval path handling into new type
Change-Id: Icf38113076a4dd0f8515a27b501f405033aec73b
2024-12-03 20:38:41 +01:00
eldritch horrors a65e9e5828 libexpr: extract eval error creation into new type
this will let us pass the capability to create debuggable eval errors
without having to pass an entire EvalState. we could pass symbols and
debug states around just as easily, but if we add new capabilities to
our debugger we might have to change many more places than with this.

Change-Id: I2f8893012e5d98a986ef1fc888234c2dd8d5e096
2024-12-03 20:38:41 +01:00
eldritch horrors a4fa93d469 libexpr: simplify EvalErrorBuilder interface
if we hold the error being built behind a pointer we no longer need to
allocate the error builder itself to avoid impacting eval performance.
adding && ref qualifiers to builder functions and adding the nodiscard
attribute to the class itself also makes the nodiscard attributes kept
on the builder functions unnecessary. we do keep the noinlines though,
removing them still regresses eval performance even after this change.

Change-Id: Ibc14a66955ac32142d97fb3680b0a7e14db250cd
2024-12-03 20:38:41 +01:00
eldritch horrors ef1d62ec6c libexpr: abstract DebugState from EvalState
this is necessary to share a debug state between multiple eval states.
while doings so makes little sense at present it will be necessary for
async io support since eval states must be async io roots, which means
we must create them as needed from a shared evaluation context object.

Change-Id: Id9d4b37aae40706f65c741e3b961855582e035ab
2024-12-03 20:38:41 +01:00
eldritch horrors 0bfa58ff53 libexpr: move runtime caches to new struct
Change-Id: Ic5492853d39eaffdf03d961b3e69c93c4a75ae33
2024-12-03 20:38:41 +01:00
eldritch horrors 2e5780ebc8 libexpr: simplify HAVE_BOEHMGC ifdefs
if we define a TraceableAllocator at all times and use that in places
that want maybe-traceable allocation we can simplify things a lot. we
also unconditionally allocate cache root pointers for Value and Env-1
caches, even though we don't need them without gc (they're so cheap).
defaulting to `std::allocator` without gc recovers previous behavior.

Change-Id: I236da8c3b0669b40cdfe355ec3ec4e764d096074
2024-11-29 17:55:08 +01:00
eldritch horrors 1e064e08fa libexpr: move builtin env to new class
Change-Id: Ib76891372be756917bddcf5eaa24ccf8e1288035
2024-11-29 17:55:07 +01:00
eldritch horrors 1ed0814859 libexpr: use eval() for builtin-using value printer tests
if we're relying on the behavior of builtins we can also use the parser.

Change-Id: I682722b2a22fbb6e748da2cab1bfeb76788bfb48
2024-11-29 17:45:04 +01:00
eldritch horrors 12802b492a libexpr: treat derivation like a normal primop for baseEnv construction
that way we don't need to eval while initializing the evaluator. this
will make a lot more sense once we start pushing asyncio through here

Change-Id: I3663052438ed97d48e213b71395ad1dd5e1318bb
2024-11-29 17:45:02 +01:00
eldritch horrors e9520ffd06 libexpr: add hidden pos origin for builtin code
currently only used to hide the nix code of derivation from stack traces
and debug frames, but perhaps we'll find it useful for other things too.

Change-Id: Ie5667873d8858d25dd4113bdf454e800b59082d7
2024-11-29 17:42:15 +01:00
eldritch horrors 63006438c4 libexpr: remove state reference from EvalError
nothing actually needs it. EvalErrorBuilder can have its own reference.

Change-Id: I8bf301d03a9c161519c130c95e58b1d5400e6411
2024-11-29 17:42:15 +01:00
eldritch horrors e44dbfe97a libexpr: move flake.nix attrsetness check out of eval state
this doesn't belong here, getFlake should be handling this instead. we
do lose the debug frame for loading a root flake, but that seems fine.
there's no point in starting a debugger if the root isn't even a flake

Change-Id: I24ad32b6716baee81a1a0f8bf9ce26814d97c7aa
2024-11-29 17:42:15 +01:00
eldritch horrors 94bb66c867 libexpr: remove maxPrimOpArity
this was only used to enable an optimization we no longer need such
strong guarantees for: SmallVector can be used with a dynamic size.

Change-Id: I4513fb7e665827fe363ce6308448656e6c5badb7
2024-11-29 17:42:15 +01:00
eldritch horrors 85d600ca4d libexpr: drop bison-specific \0\0 input trailer
bison needed it for internal reasons, the new parser does not.

Change-Id: I91f7aa23fb151e3152eee793a4fdce423fcf98d9
2024-11-29 17:42:15 +01:00
eldritch horrors 37aeb3059d libexpr: remove EvalState::addErrorTrace
Error::addTrace exists and is used far more often already. let's
standardize on the variant that doesn't need yet more templates.

Change-Id: If66b69ca02dbb546ce98cf385181bd13ce7ad9b5
2024-11-29 14:19:31 +00:00
eldritch horrors 105d8ceb36 libexpr: remove templated forceAttrs
the callback is always called immediately, which defeats the purpose of
the callback and the purpose of the template itself. there seems to not
be any performance impact of this. optimizing Value::determinePos would
be nice, but it's not used nearly often enough to matter at this point.

Change-Id: I2aec6a38103630652112f4b273653f11d2404c04
2024-11-29 14:19:31 +00:00
eldritch horrors 66d7128512 libexpr: remove EvalState parse caches
nothing needs these any more. the CLI calls evalFile, but only in places
where it'll only be called *once* per process lifetime. __import does so
too, but import doesn't care about the parse tree, only the eval result.

interestingly this improves eval performance by 6% on system eval unless
GC never occurs. no idea why this makes a GCing eval faster, but it does

Change-Id: I8289528550244e0a8b5ebc7284d8fb9aaac59e20
2024-11-29 13:29:31 +00:00
eldritch horrors f815b966c4 doc: remove utils.nix
only generate-manpage.nix uses it any more, so we can inline it there
instead of keeping it around as a separate generated header. doing so
will also allow us to remove caching functions needed *only for this*

Change-Id: I97ee91f1dd7140ecb69dbafd8479b82fba7981b8
2024-11-29 13:29:31 +00:00
eldritch horrors 2297d3f895 doc: remove obsolete files
these must've been forgotten during the move to generated builtins.

Change-Id: I0989847abc020e9356b996f26196d2c07953f77b
2024-11-29 13:29:31 +00:00
eldritch horrors 7c650ea241 libexpr: remove eval caches from EvalState
eval caches are not used by actual eval at all, only by the flake-shaped
wrappers around evaluation. moving caches into a subclass both clarifies
that eval caches and eval states are coupled and separates concerns that
should not have been intermixed as they were here. in the future we will
want to split up and decouple things even further. that'll have to wait.

Change-Id: I7b69510c0f8b212f05fae62e7b992d9475b4841f
2024-11-29 13:29:31 +00:00
eldritch horrors 564f464772 libcmd, nix: remove duplicated arguments
installables already have a ref<EvalState>. why are we passing the same
eval state in again, by reference, everywhere? that's just unnecessary.

Change-Id: I8225ea2575146edc55d283c0b5173b804553ceec
2024-11-29 13:29:31 +00:00
piegamesandGerrit Code Review 9fb5315d06 Merge "libexpr: Deprecate overriding __sub and the like" into main 2024-11-29 07:56:17 +00:00
piegames 81d5f0a7d9 libexpr: Deprecate overriding __sub and the like
It was never intended to be a feature to be used, and moreover it is
inconsistent: One cannot override `+`, and overriding `__lessThan` won't
affect the builtins which do comparisons.

Change-Id: Iaba54a05aa4c2eb37cdb3dc0d731fcee5a86deba
2024-11-28 18:15:52 +01:00
eldritch horrors f5754dc90a libexpr: move eval memory allocation to own struct
Change-Id: I9d472c9606fe66fdc1cb7cb9dcf6d1b6b46c6686
2024-11-28 15:12:22 +00:00
eldritch horrors cb8262e11c libexpr: remove EvalState::rootPath
this belongs to lazy trees, which we neither have nor intend to have.
we will keep SourcePath as that may come in handy at some later date.

Change-Id: I44b8f1dd6c435d7486c393fabdcd272766b2b56b
2024-11-28 15:12:22 +00:00
eldritch horrors 3593f5555e libexpr: handle debug trace frames as parent pointer list
this also fixes a debugger bug where leaving the debugger does not clean
up old debugger state completely. in such cases the fake frame withFrame
created was left behind after the corresponding caller frame was unwound

Change-Id: I45adcd116276b03b2f87076518c9eae6fe844e06
2024-11-28 15:12:22 +00:00
eldritch horrors 985afeeb4d libexpr: allocate debug state only when debugger is active
Change-Id: Id30f388264c5d1e472e3bdce5078db3914f3b475
2024-11-28 15:08:35 +00:00
eldritch horrors 35cb0cb28b libutil: make enumerate iter deref non-const
there's no need for this. no concept requires unary deref to be const.

Change-Id: I4e4592c5917382ad2dcc70e5d3f12662614b2fd1
2024-11-28 14:49:23 +00:00
eldritch horrors 5892ed2731 libutil: make generators iterable
range-for on generators sounds like a pretty good thing, right?

Change-Id: Ibed5e038c8dc50c918cf7c1f1aa70d822fb3efa2
2024-11-27 02:09:08 +01:00
eldritch horrors 89a0ddc108 libcmd: don't enter debugger immediately on nix repl --debugger
checking whether a repl hook is set does not tell whether it's running,
which in turn means we will enter a debug repl even when not debugging.
this is not very useful when we have no debug frames to inspect at all,
as commonly happens when starting a new repl with --debugger specified.

Change-Id: I7065dc4ec29743bdd53ed99c29d6592e2ceea89c
2024-11-27 02:09:08 +01:00
eldritch horrors 9554a1ae29 libexpr: remove dead code
Change-Id: Ife3a23822102b6038401e4944777392836039472
2024-11-27 02:09:08 +01:00
eldritch horrors 003883306d libexpr: move debug repl state into own struct
just to keep debug-related state closer together. eventually this will
also allow us to not allocate debugger state altogether unless needed.

Change-Id: Id83fea75d96d0ecbe21683cb1b57dd5b11b13535
2024-11-27 02:09:08 +01:00
eldritch horrors e635db77e3 libexpr: only runDebugRepl with EvalErrors
only break ever passed a non-EvalError, and even that was erroneous.

Change-Id: I03bb74d7fc449d9bab1e7d8e774389d2381ab457
2024-11-27 02:09:08 +01:00
eldritch horrors 7af000ddff libexpr: move symbols into own struct
not all of these are even used by eval itself. notably sWith wasn't used
at all, sEpsilon was only used by the eval cache one layer up, and other
attributes are used in places even further away from eval itself. we can
keep this commingling for now, but eventually we should clean it up too.

Change-Id: I5684ac614361bf008e04472130c6c02082b4c2d7
2024-11-27 02:09:08 +01:00
eldritch horrors f017f9ddd3 libexpr: extract some global constants from EvalState
these do not rely on the GC being initialized. there's no reason for
them to not be statics, shared between all eval states in a process.

Change-Id: Ib9675f3945d3a0a7097d6c85096adcbd6f441d83
2024-11-27 02:09:08 +01:00
eldritch horrors 650809f66e drop unnecessary EvalState shared pointers
currently ref<>s are used to share eval states between whatever created
that state and the various repl instances. repls however do not own any
eval states, not even partially. they only provide interactive use of a
previously allocated state that is owned by something else, notably for
debug repls which are run from *within* an evaluation which may already
be wrapped by another repl instance. presumably this was originally set
up like this to guarantee memory safety, even though it's never needed.

Change-Id: I29a2cd1d4245c077f9270452a0873d4c47448729
2024-11-27 02:09:08 +01:00
eldritch horrors f5cddcfc09 libcmd: make Repl instances proper scopes
there's no reason to ever *own* a repl instance. everything either wants
to run a repl and receive the result of that run, or run a repl and then
totally ignore the result. allowing non-transient repl instances doesn't
do anything useful for us, unless making ownership analysis hard counts.

this also fixes a bug in which a repl could ignore the store it was told
to use during construction, using the one returned by openStore instead.
this never showed up in lix because openStore() was always passed anyway

Change-Id: If2b1ad24ab74377340199b36af1dd629d7ce3f25
2024-11-27 02:09:08 +01:00
eldritch horrors 66f6dbda32 libstore: remove an unused function
Change-Id: If5e24046409f3c1d444e558306a98f3eba5845f4
2024-11-20 14:17:02 +00:00
eldritch horrors a911b44a31 libstore: break up QueryMissingContext::doPath
std::visit makes coroutines nearly impossible to use correctly.

Change-Id: I085c08b595a5d4a43208eb97f023aa00b48bff45
2024-11-20 14:17:02 +00:00
eldritch horrors 74d820e5e8 libstore: move Store::queryMissing into a fresh struct
we want to get rid of all these local lambdas, they make coroutines impossible.

Change-Id: I94336f0844e9cdb0aa27788d0ee5f99925f24e0c
2024-11-20 14:17:02 +00:00
eldritch horrors 9cf91b7385 cli infra: modernize legacy command interface
give legacy commands their program name and c++-converted argv directly
instead of passing on the (argc, argv) pair untouched. all are required
to process these things, and all of them do it in exactly the same way.
we can also remove a few old helpers only used to make this less awful.

Change-Id: I4ecd02343bca0cf85faf6fe043031d4f64c5f29c
2024-11-20 14:17:02 +00:00
eldritch horrors c4a077d0b8 libcmd: drop non-default ctors virtual inheritance
it's a trap. it's always a trap. non-default ctors of virtual base
classes must be called with the same parameters every time or very
surprising things will happen. good luck if you virtually derive a
class more than once with different ctor arguments. that'll break.

Change-Id: I1345963e69e98f37c3cbd070bae164d89eade455
2024-11-20 14:17:02 +00:00
eldritch horrors ac74dc3a2b treewide: drop trivial leaf virtual inheritance
this does nothing on classes that are never used as base classes and
have only a single base class of their own. all of these classes are
also made final to prove that they are indeed in this category. once
we need to derive any of them we can erase their `final` specifiers.

Change-Id: I4bd8e50a58815227392b5a5a762fda4542d15bae
2024-11-20 14:17:02 +00:00
eldritch horrors 34e592ea6a libcmd, nix: drop NixMultiCommand
there are no uses of plain MultiCommand as a base class *except* in
NixArgs, which is the only one that does not implement run(). there
is not much of a reason not to implement a run member there though,
so let's just do that and get rid of this weird intermediate class.

Change-Id: Ie84e3acd071b43bc186a2bac87646cbfb3aff845
2024-11-20 14:17:02 +00:00
eldritch horrors 95a9a4cece libstore: don't derive store classes from their configs
the very slight speedup in config setting access is not worth the
maintenance overhead of conflating concers like this. the virtual
inheritance scheme used for configs requires too much duplication
of base class constructor arguments to be worth doing. perhaps we
should get rid of all virtual inheritance of data-membered bases?

Change-Id: I4acf5ceaedb4ed7476efe1114c2e065ec72d2c6d
2024-11-20 14:59:40 +01:00
eldritch horrorsandjade b0d7a81613 fix tooling after include reorganization
clangd broke because it can't look through symlinks. compile_commands
manipulation does not fix it, clangd configuration does not fix it, a
vfs overlay does not fix it, and while a combination of those can fix
it with a bind mount in place that's just too cursed to even consider

clangd bug: https://github.com/llvm/llvm-project/issues/116877

Change-Id: I8e3e8489548eb3a7aa65ac9d12a5ec8abf814aec
2024-11-19 22:55:32 +00:00
jadeandGerrit Code Review f116608a20 Merge "libstore: abort all curl transfers on interrupt" into main 2024-11-19 05:03:47 +00:00
jadeandGerrit Code Review 0bec915522 Merge "unnamed threads: Obliterate" into main 2024-11-19 05:02:48 +00:00
Jade Lovelace 519957bd59 unnamed threads: Obliterate
Ever read gdb output and you just kinda get a headache because you have
to infer what a thread is by reading the stack trace? It's not hard, but
we could also just never have to do that again, which is also not hard.

Sample:

(gdb) info thr
  Id   Target Id                    Frame
* 1    LWP 3719283 "nix-daemon"     0x00007e558587da0f in accept ()
   from target:/nix/store/c10zhkbp6jmyh0xc5kd123ga8yy2p4hk-glibc-2.39-52/lib/libc.so.6
  2    LWP 3719284 "signal handler" 0x00007e55857b2bea in sigtimedwait ()
   from target:/nix/store/c10zhkbp6jmyh0xc5kd123ga8yy2p4hk-glibc-2.39-52/lib/libc.so.6

The API design for this is forced by the macOS pthread_setname_np only
being able to change the current thread's name, but if we just conform
everything to that, it works everywhere.

Change-Id: I2b1d6ed41e3c94170cb0b4e73ad66f239ebd9c88
2024-11-18 18:53:40 -08:00
jadeandGerrit Code Review 950c213ddf Merge "feat: better warning for common SSL errors" into main 2024-11-19 02:48:24 +00:00
eldritch horrors 16bed313c6 libstore: abort all curl transfers on interrupt
give the transfer progress callback a chance to register all transfers
as aborted when an interrupt event occurs. checkInterrupt() will throw
an Interrupted exception if an interrupt signal was caught, which will
immediately break out of the curl loop. it isn't even necessary within
the curl main loop as curl guarantees to call progress callbacks about
once per second (or more often), and we already abort transfers from a
progress update if a signal was caught. this may delay shutdown a bit,
but "about one second" as should not be noticeable in most situations.

fixes #577

Change-Id: Ida4e72732562bdf6560e4f182ecdcdb663c6dda5
2024-11-19 02:49:41 +01:00
jadeandGerrit Code Review 33e832ba5a Merge "manual: Make link to install page clickable" into main 2024-11-19 01:47:03 +00:00
Tom BereknyeiandJade Lovelace 6334e91b2f feat: better warning for common SSL errors
(cherry picked from commit 3e5bf903413f420c1f997e4b55140761172b8434)
Upstream-PR: https://github.com/NixOS/nix/pull/11246
Change-Id: Iccd92721bbcd92604a4b0e5c845242bdf3dd0f66
2024-11-18 16:49:35 -08:00
Jade Lovelace fbdb177b31 manual: Make link to install page clickable
Closes: https://github.com/lix-project/lix/pull/11

Thanks to xsova for suggesting this.

Change-Id: I5958fa4438f4b83d928e8f309aab9a33fb4cbedd
2024-11-18 16:32:20 -08:00
Jade Lovelace 6a9b66357e pkg-config: add the base lix include-dir as an include-dir
This allows <lix/*> includes to work from external projects not taking
Lix as a buildInput with Nix, but just using pkg-config.

Change-Id: I5b24fd0981344b013beaf406c16988dad17f0edb
2024-11-18 12:31:41 -08:00
Jade Lovelace 13f54451e6 meson: remove the ability to use unqualified includes from inside Lix
These are still supported externally with pkg-config, but this commit
ensures they no longer build internally.

Change-Id: I2fa38f85e43b18f1f1dbcfdb0367de8f192aca2c
2024-11-18 12:31:41 -08:00
Jade Lovelace 103cd44a04 Include rearrangement: rest
Change-Id: If9b5502ff348d358d7007b885e26e98a0d228f39
2024-11-18 12:31:19 -08:00
Jade Lovelace eeaa72b62b Include rearrangement: libexpr
Change-Id: Ib230a3021d544d3ab1376542b73ea1df1a220a3c
2024-11-18 12:22:39 -08:00
Jade Lovelace 94ca95714e Include rearrangement: libstore
Change-Id: I27c83510d9053bb8bdd7eda9ad3a4b77d633056a
2024-11-18 12:22:39 -08:00
Jade Lovelace df0e1993b7 Include rearrangement: libutil
Change-Id: Ibf4ee57e430ce1cd175f994d1db7964c8407ffa7
2024-11-18 12:22:39 -08:00
Jade Lovelace b62cc7b361 meson: prepare for include rearrangement
Context: we have include paths that are "types.hh" and similarly common
names. We currently have these compatibly available as
"lix/libutil/types.hh" externally but *not yet internally*. This is
because we don't have any way for the src directory to appear as
`"lix/"` from inside of Lix: the lix/ include directory is created by
the install process.

The goal of this whole thing is to make it clearer which component of
Lix that files are a part of, which should hopefully help at least a
little bit to new developers. One disadvantage of un-mixing these is
that it will cause some API changes if we ever move a file between
libraries, but that is not very common, and we don't care that much
about external API users.

This was planned for a while and is why we have a FixIncludes check to
begin with.

Personally I don't see a great benefit in rearranging our source code,
and in fact, it would probably be counterproductive:
- Moving the includes into a separate `include/` directory would just
  make developers have to deal with more directories, when we can
  already generate the desired layout through the build process.
- This would also decouple the .cc and .hh files which currently
  conventionally have each others' definitions and declarations
  respectively, right next to each other, making it easier for them to
  feel decoupled and diverge.

Content: Add ../include as an include directory so that lix/ in include
paths will resolve to src/ within Lix itself, just as it does externally
today. This prepares for a further series of commits applying the actual
change to each library one-by-one by accepting both include versions at
once.

This could have been done with ../ and a symlink called lix, but we
would like to not accept libexpr/foo.hh internally for it would be
broken externally, so we need an otherwise empty directory for the
include.

Change-Id: Ideac17faadae2bcea2dffbab34eb27c582ede399
2024-11-18 12:22:39 -08:00
eldritch horrors c859d03013 libfetchers: remove scheme static initializers
provide a initLibFetchers function instead that runs them explicitly.

Change-Id: Ida532e6ff18b72fdc21c9f98df7aceda6713e5df
2024-11-15 16:15:11 +01:00
eldritch horrors 8088927b90 libexpr: initGC -> initLibExpr
we'll put more stuff in there shortly. unfortunately.

Change-Id: I74be957469c3dcd5157f999e2cf23b1c668426d8
2024-11-15 16:15:11 +01:00
eldritch horrors 3dba1d341a libstore: remove TransferItem::phase
transferComplete has outlived its usefulness, and the other two phases
can be a boolean flag telling us whether we're still receiving headers

Change-Id: Ia943f95dcf0ce8ee2cc6d0f44ddf13151aef4346
2024-11-15 16:15:11 +01:00
eldritch horrors 3b1298efce libstore: simplify TransferSource::read
things can be much simpler if the read loop knows whether the transfer
is done or not. this information is available, let's pass it back down

Change-Id: Idb99afeb06a0d7a0999c3f4a1c6ce5adeab5f054
2024-11-15 16:15:11 +01:00
eldritch horrors b22f2bc8e9 libstore: use http for exceptionAbortsDownload tests
file:// urls are no longer handled by curl itself, and this is supposed
to test the curl wrapper. use http to force the wrapper to be involved.

Change-Id: Ib13087db07b3b2f1ae44ce8e3ec7a96d935b1bab
2024-11-15 16:15:11 +01:00
eldritch horrors 986a98d32f libstore: remove curlFileTransfer reference from transfers
they don't need these any more if we consider unpausing and cancelling
of transfers a cooperative affair between transfers and the curl_multi
handler. we can also remove an enable_shared_from_this base class now.

Change-Id: Iefa380df60c0cada53719cdaa500bab953892319
2024-11-15 16:15:11 +01:00
eldritch horrors 4ba19f68fb libstore: improve curl wrapper resource handling
wrap all curl pointers in unique pointers for move correctness. the
destructor of TransferItem can go away completely since it does not
do anything special any more: memory bookkeeping is done using raii
wrappers, and its effects on the transfer state are not observable.
only TransferSource inspects the result, and even it does not check
for transfer results in its own destructor (only during transfers).

Change-Id: Ibb58484703854d5d5425e7c63cf2985d93920e97
2024-11-15 16:15:11 +01:00
eldritch horrors 43777939eb libstore: simplify download buffer handling
keep the download buffer in TransferItem. keep only *one* download
buffer (not one for success returns and one for failures). we also
remove the asynchronous callbacks since they are no longer needed.

Change-Id: I9b1c1e4c59d5333602aea1c36e477c481eaaf98e
2024-11-12 14:01:57 +01:00
eldritch horrors 7d7e16ecec libstore: use curl-builtin read callback
when no custom READFUNCTION is set curl will fall back to using its
READDATA as a FILE pointer and calling fread on it to retrieve data
to send to the peer. we can wrap our upload data in a FILE pointer.
doing so also allows for upload retries, but we don't do that here.

Change-Id: I0509f936229dd978e89135862dacc8a3937cd4de
2024-11-12 14:01:57 +01:00
eldritch horrors 5be7956592 libstore: move remaining retry state out of TransferItem
Change-Id: Ie2c22722bdda18027017e12d10e16931a694743e
2024-11-12 14:01:57 +01:00
Emilia BoppandGerrit Code Review 8a9094303b Merge "libutil: fix non-ASCII chars in URL encoding" into main 2024-11-11 14:08:37 +00:00
Emilia Bopp a378c61948 libutil: fix non-ASCII chars in URL encoding
Due to the cast to (unsigned int), the encoding appended broken
bytes padding. This is fixed here with a bitmask.

Fixes: https://git.lix.systems/lix-project/lix/issues/562
Change-Id: I0c93bd2b8c2f82df208d4693b7254544e3121dc3
2024-11-11 14:26:03 +01:00
Alois Wohlschlager d0bcdfa6c4 libmain/progress-bar: actually don't show short activities
The progress bar is not supposed to show very short activities (<10ms) in order
to prevent excessive flickering. This behaviour was inadvertedly disabled in
commit da4e46dd1f due to not actually skipping
the short activity after calculating that it should have been. Fix this
oversight.

Fixes: https://git.lix.systems/lix-project/lix/issues/561
Change-Id: Ibee256a7018cdc431662cd8ea9f6c14ef1706972
2024-11-10 20:00:42 +01:00
Alois Wohlschlager d1d96cc4c8 libexpr: fix install_dir typo
Change-Id: I29f123e29115954d0395c1822128731117fba961
2024-11-10 11:44:48 +01:00
alois31andGerrit Code Review 27af6989bb Merge changes I7b4379a9,If048b73a,I4c802052 into main
* changes:
  libexpr: generate builtins from data
  treewide: generate global settings from data
  libutil: generate experimental and deprecated features from data
2024-11-10 10:19:05 +00:00
eldritch horrors efb04754be libstore: inline TransferItem::init into constructor
this function is called only once per object lifetime, and it's no
longer written such that it must be called by the curl thread. all
curl options set here are independent of the multi handle, there's
no good reason any more to split the constructor in two like that.

Change-Id: Ibfa9075bc1cfdbab979f995c5a942b47add9789d
2024-11-09 20:08:48 +00:00
eldritch horrors 001a078b96 libstore: move file transfer retry handling out of TransferItem
this simplifies the immediate curl wrappers significantly and clarifies
control flow for retries. we can almost see the promise land from here!

Change-Id: Idc66b744631eec9c1ad5a2be388beb942a04f1f9
2024-11-09 20:08:48 +00:00
eldritch horrors d75c399b29 libstore: delocalize TransferSource
future changes will need to add template functions to this class, which
cannot be done for classes declared locally in unctions. otherwise this
is mostly code motion to clean up enqueueFileTransfer a little further.

Change-Id: If9a9d9eb47ceadfa75a4eebd54e2db39f2305643
2024-11-09 20:08:48 +00:00
eldritch horrors 1338e93bc9 libstore: remove curlFileTransfer::enqueueItem return type
it's used only once, and the transfer is used for something else
afterwards as well. returning it hides that the same transfer is
being returned unchanged, and pointlessly extends the signature.

Change-Id: Idbd0af00f9aebd3606f6b537b9b7cc9263a45cf7
2024-11-09 20:08:48 +00:00
eldritch horrors 60e984f0cf libstore: allow explicit cancellation of transfers
we only wait for the worker thread to remove the handle from its multi
and then signal us back. after that's done the transfer can be cleaned
up independently of the multi handle and no transfer progress is made.

we also no longer remove transfer items from multi handles in the item
destructor; only the worker thread can add a transfer and as such only
the worker thread should be responsible for removing them again later.

Change-Id: I6ff57bb5e7c1e91faf8657b257e73d6a72aa928e
2024-11-09 20:08:48 +00:00
eldritch horrors 40cf413a48 libstore: make acceptRanges a function
we don't need to keep this as state. it's only used for retries anyway.

Change-Id: I807f161a516a226567972757ec07ff91b3cf0899
2024-11-09 20:08:48 +00:00
eldritch horrors 90536e27e1 libstore: simplify TransferSource::awaitData
don't pass in a lock that's only used inside this function.

Change-Id: I15c01e9cfe343cf13828ae3b3742c36ae697291f
2024-11-09 20:08:48 +00:00
eldritch horrors 5d02800e57 libstore: do not retry FileTransfer uploads
this only ever worked for empty uploads, and there it worked only by
complete accident: curl was asked to send more data than the wrapper
would provide, which curl would not like and report as an error. the
error would cause a retry with even less data to send, until finally
failing by running into the retry limit. let's just forbid all this.

Change-Id: I229a94b3b8b33e2c6cdb8ea19edd57cd6740e6c6
2024-11-09 20:08:48 +00:00
eldritch horrors 40be91afbf libstore: add filetranfer retry handling tests
we did not have any, despite retry handling being somewhat complex.

Change-Id: I5051a1c0a3861849ff67f512b33f6d3dda12cc95
2024-11-09 20:08:48 +00:00
eldritch horrors fb85228755 libstore: extract eager transfers into new method
we'll need this shared code for kjified transfers that don't use
sources. it's a while out, but we can clean this up now already.

Change-Id: Ife8c160e6ab379761362d6c54aba05093deee99e
2024-11-09 20:08:48 +00:00
eldritch horrors 12156d3beb libstore: fix download thread notifications
since 4ae6fb5a8f dropping a source of a
download might not properly cancel the associated curl transfer after
the transfer was paused. we have also not unpaused the transfer often
enough, only if the transfer buffer had been drained in its entirety.

Change-Id: Ic9298d9df71daa0f3d1c3fd718ed441edae9e863
2024-11-09 20:08:48 +00:00
Alois Wohlschlager c1746300a9 libexpr: generate builtins from data
Most builtins are now generated from data too, with two exceptions:
* Undocumented builtins, since supporting them would add complexity to the
  generator, the harms of the current implementation mostly don't apply, and
  the proper fix is to document them.
* `derivation` is somewhat magic (it is a function, but defined in the code as
  a constant), so the current treatment of having it separately documented is
  kept (for now, at least).
Since it is slightly easier to do and probably a good idea anyway, the builtin
function registrations generated this way are now processed directly in code
and don't go through global variables any more.
Unfortunately, a slight breaking change is introduced because the order of the
builtins' names in the symbol table changes. Hopefully, this will turn out to
not matter in practice.

Change-Id: I7b4379a93ae380b6524e41a916a21c5c6f70555e
2024-11-09 16:17:26 +01:00
Alois Wohlschlager 2e0c1a5ea9 treewide: generate global settings from data
Change-Id: If048b73a55d42522827eb9c140a066ba061e957c
2024-11-09 16:17:14 +01:00
Alois Wohlschlager 21fc0ddce5 libutil: generate experimental and deprecated features from data
Currently, a bunch of documentation is generated by embedding parts of it in
the nix executable, getting it out again by running it, and then postprocessing
the output. This is bad, since it creates a pointless dependency of the
documentation on the executable, and also makes documentation generation
impossible when cross-compiling.
Instead, both the code and the documentation should be generated from data, see
https://git.lix.systems/lix-project/lix/issues/292 . Here we start applying
this approach to the experimental and deprecated features, which are done in
one go since the technical implementation is very similar.
Of course, the actual benefits are not realised yet, since the offending
pattern is used in several more places. These will be fixed later.

Change-Id: I4c802052cc7e865c61119a34b8f1063c4decc9cb
2024-11-09 16:05:12 +01:00
alois31andGerrit Code Review b967f1d5fe Merge "libutil/config: fix appendable options not getting marked as overridden" into main 2024-11-09 15:01:53 +00:00
Alois Wohlschlager 08a362a540 libutil/config: fix appendable options not getting marked as overridden
Commit 4dbbd721eb intended to mark all settings
as overridden when they are. Unfortunately, due to an oversight, this marking
was accidentally performed in the implementation details of non-appendable
options. Move it to the common codepath so that it works for appendable options
too.

Fixes: https://git.lix.systems/lix-project/lix/issues/573
Change-Id: Idc3402bac48b19d832acd9b553e16e5791470c26
2024-11-09 12:08:24 +01:00
Lily BallardandGerrit Code Review d0e0969810 Merge "feat: Add temp-dir setting" into main 2024-11-09 08:44:58 +00:00
Maximilian BoschandGerrit Code Review 116895acb1 Merge "libexpr/flake: (opinionated) changes to interactive flake config" into main 2024-11-08 17:35:38 +00:00
Maximilian Bosch 86eddb9e27 libexpr/flake: (opinionated) changes to interactive flake config
So I recently saw it the first time in the wild, I liked that you get
interactively asked about the nix.conf settings from the flake, but
there were a few minor things that I'd like to see changed:

* The `(y/N)` was somewhere in the middle of the line. Moved it to
  the end. At first I assumed it was a bug because another thread into
  my terminal while I was answering the question.

* I had to say no four times for a single flake with two options. So if
  you already know you don't want any of the config for _this_ flake, I
  found a `No to all` switch that ignores the rest of the nix.conf
  settings a little more ergonomic than having to stop the invocation,
  looking up the exact wording of `--no-accept-flake-config` and
  restarting it. Hence, I added it.

* Added a note where the choices which settings to trust are persisted.
  My initial assumption was that this went into `nix.conf` which is not
  writable on NixOS, so I said no there as well.

Change-Id: I0a0d9c403f0662df4707697a77f08e6cd003ec6f
2024-11-07 21:47:26 +01:00
Lily Ballard 3c8096e5cb feat: Add temp-dir setting
This adds a new temp-dir setting for controlling the temporary directory
without having to change the TMPDIR env var. This can be used to e.g.
use a path on a case-sensitive store on macOS for temporary files
without changing the TMPDIR var used by interactive shells or commands
invoked with `nix run`.

This also stops unsetting `TMPDIR` on darwin when the env var value
starts with `/var/folders/`, preferring instead to just do the check
when reading `TMPDIR`. This way the inherited `TMPDIR` env var is
preserved for child processes (such as interactive shells).

As a side effect this changes the behavior of `nix-build -o ''` to act
like `nix-build --no-out-link` instead of failing with an error caused
by trying to create a symlink at the cwd.

Fixes: https://git.lix.systems/lix-project/lix/issues/253
Fixes: https://git.lix.systems/lix-project/lix/issues/112
Change-Id: I9ee826323f2deca62854715a77ca7a373a948a29
2024-11-06 18:11:47 -08:00
Lily BallardandGerrit Code Review 72cce7be3f Merge "libstore: replace random() calls with atomic counter" into main 2024-11-07 00:15:23 +00:00
V.andGerrit Code Review 1ecfff9c37 Merge "fix: make static build work again" into main 2024-11-06 22:49:56 +00:00
Kiara GrouwstraandGerrit Code Review 72292671a9 Merge "fix(libfetchers): set GitHub API version header, closes #255" into main 2024-11-05 22:44:29 +00:00
eldritch horrors 1c28270c9d libstore: don't hold state lock while unpausing transfers
libcurl may call callbacks during unpause. if libcurl calls these
callbacks often enough they may find that they've exhausted their
allotted buffer space, at which point they will call dataCallback
as provided in enqueueFileTransfer. download() provides callbacks
that have their own state locks and may call unpause on transfers
with *their* state locks they share with curlFileTransfer. it was
possible to cause a deadlock between these two if the curl worker
thread tried to provide some data to a callback with the transfer
state lock held and the user transfer simultaneously unpaused its
associated curl transfer, with its own state lock held. obviously
not a great situation, but avoidable by not holding any lock when
we unpause transfers and execute their callbacks, as is otherwise
the case when a transfer runs normally and is never paused at all

Change-Id: I58556d292adaf7dfb14001d3a6c5c38fa71994da
2024-11-04 23:54:41 +00:00
kloenkandGerrit Code Review 6b7076f81c Merge "add .mailmap" into main 2024-11-04 13:16:16 +00:00
Fiona Behrens d73211356a add .mailmap
Add .mailmap file to allow display/email changes in the git log
without rewriting the history

Change-Id: Ie507aba563cd4fa8ba24e65269aefc647c6376ed
2024-11-04 13:46:10 +01:00
Lily BallardandGerrit Code Review b1a0e3c002 Merge "libstore: remove unnecessary lstats in optimisePath" into main 2024-11-03 05:15:40 +00:00
Raito BezariusandGerrit Code Review 763a61bb7c Merge "Revert "Reject weak hash algorithms as SRIs, and warn in any other cases"" into main 2024-11-02 19:50:52 +00:00
Artemis TosiniandGerrit Code Review 7df8b15b39 Merge "libstore: Fix FreeBSD build" into main 2024-11-02 17:20:46 +00:00
Artemis Tosini 3aad088cf1 libstore: Fix FreeBSD build
FreeBSD was left out of a few refactors over the last few months.

Add a header and register the store implementation so it's back
to working as well as it was before.

Change-Id: I6f7b2ceb557c290f2d9e0d7f207b3fea87b353ed
2024-11-02 07:23:55 +00:00
Artemis Tosini add8a4df9f package.nix: Fix cross devShell
The devShell relied on several packages directly from `pkgs`
or used with a non-splice-aware functions.
These would be built for the host system, making them useless
in a devShell for the build system.

Make sure that all packages are for the build system when needed.

Some other minor changes also required:
 * Make devShells use `clangStdenv` because GCC is currently broken
 * Disable rr when making a cross stdenv

Change-Id: Iee5f8a1a0c594139a50f2261b203491bd1644866
2024-11-02 06:32:20 +00:00
Artemis Tosini 9903bed3f4 flake.nix: Fix cross build
The lix package currently fails unless it's using a clang stdenv.
However, the flake's cross build outputs (e.g. `packages.x86_64-linux.nix-armv7l-linux`)
used the default stdenv, normally gcc.
Replace this with clang to fix package build.

Also take this opportunity to remove the no longer necessary `useLLVM = true`
override on FreeBSD. Since 24.05, nixpkgs always sets `useLLVM = true`
on FreeBSD in `lib.systems.elaborate`.

Change-Id: I939302e4f6385291fa9e582d38d908c42f6db89a
2024-11-02 00:57:09 +00:00
vigress8 486d1a1437 fix: make static build work again
I copied the workaround from here:
https://github.com/avdv/scalals/commit/a2de0eff59cb4c9be6f8a33b2957e31727f87b89
Properly fixing the issue upstream will be much more difficult.

Closes: https://git.lix.systems/lix-project/lix/issues/527
Change-Id: I967d53fa9e80510b620df485af448f76bd9aa52a
2024-11-01 16:55:47 +04:00
Raito BezariusandGerrit Code Review 6e2349d2e1 Revert "Reject weak hash algorithms as SRIs, and warn in any other cases"
This reverts commit 02c35ea9df.

Reason for revert: this code path is also used for `Input::getRev()`, i.e. flakes VCS revision validation, which, in the case of Git, are using SHA1.
As a result, this cause too much noise due to SHA1 revisions in Flakes.

Change-Id: I8064c1ebc26e4e83b627f0803a7a9ba56cfe1f37
2024-11-01 11:59:59 +00:00
Lily BallardandGerrit Code Review 834450e237 Merge changes Ibb849b68,I501397c8 into main
* changes:
  libstore: ignore broken symlinks in ssl-cert-file default
  change-authors: add lilyball
2024-11-01 03:51:21 +00:00
AriaandGerrit Code Review 8005d17365 Merge "Reject weak hash algorithms as SRIs, and warn in any other cases" into main 2024-10-31 22:13:27 +00:00
Lily Ballard 69957a971e libstore: replace random() calls with atomic counter
random() is not thread-safe, it relies on global state, and calling it
from worker threads can result in multiple threads producing the same
value. It also doesn't guarantee unique values even in single-threaded
use.

Use an atomic counter for the use-case of generating temporary paths,
and switch to a thread-local RNG for the one remaining call.

This will probably fix https://github.com/NixOS/nix/issues/7273 though
I'm not willing to risk corrupting my store to find out.

Change-Id: I4c4c4c9796613573ffefd29cc8efe3d07839facc
2024-10-30 19:54:43 -07:00
Lily Ballard 684f93e783 libstore: ignore broken symlinks in ssl-cert-file default
Also tweak `pathAccessible` to ignore other relevant errors too. It was
documented as ignoring permission errors but it was only ignoring
`EPERM`, which comes from the darwin sandbox, and not ignoring `EACCESS`
which is the real permission error. I figured it also makes sense to
ignore `ELOOP`.

Fixes: https://git.lix.systems/lix-project/lix/issues/560
Change-Id: Ibb849b68d07386eb80afb52b57f7d12b3a48a202
2024-10-30 19:50:38 -07:00
Lily Ballard 11950a0a79 change-authors: add lilyball
Change-Id: I501397c8e3a215a2ccb6074100e2508bae98d1a9
2024-10-30 19:50:38 -07:00
eldritch horrors beb193d1e2 libstore: remove our custom early timeout handling
curl handles timeouts internally when it acts as the main event loop. we
only need to wake up to add new transfers to the multi handle or restart
an existing transfer that has passed its restart wait time. periodically
waking up is not required for curl, and we don't need it any more either

Change-Id: Ic774e1d9519f807cda1a89694bc3ede75216f329
2024-10-30 22:52:19 +00:00
eldritch horrors 4ae6fb5a8f libstore: pause only stalling transfers
don't pause the entire curl thread. we have multiple consumer threads
after all, not just one, so stalling all of them is likely not great.

note that libcurl advises against using transfer pauses if compressed
encodings are allowed and automatically decoded. this should not lead
to problems in practice because our data is usually not compressed to
such a degree that curl buffering *uncompressed* data matters. should
this cause problems we can reintroduce the whole-thread pause, but we
will probably get away with this until the entire file transfer class
is made kj::Promise-using async (and *then* curl can be hardpaused if
it cannot get rid of its data, solving the problem once and for all).

Change-Id: I218e41bfa5a27c7454eafb0bdb54f2a29a7f6493
2024-10-30 22:52:19 +00:00
Linus HeckemannandGerrit Code Review c95b73d8a1 Merge "libstore: report all differing outputs rather than just the first" into main 2024-10-30 19:04:57 +00:00
Kiara Grouwstra a778b0f85a fix(libfetchers): set GitHub API version header, closes #255
Sets the `X-GitHub-Api-Version` header to `2022-11-28` for calls to the
GitHub API.
This follows the later version as per
https://docs.github.com/en/rest/about-the-rest-api/api-versions?apiVersion=2022-11-28.

This affected the check on whether to use the API versus unauthenticated
calls as well, given the headers would no longer be empty if the
authentication token were missing.
The workaround used here is to use a check similar to an existing
check for the token.

In the current implementation, headers are (still) similarly sent to
non-authenticated as well as GitHub on-prem calls.
For what it's worth, manual curl calls with such a header seemed to
break nor unauthenticated calls nor ones to the github.com API.

Change-Id: I6e10839e6b99cb65eb451e923b2a64f5d3c0f578
2024-10-30 18:40:13 +01:00
V.andGerrit Code Review 56ead73fda Merge "chore: remove monolithic coreutils requirement" into main 2024-10-30 16:53:43 +00:00
Dusk Banks 22eb47f0fd tests/functional/flakes: test with UTF-8 bullets
using UTF-8 bullets in the sample avoids locale confusion where Bash
doesn't know to treat `•` as a single character.

Signed-off-by: Dusk Banks <me@bb010g.com>
Change-Id: I829019b66e93e6d33ac3a6641df07d0dd2332a5a
2024-10-30 08:21:58 -07:00
Dusk BanksandGerrit Code Review 8b2f8d538b Merge "libstore: restore mode after changing xattrs" into main 2024-10-30 14:56:43 +00:00
vigress8 fb1b211037 chore: remove monolithic coreutils requirement
It's only used in a couple of tests, and only in such a way that
replacing it with a random command suffices.
I also removed a few pointless uses of the variable.

Fixes: https://git.lix.systems/lix-project/lix/issues/376
Change-Id: I90aedb61d64b02f7c9b007e72f9d614cc1b37a2e
2024-10-30 15:12:35 +04:00
Linus Heckemann 8b0ac51f12 libstore: report all differing outputs rather than just the first
Before:

error: derivation '/nix/store/4spy3nz1661zm15gkybsy1h5f36aliwx-python3.11-test-1.0.0.drv' may not be deterministic: output '/nix/store/ccqcp01zg18wp9iadzmzimqzdi3ll08d-python3.11-test-1.0.0-dist' differs from '/nix/store/ccqcp01zg18wp9iadzmzimqzdi3ll08d-python3.11-test-1.0.0-dist.check'

After:

error: derivation '4spy3nz1661zm15gkybsy1h5f36aliwx-python3.11-test-1.0.0.drv' may not be deterministic: outputs differ
         output differs: output '/nix/store/ccqcp01zg18wp9iadzmzimqzdi3ll08d-python3.11-test-1.0.0-dist' differs from '/nix/store/ccqcp01zg18wp9iadzmzimqzdi3ll08d-python3.11-test-1.0.0-dist.check'
         output differs: output '/nix/store/yl59v08356i841c560alb0zmk7q16klb-python3.11-test-1.0.0' differs from '/nix/store/yl59v08356i841c560alb0zmk7q16klb-python3.11-test-1.0.0.check'

Change-Id: Ib2871fa602bf1fa9c00e2565b3a2e1b26f908152
2024-10-29 18:34:54 +01:00
eldritch horrors 9c22a4d31b libstore: don't use curl for file:// downloads
curl can't pause downloads of file:// urls, which is very much in the
way of making the curl wrapper fully asynchronous. we can emulate all
the things curl does, but unfortunately curl is *rather extensive* in
its support of frankly weird shit. hopefully this subset of features,
which notably does not include curl readdir support, is enough for us

Change-Id: I5f67768c4b512565655b94b0421270c7dbbd8d11
2024-10-28 18:52:49 +00:00
eldritch horrors c83b13eafd libstore: reunify all file transfer methods again
with the api cleaned up we can suddenly reunify uploads, downloads, and
existence checks through curl in the same wrapper function. uploads and
existence checks simply don't use the result source, and given that all
transfers (or at least *most* transfers to date) go through the network
the few extra allocations do not hurt us at all. even for file:// calls
the overhead won't much matter as going to disk and back *is* expensive

Change-Id: I4f9ca6681a8fc303377b4cf4c63e3363ae32c18b
2024-10-28 18:52:49 +00:00
eldritch horrors d65838a900 libstore: remove FileTransfer::enqueueDownload
it's no longer needed. `download` can do everything `enqueueDownload`
did, and a lot more. e.g. not block the calling thread, for instance.

Change-Id: I4b36235ed707c92d117b4c33efa3db50d26f9a84
2024-10-28 18:52:49 +00:00
eldritch horrors c68f0cdf00 libstore: return transfer metadata from download
as promised earlier. nothing uses it yet, but just you wait.

Change-Id: I77d185578d96c2134b756d20f2fcf1c02de0da6f
2024-10-28 18:52:49 +00:00
eldritch horrors 14eff10fe4 libstore: split callback into metadata and finished parts
this will let us return metadata from FileTransfer::download, which in
turn is necessary to remove enqueueDownload. it also opens avenues for
streaming downloads that keep download metadata instead of dropping it

Change-Id: If0fc6af5eb2aeb689fc866c345c9d7bce4d59f2d
2024-10-28 18:52:49 +00:00
eldritch horrors 923abe347c libstore: use data callback for simple downloads too
this is an intermediate step towards removing enqueueDownload entirely.

Change-Id: I05ec0c7f4a234fdc966e5005308b37f6f905d433
2024-10-28 18:52:49 +00:00
eldritch horrors 64864c3730 libstore: pass only data to TransferItem data callback
with encoding being handled by curl the reference is no longer needed.

Change-Id: Ibfaf5f55e5314e81ce45ba4523b960c401dd2e1c
2024-10-28 18:52:49 +00:00
eldritch horrors 10488f7431 libstore: use curl content-encoding support, not our own
let's use the automatic decoding functions curl provides instead of
implementing them ourselves for the dubious ability to support both
xz and bzip2 encodings as well, neither of which anything will send

Change-Id: I3edfebeb596a0e9d5c986efca9270501c996f2dd
2024-10-28 18:52:49 +00:00
Justin !andGerrit Code Review 8c567c0424 Merge "libutil: implement PathsSetting<PathSet>" into main 2024-10-28 14:22:59 +00:00
Lily Ballard a042bbde56 libstore: remove unnecessary lstats in optimisePath
We were calling `lstat()` twice on the link path, and a third time if it
existed. We only need to call it once.

Change-Id: Ifadc2f24681648d0ec7e4bad5c6d2dcb2e560e7b
2024-10-27 20:03:29 -07:00
eldritch horrorsandGerrit Code Review 61146c73ce Merge changes I0220cedd,Ide0c0512,I6fcd920e,I85ec62ff,I35853a91, ... into main
* changes:
  libstore: check that transfer headers don't change during retries
  libstore: use effective transfer url for retries
  libstore: collect effective url and cachedness earlier
  libstore: remove TransferItem::active
  libstore: always allocate TransferItem::req
  libstore: remove FileTransferResult::data
  libstore: de-future-ize FileTransfer::enqueueUpload
  libstore: remove FileTransferRequest
  libstore: remove FileTransferRequest::expectedETag
  libstore: remove FileTransferResult::bodySize
  libstore: remove FileTransferRequest::verifyTLS
  libstore: remove FiletransferRequest::head
2024-10-28 01:36:45 +00:00
Linus HeckemannandGerrit Code Review f55ed83991 Merge "libutil: handle json builder log messages with unexpected format" into main 2024-10-27 22:54:11 +00:00
eldritch horrors 212a14bb1f libstore: check that transfer headers don't change during retries
etag changing implies with high probability that the content of the
resource changed. immutable url changing implies that the immutable
url we got previously was wrong, which is probably a server bug. if
the encoding changes our decoding will break completely, so that is
also very illegal. one notable change we still allow is etags going
away completely, mostly since this does not imply any data changes.

Change-Id: I0220ceddc3fd732cd1b3bb39b40021cc631baadc
2024-10-27 21:44:38 +00:00
eldritch horrors 7c716b9716 libstore: use effective transfer url for retries
do not retread the entire redirection path if we've seen the end of the
road. this avoids silently downloading wrong data, and notifies us when
a url we've received data from turns into a redirect when retrying. for
reasons of simplicity we don't turn of libcurl redirects on retries. if
we did that we'd have to conditionally process http status codes, which
sounds annoying and would make the header callback even more of a mess.

Change-Id: Ide0c0512ef9b2579350101246d654a2375541a39
2024-10-27 21:44:38 +00:00
eldritch horrors 2b3bdda027 libstore: collect effective url and cachedness earlier
this will let us return metadata for a transfer without having to wait
for the entire transfer to complete. more importantly for current uses
though is that we could now send retries to the effective url directly
instead of retreading the entire redirect path. this improves latency,
and in such cases where redirects change while we're downloading it'll
also improve correctness (previously we'd silently download bad data).

Change-Id: I6fcd920eb96fbdb2e960b73773c0b854e0300e99
2024-10-27 21:44:38 +00:00
eldritch horrors 97c76c4655 libstore: remove TransferItem::active
it's always legal to call curl_multi_remove_handle on a valid pair of
multi and easy handles. removing an easy handle that is not currently
attached to a multi handle is a no-op, and removing an easy handle of
a different multi handle is something we can't reasonably trigger. if
we *did* ever manage it would result in an error we'd ignore, and the
handles in question would not be changed at all. this is just simpler

Change-Id: I85ec62ff89385981ca49d243376b9c32586bd128
2024-10-27 21:44:38 +00:00
eldritch horrors c82407fc1e libstore: always allocate TransferItem::req
there's no reason not to do this. also improve error handling a bit.

Change-Id: I35853a919fa58a9a34ad47ffab6de77ba6f7fb86
2024-10-27 21:44:38 +00:00
eldritch horrors 982d049d3b libstore: remove FileTransferResult::data
return it as a separate item in a pair instead. this will let us remove
enqueueDownload() in favor of returning metadata from download() itself

Change-Id: I74fad2ca15f920da1eefabc950c2baa2c360f2ba
2024-10-27 21:44:38 +00:00
eldritch horrors 5cd7055044 libstore: de-future-ize FileTransfer::enqueueUpload
it's only used once, and synchronously at that.

Change-Id: Ife9db15dd97bc0de8de59a25d27f3f7afeb8791b
2024-10-27 21:44:38 +00:00
eldritch horrors 6f18e1ebde libstore: remove FileTransferRequest
it's just a uri and some headers now. those can be function arguments
with no loss of clarity. *actual* additional arguments, for example a
TLS context with additional certificates, could be added on a new and
improved FileTransfer class that carries not just a backend reference
but some real, visible context for its transfers. curl not being very
multi-threading-friendly when using multi handles will make sharing a
bit hard anyway once we drop the single global download worker thread

Change-Id: Id2112c95cbd118c6d920488f38d272d7da926460
2024-10-27 21:44:38 +00:00
eldritch horrors a839c31e6c libstore: remove FileTransferRequest::expectedETag
just another http specific used in only one place.

Change-Id: I99361a7226f4e6cd8f18170d3683c0025657bcb3
2024-10-27 21:44:38 +00:00
eldritch horrors 30bec83fa4 libstore: remove FileTransferResult::bodySize
it's only used for internal bookkeeping in TransferItem.

Change-Id: I467c5be023488be4a8a76e5f98a4ef25762df6f3
2024-10-27 21:44:38 +00:00
eldritch horrors d82b212d33 libstore: remove FileTransferRequest::verifyTLS
it's never set to false.

Change-Id: I1e436c82f1097091a08faa1dfada75e51bd5edf9
2024-10-27 21:44:38 +00:00
eldritch horrors 220251ba51 libstore: remove FiletransferRequest::head
add a method to FileTransfer that provides this functionality instead.

Change-Id: Ic1933a5df76a109c248c9c5efea065356b20a6f9
2024-10-27 21:44:38 +00:00
Raito BezariusandGerrit Code Review 9f682204b5 Merge changes I85b6075a,Iee41b055 into main
* changes:
  libstore: ban unpacking case hacked filenames from NARs
  testsuite: add a NAR generator with some evil NARs
2024-10-27 18:12:18 +00:00
Raito BezariusandGerrit Code Review f9e7df01f3 Merge "daemon: stop eating SIGINTs" into main 2024-10-27 18:11:20 +00:00
Raito BezariusandGerrit Code Review f7edee7c14 Merge changes I8e11ddbe,Idb8d9a00 into main
* changes:
  nix-shell: stop using dynamic format strings!!
  tests: move nix-shell related tests to subdir
2024-10-27 18:10:17 +00:00
eldritch horrors 6c2609c5f9 libstore: remove FileTransferRequest::tries
it's never set, and then only used internally. *once*.

Change-Id: I32585b1821e979f3ebb53b794ba0d1f576126b92
2024-10-26 21:42:35 +00:00
eldritch horrors af27d1ecd8 libstore: make baseRetryTimeMs a FileTransfer property
we don't even need this outside of tests. maybe we should not do
automatic retries at this level at all and use retrying wrappers
instead? at some point we may have to do this, but not just yet.

Change-Id: If0088aa55215be81f1770c25b3bb1b5268c65cf8
2024-10-26 21:42:35 +00:00
eldritch horrors 1e3b45546c libstore: remove FileTransferRequest::parentAct
never set explicitly, and transfers are never instantiated with one
current activity but submitted with a *different* current activity.

Change-Id: I1a3ec57c02013565aeb9e9398ea42d0c4279095e
2024-10-26 21:42:35 +00:00
eldritch horrors ce3e1d1e7a libstore: remove FileTransferRequests::data
use separate upload and download methods instead.

Change-Id: I5baa2177c8ddd70268c75ff074e361b2f17dddbd
2024-10-26 21:42:35 +00:00
eldritch horrors 2d49efaa2e libstore: remove Filetransfer::transfer
just use enqueueFileTransfer().get() insteaad.

Change-Id: I67a43c9d3d5f68ac3f9e8ba7973c243dd78b86a3
2024-10-26 21:42:35 +00:00
eldritch horrors 98b55c3a1d libstore: move FileTransferRequest::verb to TransferItem
this function is only used internally by curl wrapper.

Change-Id: I71d4c430cb069e2c949be769c17fede8dd04d480
2024-10-26 21:42:35 +00:00
eldritch horrors a83bf24281 libstore: remove FileTransferRequest::mimeType
it's only used by HttpBinaryCacheStore, and even there used in only on
place. this one place can set the header explicitly, which it now does

Change-Id: Id89228150669e25e7f59a3d6bd939e46059ce29e
2024-10-26 21:42:35 +00:00
tcmal 02c35ea9df Reject weak hash algorithms as SRIs, and warn in any other cases
Fixes #114

Change-Id: Ib9e68edfed5c186a029531e1eb9bda9d2e338e54
2024-10-26 22:29:54 +01:00
eldritch horrors a8d6577bf0 libstore: HttpBinaryCacheStore::{makeRequest -> makeURI}
it only sets the one field anyway (and the parent activity as a side
effect that does not depend on the exact location of the constructor
call). when FileTransferRequest goes away we would need this anyway.

Change-Id: I35cf2ed3533239181449a62cf34cd282b395e5db
2024-10-26 20:35:16 +00:00
eldritch horrors 59e364c2a8 tests: stop using OCR for nix copy tests
this is fragile, slow as fuck, breaks constantly under high concurrency,
and completely unnecessary since ssh bypasses the stdio file descriptors
*anyway*. we do still check that we see ssh messages to ensure that none
of our subprocess handling messes with ssh's /dev/tty, but that's it now

Change-Id: Ib8e31e1999f813d07a27efc63a9d3454a9e4fcdd
2024-10-26 17:50:54 +00:00
YurekaandGerrit Code Review b020d1fc27 Merge "fix build for 32-bit platforms" into main 2024-10-26 15:46:05 +00:00
alois31andGerrit Code Review 2734a9cf94 Merge changes I29e66ad8,I77ea62cd,I7cd58d92 into main
* changes:
  treewide: make more settings conditionally available
  libstore/build: only send overridden settings to the build hook
  treewide: consistently mark overridden settings as such
2024-10-23 15:20:51 +00:00
eldritch horrors 5f1344dd8a libstore: turn Worker::run into a promise
a first little step into pushing the event loops up, up and away.
eventually we will want them to be instantiated only at the roots
of every thread (since kj binds loops to threads), but not today.

Change-Id: Ic97f1debba382a5a3f46daeaf2d6d434ee42569f
2024-10-23 11:55:12 +00:00
eldritch horrors faee771b30 libstore: hide Worker and goals where possible
goals should be considered internal to the worker architecture due to
the tight coupling of the two, and we can finally do that. doing this
is also a prerequisite for turning Worker::run() into a real promise.

Change-Id: I7cf273d4a6fdb75b8d192fce1af07c6265ff6980
2024-10-23 11:55:12 +00:00
eldritch horrors b8cc54df0a libstore: return relevant store path in goal result
we now do not need the goal pointers any more to process worker results.

Change-Id: I1a021b862ca666bcd23fee9f38973e90e6f94a72
2024-10-23 11:55:12 +00:00
eldritch horrors 67f1aafd61 libstore: restrict curl protocols
previously it was possible to fetchurl a dict server, or an ldap server,
or an imap server. this is a bit of a problem, both because rare schemes
may not be available on all systems, and because some schemes (e.g. scp)
are inherently insecure in potentially surprising ways we needn't allow.

Change-Id: I18fc567c6f58c3221b5ea8ce927f4da780057828
2024-10-23 11:32:14 +00:00
eldritch horrors 1d9d40b2a6 libstore: move failingExitStatus into worker results
we already have a results type for the entire worker call, we may as
well put this bit of info in there. we do keep the assumption of the
old code that the field will only be read if some goals have failed;
fixing that is a very different mess, and not immediately necessary.

Change-Id: If3fc32649dcd88e1987cdd1758c6c5743e3b35ac
2024-10-22 15:32:36 +00:00
eldritch horrors 343aca3a27 libstore: clear derivation build activities when done
without this derivations do not show as completely processed in the
internal-json logs (or the newer multiline output). the former also
breaks external tools like nix-output-monitor which, like multiline
output, grow vertically until at least some goals are finally freed

Change-Id: I55758daf526ba29ae15fb82e0d88da8afb45bf5c
2024-10-22 14:55:55 +00:00
Lily BallardandGerrit Code Review 5ce1d8463a Merge "libstore: add missing #include on darwin" into main 2024-10-22 02:05:44 +00:00
Yureka b77687945e fix build for 32-bit platforms
Change-Id: I113e131eb5c66c42c0bbc60181a7faafc02ca02e
2024-10-21 08:52:29 +02:00
Lily Ballard 4308ec1ae4 gitignore: ignore *.pyc files
Running our installcheck test suite creates these files.

Change-Id: I97ac8f1aa165a491c55dff6b48486db17b75443b
2024-10-20 22:08:35 -07:00
Lily Ballard 65551175e3 libstore: add missing #include on darwin
optimise-store.cc used std::regex on darwin, but forgot to include the
header. This probably compiled due to the precompiled headers file, but
it caused errors in the editor.

Change-Id: I23297c08cb66d44e4d4f303560f46e4adc7d5a43
2024-10-20 22:08:35 -07:00
Lily Ballard 068f4b147d libstore: fix sign comparison warnings in darwin platform
It's not clear to me if `proc_pidinfo()` or `proc_pidfdinfo()` can
actually return negative values, the syscall wrappers convert `-1` into
zero and the semantics suggest that negative values don't make sense,
but just to be safe we'll preserve the int type until we've checked that
it's a positive value.

Fixes: https://git.lix.systems/lix-project/lix/issues/548
Change-Id: If575aec6b1e27dba63091c7a0316c7b3788747cd
2024-10-20 13:13:11 -07:00
eldritch horrors 0ff8f91325 libutil: disallow AsyncCollect relocations
some promises capture `this`.  we could also allocate a shared state,
but this thing doesn't really need to ever be moved anyway. so there.

Change-Id: I50b5c44684a8ab4e984b1323de21f97ace4a864a
2024-10-19 19:47:46 +00:00
eldritch horrorsandGerrit Code Review b0e619b8bd Merge "libstore: always release build/substitution slot tokens" into main 2024-10-19 18:32:30 +00:00
eldritch horrors 564d931134 libstore: always release build/substitution slot tokens
not doing this can freeze slots until the goal that occupied them is
freed (rather than simply complete), and then can freeze the system.

fixes #549

Change-Id: I042df04222f8ffbaa18ef4a4eae6cbd6f89b679e
2024-10-19 16:17:58 +02:00
Jade Lovelace dccde94369 daemon: stop eating SIGINTs
Daemon client handler processes are forked off of the main nix process
and thus will not have a signal handler thread anymore. This leads to a
high likelihood of bustage, since the Worker infrastructure expects the
interrupt infrastructure to actually, you know, work, to be able to get
interrupted.

The expected behaviour after fork is either:
- Start a signal handler thread if you expect to do complicated things
  that need ReceiveInterrupts.
- Call restoreProcessContext and don't handle signals specially
  otherwise.

Change-Id: I73d36b5bbf96dddd21d5e1c3bd0484d715c00e8b
2024-10-18 21:34:18 -07:00
Dusk Banks 60b89c63db libstore: restore mode after changing xattrs
this was missed in 3f07c65510. if mode is
not restored, the tree will have mutability where it shouldn't.

fixes test `functional-repair`, which fails from the output path
directory being unnecessarily writable:

```
++(repair.sh:12) nix-hash /tmp/nix-shell.IIUJAq/nix-test/repair/store/j3kmp9zhc5y7gqs591l0nrscm5hw4145-dependencies-input-2
+(repair.sh:12) hash=d790f49fc89cb6f384b6dbe450790d07
+(repair.sh:15) chmod u+w /tmp/nix-shell.IIUJAq/nix-test/repair/store/j3kmp9zhc5y7gqs591l0nrscm5hw4145-dependencies-input-2
+(repair.sh:16) touch /tmp/nix-shell.IIUJAq/nix-test/repair/store/j3kmp9zhc5y7gqs591l0nrscm5hw4145-dependencies-input-2/bad
+(repair.sh:18) nix-store --verify --check-contents -v
reading the Nix store...
checking path existence...
checking link hashes...
checking store hashes...
checking contents of '/tmp/nix-shell.IIUJAq/nix-test/repair/store/20z19rjkwmwpb2ba4x29kac6xnslai38-dependencies-top.drv'
checking contents of '/tmp/nix-shell.IIUJAq/nix-test/repair/store/3mmawi9lj33xz96cf6kw9989xc8v5i96-fod-input'
checking contents of '/tmp/nix-shell.IIUJAq/nix-test/repair/store/5fp4r2kh1fcv6mv9dv6rywhhr1am9hhm-builder-dependencies-input-0.sh'
checking contents of '/tmp/nix-shell.IIUJAq/nix-test/repair/store/9kryn4ihv6b7bjswv2rsjq4533n2w5zk-fod-input.drv'
checking contents of '/tmp/nix-shell.IIUJAq/nix-test/repair/store/ap8s0fim8s3ilzj8aqwlwk7gv1crjp4j-dependencies-top'
checking contents of '/tmp/nix-shell.IIUJAq/nix-test/repair/store/i2ipd9p282zkqr1zb4glqiqigv8ybsyk-dependencies.builder0.sh'
checking contents of '/tmp/nix-shell.IIUJAq/nix-test/repair/store/i3xyw46h0lsx7ad6bczwi9sqjjx5f0j0-dependencies-input-0'
checking contents of '/tmp/nix-shell.IIUJAq/nix-test/repair/store/j3kmp9zhc5y7gqs591l0nrscm5hw4145-dependencies-input-2'
path '/tmp/nix-shell.IIUJAq/nix-test/repair/store/j3kmp9zhc5y7gqs591l0nrscm5hw4145-dependencies-input-2' was modified! expected hash 'sha256:1rhaylnjs5lbp089lnk7qvsypqmbm5vvyvxvv7i68b4x33pncqgs', got 'sha256:0nkjrmdc6ixf935chj3zhpqph5i15p306ffdsa850qh8mncpnsmc'
checking contents of '/tmp/nix-shell.IIUJAq/nix-test/repair/store/pfaxqiw8zf3bw0w8w8gazswh76d729yy-builder-dependencies-input-2.sh'
checking contents of '/tmp/nix-shell.IIUJAq/nix-test/repair/store/pzzms3k5wl8d3wszv3maw29zylfkiiw0-dependencies-input-1'
checking contents of '/tmp/nix-shell.IIUJAq/nix-test/repair/store/riwx84p57ckfvgli9nwhx88z6zh1c8ss-builder-fod-input.sh'
checking contents of '/tmp/nix-shell.IIUJAq/nix-test/repair/store/rjsphx7xvnqh2qafdrr7fiyxqc1rljhw-dependencies-input-2.drv'
checking contents of '/tmp/nix-shell.IIUJAq/nix-test/repair/store/scywzq87dvm0c5f1h16hww6mvzhcsx3f-builder-dependencies-input-1.sh'
checking contents of '/tmp/nix-shell.IIUJAq/nix-test/repair/store/v65j4w033i05nyfd2h0g8h41ijmfglp7-dependencies-input-0.drv'
checking contents of '/tmp/nix-shell.IIUJAq/nix-test/repair/store/ykwibh62xyp81k75vdw9c7y21kg4ibzf-dependencies-input-1.drv'
warning: not all store errors were fixed
+(repair.sh:21) nix-store --verify --check-contents --repair
reading the Nix store...
checking path existence...
checking link hashes...
checking store hashes...
path '/tmp/nix-shell.IIUJAq/nix-test/repair/store/j3kmp9zhc5y7gqs591l0nrscm5hw4145-dependencies-input-2' was modified! expected hash 'sha256:1rhaylnjs5lbp089lnk7qvsypqmbm5vvyvxvv7i68b4x33pncqgs', got 'sha256:0nkjrmdc6ixf935chj3zhpqph5i15p306ffdsa850qh8mncpnsmc'
checking path '/tmp/nix-shell.IIUJAq/nix-test/repair/store/j3kmp9zhc5y7gqs591l0nrscm5hw4145-dependencies-input-2'...
path '/tmp/nix-shell.IIUJAq/nix-test/repair/store/j3kmp9zhc5y7gqs591l0nrscm5hw4145-dependencies-input-2' is corrupted or missing!
checking path '/tmp/nix-shell.IIUJAq/nix-test/repair/store/i3xyw46h0lsx7ad6bczwi9sqjjx5f0j0-dependencies-input-0'...
repairing outputs of '/tmp/nix-shell.IIUJAq/nix-test/repair/store/rjsphx7xvnqh2qafdrr7fiyxqc1rljhw-dependencies-input-2.drv'...
+(repair.sh:23) '[' -e /tmp/nix-shell.IIUJAq/nix-test/repair/store/j3kmp9zhc5y7gqs591l0nrscm5hw4145-dependencies-input-2/bad ']'
+(repair.sh:24) '[' -w /tmp/nix-shell.IIUJAq/nix-test/repair/store/j3kmp9zhc5y7gqs591l0nrscm5hw4145-dependencies-input-2 ']'
++(repair.sh:24) onError
++(/var/home/bb010g/Sources/Nix/lix/build/tests/functional/common/vars-and-functions.sh:244) set +x
repair.sh: test failed at:
  main in repair.sh:24
```

fixes test `functional-simple`, which fails from the output path file
being unnecessarily writable:

```
+(simple.sh:11) echo 'output path is /tmp/nix-shell.IIUJAq/nix-test/simple/store/9fqn0rs99ymn1r09yip7bsifcdh3ra0y-simple'
+(simple.sh:13) '[' -w /tmp/nix-shell.IIUJAq/nix-test/simple/store/9fqn0rs99ymn1r09yip7bsifcdh3ra0y-simple ']'
++(simple.sh:13) onError
++(/var/home/bb010g/Sources/Nix/lix/build/tests/functional/common/vars-and-functions.sh:244) set +x
simple.sh: test failed at:
  main in simple.sh:13
```

fixes test `functional-optimise-store`, which fails from the unnecessary
mutability making Lix avoid hard linking:

```
++(optimise-store.sh:5) nix-build - --no-out-link --auto-optimise-store
this derivation will be built:
  /tmp/nix-shell.IIUJAq/nix-test/optimise-store/store/2ql4kjxhnzdard8d6n3h9hc1m3lawr2b-foo1.drv
building '/tmp/nix-shell.IIUJAq/nix-test/optimise-store/store/2ql4kjxhnzdard8d6n3h9hc1m3lawr2b-foo1.drv'...
warning: skipping suspicious writable file '/tmp/nix-shell.IIUJAq/nix-test/optimise-store/store/h7i1pp848f9a8452y0s18kgsnis77vjn-foo1/foo'
+(optimise-store.sh:5) outPath1=/tmp/nix-shell.IIUJAq/nix-test/optimise-store/store/h7i1pp848f9a8452y0s18kgsnis77vjn-foo1
++(optimise-store.sh:6) echo 'with import ./config.nix; mkDerivation { name = "foo2"; builder = builtins.toFile "builder" "mkdir $out; echo hello > $out/foo"; }'
++(optimise-store.sh:6) nix-build - --no-out-link --auto-optimise-store
this derivation will be built:
  /tmp/nix-shell.IIUJAq/nix-test/optimise-store/store/wjgvfhfsp14w06im8bbp1kqzz7smdkcy-foo2.drv
building '/tmp/nix-shell.IIUJAq/nix-test/optimise-store/store/wjgvfhfsp14w06im8bbp1kqzz7smdkcy-foo2.drv'...
warning: skipping suspicious writable file '/tmp/nix-shell.IIUJAq/nix-test/optimise-store/store/gjg6gayj2f6x3h53sp5i21nbnsd7b4i3-foo2/foo'
+(optimise-store.sh:6) outPath2=/tmp/nix-shell.IIUJAq/nix-test/optimise-store/store/gjg6gayj2f6x3h53sp5i21nbnsd7b4i3-foo2
++(optimise-store.sh:8) stat --format=%i /tmp/nix-shell.IIUJAq/nix-test/optimise-store/store/h7i1pp848f9a8452y0s18kgsnis77vjn-foo1/foo
+(optimise-store.sh:8) inode1=328316
++(optimise-store.sh:9) stat --format=%i /tmp/nix-shell.IIUJAq/nix-test/optimise-store/store/gjg6gayj2f6x3h53sp5i21nbnsd7b4i3-foo2/foo
+(optimise-store.sh:9) inode2=328404
+(optimise-store.sh:10) '[' 328316 '!=' 328404 ']'
+(optimise-store.sh:11) echo 'inodes do not match'
+(optimise-store.sh:12) exit 1
```

Signed-off-by: Dusk Banks <me@bb010g.com>
Change-Id: I87eeb74e718746a587be2ac52bcc9b5b1e5529db
2024-10-18 20:57:22 -07:00
jadeandGerrit Code Review 60578b4d7d Merge "flake.lock: update everything" into main 2024-10-19 00:03:42 +00:00
jadeandGerrit Code Review eadce58a90 Merge "testsuite: Fix tests on systems with a non-master defaultBranch" into main 2024-10-19 00:00:30 +00:00
Jade Lovelace ca55060ac6 packaging: use in-tree capnproto derivation
This is done because the one in nixpkgs has several problems and we
don't want to conflict with them fixing those problems:
- not building shared libs
- not building debuginfo (more critical for us due to
  https://git.lix.systems/lix-project/lix/issues/549)
- not setting the correct cmake build type
- not setting the correct cxxflags to build the coroutine library
- not building with clang (required for the coroutine library since gcc
  is known to miscompile coroutines *in kj*).

CC: https://git.lix.systems/lix-project/lix/issues/551

Fixes: https://git.lix.systems/lix-project/lix/issues/550
Change-Id: Ia5b78dc1809963fdd1a8203b127a216cb575d751
2024-10-18 15:40:22 -07:00
Jade Lovelace 77b6f6734f packaging: remove workaround for clang stdenv asserts
This workaround was for the stdenv not being set when callPackage'ing
package.nix for some of the stranger CI outputs.

Change-Id: I2acdd6efa721b90dd3cb04358544d25d591ff084
2024-10-18 15:40:22 -07:00
piegames e2d00ac3a8 libexpr: Fix typo in error message
Closes #523

Change-Id: Ib5705e405b74d07a8fcf0163847405e9c791c3e3
2024-10-18 19:37:23 +02:00
piegames 3ba5ef91bc benchmarks: Fix purity
Some stuff wasn't working on my machine because it depended on the host
environment in some ways, fixed those.

Change-Id: Iff4931a9a26c6827978f1ee6434710f406d67a96
2024-10-18 19:37:23 +02:00
piegames e5de1d13c4 libexpr: Optimize complex indented strings
The old behavior results in lots of concatenations happening for no good
reason and is an artifact of the technical limitations of the old parser
(combined with some lack of care for such details).

Change-Id: I0d78d6220ca6aeaa10bc437e48e08bf7922e0bb3
2024-10-18 19:37:23 +02:00
piegames 878e181882 libexpr: Print interpolations more accurately in show
This is only a minor semantical distinction, but we should be able to
properly test it, and the parser tests rely on show for that.

Change-Id: I25e868cf9544e30cdff17deb5fd50a434e0f367e
2024-10-18 11:40:04 +00:00
piegames c852ae60da libexpr: Rewrite stripIndentation for indented strings
This commit should faithfully reproduce the old behavior down to the
bugs. The new code is a lot more readable, all quirks are well
documented, and it is overall much more maintainable.

Change-Id: I629585918e4f2b7d296b6b8330235cdc90b7bade
2024-10-18 11:40:04 +00:00
piegames 765771a355 tests/functional-lang: Add test cases for indented strings
Accidentally came across them elsewhere, wanted them covered in the
ind-strings test as well.

Change-Id: Iba418a687388ba85516d13d4c9d815744297dc5c
2024-10-18 11:40:04 +00:00
piegames 14291856e4 tests/functional-lang: Test indented string parsing
There is already an eval test for it, but it doesn't test the parser's
output

Change-Id: I94809080f90ace8aa3f610bcd315e9df0cf5d12f
2024-10-18 11:40:04 +00:00
piegames e7d6212f77 libexpr: move parser semantics into separate file
Another preparation for forking off and versioning the parser

Change-Id: I7b1225a44a3b81486414c1d37bd3e76a3ab307f9
2024-10-18 11:40:04 +00:00
piegames f98ee07573 libexpr: rename grammar to grammar::v1
Let's make some space in the namespace for a v2

https://wiki.lix.systems/books/lix-contributors/page/nix-lang-v2
Change-Id: If56e6dbf680d931233aa822ef91c8832464471e4
2024-10-18 11:40:04 +00:00
piegames 580df9bdb3 tests/functional-lang: Small cleanup of flagfiles handling
Flag files now properly work for all tests.

Change-Id: If652235960e8cf4120270a1b974c17586a31a157
2024-10-18 11:40:04 +00:00
jadeandGerrit Code Review 52b64662a4 Merge "fix: macOS build broken by fatal lowdown CLI sandbox setup" into main 2024-10-17 21:17:49 +00:00
Alois Wohlschlager 689eb45630 treewide: make more settings conditionally available
Some settings only make sense on particular platforms, or only when a certain
experimental feature is enabled. Several of those were already conditionally
available. Do the same for a bunch more instead of silently ignoring them.
Exceptionally, the use-case-hack setting is not made conditional because it is
included in the test suite.

Change-Id: I29e66ad8ee6178a7c0eff9efb55c3410fae32514
2024-10-15 19:55:50 +02:00
Alois Wohlschlager ece99fee23 libstore/build: only send overridden settings to the build hook
The build hook is still running locally, so it will run with the same default
settings. Hence, just as with the daemon, it is enough to send it only the
overridden settings. This will prevent warnings like

   warning: Ignoring setting 'auto-allocate-uids' because experimental feature 'auto-allocate-uids' is not enabled

when the user didn't actually set those settings.

This is inspired by and an alternative to [0].
[0] https://github.com/NixOS/nix/pull/10049

Change-Id: I77ea62cd017614b16b55979dd30e75f09f860d21
2024-10-15 19:55:50 +02:00
Alois Wohlschlager 4dbbd721eb treewide: consistently mark overridden settings as such
Only overridden settings are sent to the daemon, and we're going to do the same
for the build hook to. It needs to be ensured that overridden settings are in
fact consistently marked as such, so that they actually get sent.

Change-Id: I7cd58d925702f86cf2c35ad121eb191ceb62a355
2024-10-15 19:55:50 +02:00
Linus Heckemann e55cd3beea libutil: handle json builder log messages with unexpected format
Before this change, expressions like:

with import <nixpkgs> {};
runCommand "foo" {} ''
  echo '@nix {}' >&$NIX_LOG_FD
''

would result in Lix crashing, because accessing nonexistent fields of
a JSON object throws an exception.

Rather than handling each field individually, we just catch JSON
exceptions wholesale. Since these log messages are an unusual
circumstance, log a warning when this happens.

Fixes #544.

Change-Id: Idc2d8acf6e37046b3ec212f42e29269163dca893
2024-10-15 11:15:42 +02:00
Justin ! 5a06b17b91 libutil: implement PathsSetting<PathSet>
By implementing the `PathSet` specialization for `PathsSetting`, we'll
be able to use `PathsSetting` for the `sandboxPaths` setting in
`src/libstore/globals.hh`.

Fixes: https://git.lix.systems/lix-project/lix/issues/498

Change-Id: I8bf7dfff98609d1774fdb36d63e57d787bcc829f
2024-10-14 20:31:04 -04:00
jadeandGerrit Code Review f6077314fa Merge "Fix std::terminate call in thread pool" into main 2024-10-15 00:11:59 +00:00
Jade Lovelace a020f5f6cb fix: macOS build broken by fatal lowdown CLI sandbox setup
This failed due to https://github.com/NixOS/nixpkgs/pull/346945, which
makes a second lowdown-unsandboxed that works in nix builds, and the
regular lowdown has executables that fail closed when the sandbox setup
fails.

The actual failure here is only visible on nixos-unstable at the moment,
not 24.05, but this commit should fix it up for all versions.

Fixes: https://git.lix.systems/lix-project/lix/issues/547
Change-Id: I50c0ecb59518ef01a7c0181114c1b4c5a7c6b78b
2024-10-14 16:49:16 -07:00
V.andGerrit Code Review fbf7a8b440 Merge "Remove ancient let from 2 test files" into main 2024-10-14 22:06:48 +00:00
vigress8 31ff77b3f9 Remove ancient let from 2 test files
Change-Id: I992bc7f9e1cfcb1e4038fbe6ee04178bbf938556
2024-10-14 17:19:22 +04:00
Jade Lovelace c1f4c60bc2 nix-shell: stop using dynamic format strings!!
This was always a terrible idea independently of whether it crashes.
Stop doing it!

This commit was verified by running nix-shell on a trivial derivation
with --debug --verbose to get the vomit-level output of the shell rc
file and then diffing it before/after this change. I have reasonable
confidence it did not regress anything, though this code is genuinely
really hard to follow (which is a second reason that I split it into two
fmt calls).

Fixes: https://git.lix.systems/lix-project/lix/issues/533
Change-Id: I8e11ddbece2b12749fda13efe0b587a71b00bfe5
2024-10-13 23:12:45 -07:00
Jade Lovelace 8497f0fe19 tests: move nix-shell related tests to subdir
This change feels kind of gross and reveals a fair bit about the
disorganization of our tests, but I think it makes parts of it a bit
better.

Change-Id: Idb8d9a00cbd75d5c156678c6b408b42b59d5e4d7
2024-10-13 23:12:45 -07:00
Maximilian Bosch 4682e40183 ssh-ng: better way to keep SSH errors visible
A better fix than in 104448e75d, hence a
revert + the fix.

It turns out that this commit has the side-effect that when having e.g.
`StrictHostKeyChecking=accept-new` for a remote builder, the warnings à la

    Warning: Permanently added 'builder' (ED25519) to the list of known hosts.

actually end up in the derivation's log whereas hostkey verification
errors don't, but only in the stderr of the `nix-build` invocation
(which was the motivation for the patch).

This change writes the stderr from the build-hook to

* the daemon's stderr, so that the SSH errors appear in the journal
  (which was the case before 104448e75d)
* the client's stderr, as a log message
* NOT to the drv log (this is handled via `handleJSONLogMessage`)

I tried to fix the issue for legacy-ssh as well, but failed and
ultimately decided to not bother.

I know that we'll sooner or later replace the entire component, however
this is the part of the patch I have working for a while, so I figured I
might still submit it for the time being.

Change-Id: I21ca1aa0d8ae281d2eacddf26e0aa825272707e5
2024-10-14 06:01:18 +00:00
Zebreus d726236e27 testsuite: Fix tests on systems with a non-master defaultBranch
When the git default branch is not set to master the installcheck
test suite fails. This patch adjusts the test setup scripts to
ignore the system and user git config files.

GIT_CONFIG_SYSTEM is set to /dev/null to ignore /etc/gitconfig

GIT_CONFIG_GLOBAL is not set because the global config files
are loaded from $HOME or $XDG_CONFIG_HOME which we already
reset.

git documentation: https://git-scm.com/docs/git#Documentation/git.txt-codeGITCONFIGGLOBALcode

Change-Id: Ie73bbed1db9419c9885b9d57e4edb7a4047d5cce
2024-10-14 05:56:02 +00:00
jadeandGerrit Code Review 326cbecb61 Merge changes I327db40f,If762efce into main
* changes:
  testsuite: use xdist for parallel test running
  testsuite: add a functional2 test suite based on pytest
2024-10-14 05:53:44 +00:00
Maximilian Bosch a322fcea4a worker: respect C-c on sudo nix-build
While debugging something else I observed that latest `main` ignores
`Control-C` on `sudo nix-build`.

After reading through the capnproto docs, it seems as if the promise
must be fulfilled to actually terminate the `promise.wait()` below.

This also applies to scenarios such as stopping the client
(`nix-build`), but the builders on the daemon-side are still running,
i.e. closes #540

Co-authored-by: eldritch horrors <pennae@lix.systems>

Change-Id: I9634d14df4909fc1b65d05654aad0309bcca8a0a
2024-10-12 21:16:30 +02:00
Jade Lovelace a0fb52c0af Fix std::terminate call in thread pool
So we received a report that the thread pool crashed due to an
Interrupted exception.

Relevant log tail:

copying path '/nix/store/0kal2k73inviikxv9f1ciaj39lkl9a87-etc-os-release' to 'ssh://192.168.0.27'...
Lix crashed. This is a bug. We would appreciate if you report it along with what caused it at https://git.lix.systems/lix-project/lix/issues with the following information included:

error (ignored): error: interrupted by the user
Exception: nix::Interrupted: error: interrupted by the user

Relevant stack trace:

 4# __cxa_rethrow in /nix/store/22nxhmsfcv2q2rpkmfvzwg2w5z1l231z-gcc-13.3.0-lib/lib/libstdc++.so.6
 5# nix::ignoreExceptionExceptInterrupt(nix::Verbosity) in /nix/store/ghxr2ykqc3rrfcy8rzdys0rzx9ah5fqj-lix-2.92.0-dev-pre20241005-ed9b7f4/lib/liblixutil.so
 6# nix::ThreadPool::doWork(bool) in /nix/store/ghxr2ykqc3rrfcy8rzdys0rzx9ah5fqj-lix-2.92.0-dev-pre20241005-ed9b7f4/lib/liblixutil.so
 7# 0x00007FA7A00E86D3 in /nix/store/22nxhmsfcv2q2rpkmfvzwg2w5z1l231z-gcc-13.3.0-lib/lib/libstdc++.so.6
 8# 0x00007FA79FE99A42 in /nix/store/3dyw8dzj9ab4m8hv5dpyx7zii8d0w6fi-glibc-2.39-52/lib/libc.so.6
 9# 0x00007FA79FF1905C in /nix/store/3dyw8dzj9ab4m8hv5dpyx7zii8d0w6fi-glibc-2.39-52/lib/libc.so.6

Notably, this is *not* in the main thread, so this implies that the
thread didn't get joined properly before their destructors got called.
That, in turn, should have only possibly happened because join() threw
on a previous iteration of the loop joining threads, I think. Or if it
threw while in the ThreadPool destructor. Either way we had better stop
letting Interrupted fall out of our child threads!

If:
- Interrupted was thrown inside the action in the main thread: it would
  have fallen out of doWork if state->exception was already set and got
  caught by ThreadPool::process, calling shutdown() and the join loop
  which would crash the process entirely.
- Interrupted was thrown inside the action on a secondary thread: it
  would have been caught and put into the exception field and then
  possibly rethrown to fall out of the thread (since it was previously
  ignoreExceptionExceptInterrupt).

The one possible hole in this hypothesis is that there is an "error
(ignored)" line in there implying that at least one Interrupted got
eaten by an ignoreExceptionInDestructor. It's also unclear whether this
got reordered because of stderr buffering.

Fixes: https://git.lix.systems/lix-project/lix/issues/542
Change-Id: I322cf050da660af78f5cb0e08ec6e6d27d09ac76
2024-10-09 15:38:40 -07:00
Jade Lovelace 822997bd34 libstore: ban unpacking case hacked filenames from NARs
There is absolutely no good reason these should show up in NARs besides
misconfigured systems and as long as the case hack exists, unpacking
such a NAR will cause its repacking to be wrong on systems with case
hack enabled.

This should not have any security impact on Lix to fix, but it was one
of the vectors for CVE-2024-45593:
https://github.com/NixOS/nix/security/advisories/GHSA-h4vv-h3jq-v493

Change-Id: I85b6075aacc069ee7039240b0f525804a2d8edcb
2024-10-09 14:47:39 -07:00
Jade Lovelace 4180b84a67 testsuite: use xdist for parallel test running
This is capped at 12 because 3.7 seconds of startup is painful enough
and 5.5 seconds with 24 was more annoying.

Change-Id: I327db40fd98deaa5330cd9cf6de99fb07b2c1cb0
2024-10-09 14:47:39 -07:00
Jade Lovelace 3571817e3a testsuite: add a NAR generator with some evil NARs
This also rewrites a lot of the command handling in the fixtures
library, since we want to more precisely control which way that the nix
store is set up in the tests, rather than the previous method of
renaming /nix/store to some temp dir (which allows builds but does not
allow any /nix/store paths or stability across runs, which is a
significant issue for snapshot testing).

It uses a builder to reduce the amount of state carelessly thrown
around.

The evil NARs are inspired by CVE-2024-45593
(https://github.com/NixOS/nix/security/advisories/GHSA-h4vv-h3jq-v493).

No bugs were found in this endeavor.

Change-Id: Iee41b055fa96529c5a3c761f680ed1d0667ba5da
2024-10-09 14:47:39 -07:00
Jade Lovelace 3caf3e1e08 testsuite: add a functional2 test suite based on pytest
I am tired of bad shell scripts, let me write bad python quickly
instead. It's definitely, $100%, better.

This is not planned as an immediate replacement of the old test suite,
but we::jade would not oppose tests getting ported.

What is here is a mere starting point and there is a lot more
functionality that we need.

Fixes: https://git.lix.systems/lix-project/lix/issues/488

Change-Id: If762efce69030bb667491b263b874c36024bf7b6
2024-10-09 14:47:39 -07:00
jadeandGerrit Code Review 9865ebaaa6 Merge "Remove static initializers for RegisterLegacyCommand" into main 2024-10-09 20:37:58 +00:00
jadeandGerrit Code Review 7f7a38f278 Merge changes Ib27cb43d,I03687b8b into main
* changes:
  testsuite: override NIX_CONF_DIR and NIX_USER_CONF_FILES
  Remove some outdated `make test` invocation suggestions
2024-10-09 20:37:16 +00:00
rebecca “wiggles” turnerandGerrit Code Review 0012887310 Merge "Add release note for CTRL-C improvements" into main 2024-10-08 22:15:56 +00:00
Lulu 4ea8c9d643 Set c++ version to c++23
I followed @pennae's advice and moved the constructor definition of
`AttrName` from the header file `nixexpr.hh` to `nixexpr.cc`.

Change-Id: I733f56c25635b366b11ba332ccec38dd7444e793
2024-10-08 20:05:28 +02:00
Lulu 43e79f4434 Fix gcc warning -Wmissing-field-initializers
The approach that was taken here was to add default values to the type
definitions rather than specify them whenever they are missing.

Now the only remaining warning is '-Wunused-parameter' which @jade said
is usually counterproductive and that we can just disable it:
https://git.lix.systems/lix-project/lix/issues/456#issuecomment-6617

So this change adds the flags '-Wall', '-Wextra' and
'-Wno-unused-parameter', so that all warnings are enabled except for
'-Wunused-parameter'.

Change-Id: Ic223a964d67ab429e8da804c0721ba5e25d53012
2024-10-08 01:44:38 +00:00
LuluandGerrit Code Review 299813f324 Merge "Avoid calling memcpy when len == 0 in filetransfer.cc" into main 2024-10-08 01:41:41 +00:00
Lulu d6e1b11d3e Fix gcc warning -Wsign-compare
Add the compile flag '-Wsign-compare' and adapt the code to fix all
cases of this warning.

Change-Id: I26b08fa5a03e4ac294daf697d32cf9140d84350d
2024-10-08 01:32:12 +02:00
Lulu 51a5025913 Avoid calling memcpy when len == 0 in filetransfer.cc
There was a bug report about a potential call to `memcpy` with a null
pointer which is not reproducible:
https://git.lix.systems/lix-project/lix/issues/492

This occurred in `src/libstore/filetransfer.cc` in `InnerSource::read`.

To ensure that this doesn't happen, an early return is added before
calling `memcpy` if the length of the data to be copied is 0.

This change also adds a test that ensures that when `InnerSource::read`
is called with an empty file, it throws an `EndOfFile` exception.

Change-Id: Ia18149bee9a3488576c864f28475a3a0c9eadfbb
2024-10-08 01:26:30 +02:00
eldritch horrors ed9b7f4f84 libstore: remove Worker::{childStarted, goalFinished}
these two functions are now nearly trivial and much better inline into
makeGoalCommon. keeping them separate also separates information about
goal completion flows and how failure information ends up in `Worker`.

Change-Id: I6af86996e4a2346583371186595e3013c88fb082
2024-10-05 21:19:51 +00:00
eldritch horrors 649d8cd08f libstore: remove Worker::removeGoal
we can use our newfound powers of Goal::work Is A Real Promise to remove
completed goals from continuation promises. apart from being much easier
to follow it's also a lot more efficient because we have the iterator to
the item we are trying to remove, skipping a linear search of the cache.

Change-Id: Ie0190d051c5f4b81304d98db478348b20c209df5
2024-10-05 21:19:51 +00:00
eldritch horrors 9adf6f4568 libstore: remove Goal::notify
Goal::work() is a fully usable promise that does not rely on the worker
to report completion conditions. as such we no longer need the `notify`
field that enabled this interplay. we do have to clear goal caches when
destroying the worker though, otherwise goal promises may (incorrectly)
keep goals alive due to strong shared pointers created by childStarted.

Change-Id: Ie607209aafec064dbdf3464fe207d70ba9ee158a
2024-10-05 21:19:51 +00:00
eldritch horrors 03cbc0ecb9 libstore: move Goal::ex to WorkResult
yet another duplicated field. it's the last one though.

Change-Id: I352df8d306794d262d8c9066f3be78acd40e82cf
2024-10-05 21:19:51 +00:00
eldritch horrors 1caf2afb1d libstore: move Goal::buildResult to WorkResult
derivation goals still hold a BuildResult member variable since parts of
these results of accumulated in different places, but the Goal class now
no longer has such a field. substitution goals don't need it at all, and
derivation goals should also be refactored to not drop their buildResult

Change-Id: Ic6d3d471cdbe790a6e09a43445e25bedec6ed446
2024-10-05 20:53:39 +00:00
eldritch horrors 7ff60b7445 libstore: move Goal::exitCode to WorkResult
the field is simply duplicated between the two, and now that we can
return WorkResults from Worker::run we no longer need both of them.

Change-Id: I82fc47d050b39b7bb7d1656445630d271f6c9830
2024-10-05 20:17:20 +00:00
eldritch horrors fc6291e46d libstore: return goal results from Worker::run()
this will be needed to move all interesting result fields out of Goal
proper and into WorkResult. once that is done we can treat goals as a
totally internal construct of the worker mechanism, which also allows
us to fully stop exposing unclear intermediate state to Worker users.

Change-Id: I98d7778a4b5b2590b7b070bdfc164a22a0ef7190
2024-10-05 20:12:13 +00:00
eldritch horrors 40f154c0ed libstore: remove Worker::topGoals
since we now propagate goal exceptions properly we no longer need to
check topGoals for a reason to abort early. any early abort reasons,
whether by exception or a clean top goal failure, can now be handled
by inspecting the goal result in the main loop. this greatly reduces
goal-to-goal interactions that do not happen at the main loop level.

since the underscore-free name is now available for use as variables
we'll migrate to that where we currently use `_topGoals` for locals.

Change-Id: I5727c5ea7799647c0a69ab76975b1a03a6558aa6
2024-10-05 19:53:30 +00:00
eldritch horrors f389a54079 libstore: propagate goal exceptions using promises
drop childException since it's no longer needed. also makes
waitForInput, childFinished, and childTerminated redundant.

Change-Id: I05d88ffd323c5b5c909ac21056162f69ffb0eb9f
2024-10-05 19:44:47 +00:00
eldritch horrors 7ef4466018 libstore: have goals promise WorkResults, not void
Change-Id: Idd218ec1572eda84dc47accc0dcd8a954d36f098
2024-10-05 19:06:59 +00:00
eldritch horrors a9f2aab226 libstore: extract Worker::goalFinished specifics
there's no reason to have the worker set information on goals that the
goals themselves return from their entry point. doing this in the goal
`work()` function is much cleaner, and a prerequisite to removing more
implicit strong shared references to goals that are currently running.

Change-Id: Ibb3e953ab8482a6a21ce2ed659d5023a991e7923
2024-10-05 19:06:59 +00:00
eldritch horrors 99edc2ae38 libstore: check for interrupts in parallel promise
this simplifies the worker loop, and lets us remove it entirely later.
note that ideally only one promise waiting for interrupts should exist
in the entire system. not one per event loop, one per *process*. extra
interrupt waiters make interrupt response nondeterministic and as such
aren't great for user experience. if anything wants to react to aborts
caused by explicit interruptions, or anything else, those things would
be much better served using RAII guards such as Finally (or KJ_DEFER).

Change-Id: I41d035ff40172d536e098153c7375b0972110d51
2024-10-05 19:06:59 +00:00
eldritch horrors 896a123605 libstore: remove Goal::StillAlive
this was a triumph. i'm making a note here: huge success. it's hard to
overstate my satisfaction! i'm not even angry. i'm being so sincere ri

actually, no. we *are* angry. this was one dumbass odyssey. nobody has
asked for this. but not doing it would have locked us into old, broken
protocols forever or (possibly worse) forced us to write our own async
framework building on the old did-you-mean-continuations in Worker. if
we had done that we'd be locked into ever more, and ever more complex,
manual state management all over the place. this just could not stand.

Change-Id: I43a6de1035febff59d2eff83be9ad52af4659871
2024-10-05 18:21:02 +00:00
Rebecca Turner 0d484aa498 Add release note for CTRL-C improvements
I'm very excited for cl/2016, so others will probably be excited also!
Let's add a release note.

Change-Id: Ic84a4444241aafce4cb6d5a6d1dddb47e7a7dd7b
2024-10-05 10:40:51 -07:00
rebecca “wiggles” turnerandGerrit Code Review 86b213e632 Merge "Split ignoreException to avoid suppressing CTRL-C" into main 2024-10-05 17:33:00 +00:00
eldritch horrors a3dd07535c fix build test error count checks
with async runtime scheduling we can no longer guarantee exact error
counts for builds that do not set keepGoing. the old behavior can be
recovered with a number of hacks that affect scheduling, but none of
those are very easy to follow now advisable. exact error counts will
like not be needed for almost all uses except tests, and *those* had
better check the actual messages rather than how many they got. more
messages can even help to avoid unnecessary rebuilds for most users.

Change-Id: I1c9aa7a401227dcaf2e19975b8cb83c5d4f85d64
2024-10-05 16:21:19 +00:00
Alois Wohlschlager 5df2cccc49 doc: install the HTML manual again
In 0e6b3435a1, installation of the HTML manual
was accidentally dropped: setting install_dir on a custom_target only sets the
directory where something is going to be installed if it is installed at all,
but does not itself trigger installation. The latter has to be explicitly
requested, which is just what we do here to get the manual back.

Change-Id: Iff8b791de7e7cb4c8d747c2a9b1154b5fcc32fe0
2024-10-05 10:49:34 +02:00
Jade Lovelace 345e3d068a testsuite: override NIX_CONF_DIR and NIX_USER_CONF_FILES
The test suite can load the global configuration files under certain
circumstances, and, though we would really rather it didn't ever do that
at all, we should at least break the mechanism.

Fixes: https://git.lix.systems/lix-project/lix/issues/474
Change-Id: Ib27cb43dd5dfaa70ac491c395b5ba308fd7bd289
2024-10-04 19:17:08 -07:00
Jade Lovelace 19edaed81b Remove some outdated make test invocation suggestions
These should be meson.

Change-Id: I03687b8b03f50fb1684e7ffcd487be855052d6c2
2024-10-04 18:55:52 -07:00
eldritch horrors 5b1715e633 libstore: forbid addWantedGoals when finished
due to event loop scheduling behavior it's possible for a derivation
goal to fully finish (having seen all paths it was asked to create),
but to not notify the worker of this in time to prevent another goal
asking the recently-finished goal for more outputs. if this happened
the finished goal would ignore the request for more outputs since it
considered itself fully done, and the delayed result reporting would
cause the requesting goal to assume its request had been honored. if
the requested goal had finished *properly* the worker would recreate
it instead of asking for more outputs, and this would succeed. it is
thus safe to always recreate goals once they are done, so we now do.

Change-Id: Ifedd69ca153372c623abe9a9b49cd1523588814f
2024-10-04 17:49:57 +00:00
rebecca “wiggles” turnerandGerrit Code Review 0b29859cfe Merge "editorconfig: Add meson.build" into main 2024-10-04 16:36:20 +00:00
Olivia CrainandGerrit Code Review 1bfc37fea5 Merge "internal-api-docs: allow Doxygen to build regardless of workdir" into main 2024-10-04 09:59:01 +00:00
Olivia CrainandGerrit Code Review 8f300fbd82 Merge "build: let meson add compiler flags for libstdc++ assertions" into main 2024-10-04 09:58:32 +00:00
Rebecca Turner 36073781fb editorconfig: Add meson.build
Change-Id: Ibb59ddc21f5d3ef7fb4c900e3413e426c201334d
2024-10-01 16:09:47 -07:00
Rebecca Turner b63d4a0c62 Remove static initializers for RegisterLegacyCommand
This moves the "legacy"/"nix2" commands under a new `src/legacy/`
directory, instead of being scattered around in a bunch of different
directories.

A new `liblegacy` build target is defined, and the `nix` binary is
linked against it.

Then, `RegisterLegacyCommand` is replaced with `LegacyCommand::add`
calls in functions like `registerNixCollectGarbage()`. These
registration functions are called explicitly in `src/nix/main.cc`.

See: https://git.lix.systems/lix-project/lix/issues/359

Change-Id: Id450ffc3f793374907599cfcc121863b792aac1a
2024-10-01 16:08:58 -07:00
Robert HensingandRebecca Turner ee0c195eba Split ignoreException to avoid suppressing CTRL-C
This splits `ignoreException` into `ignoreExceptionExceptInterrupt`
(which ignores all exceptions except `Interrupt`, which indicates a
SIGINT/CTRL-C) and `ignoreExceptionInDestructor` (which ignores all
exceptions, so that destructors do not throw exceptions).

This prevents many cases where Nix ignores CTRL-C entirely.
See: https://github.com/NixOS/nix/issues/7245

Upstream-PR: https://github.com/NixOS/nix/pull/11618
Change-Id: Ie7d2467eedbe840d1b9fa2e88a4e88e4ab26a87b
2024-10-01 15:49:56 -07:00
eldritch horrors 7752927660 libstore: turn DerivationGoal::work into *one* promise
Change-Id: Ic2f7bc2bd6a1879ad614e4be81a7214f64eb0e85
2024-10-01 11:55:47 +00:00
eldritch horrors 3edc272341 libstore: turn DrvOutputSubstitutionGoal::work into *one* promise
Change-Id: I2d4dcedff0a278d2d8f3d264a9186dfb399275e2
2024-10-01 11:55:42 +00:00
eldritch horrors 9b05636937 libstore: make PathSubstitutionGoal::work *one* promise
Change-Id: I38cfe8c7059251b581f1013c4213804f36b985ea
2024-10-01 11:55:36 +00:00
eldritch horrors 9889c79fe3 libstore: turn Worker::updateStatistics into a promise
we'll now loop to update displayed statistics, and use this loop to
limit the update rate to 50 times per second. we could have updated
much more frequently before this (once per iteration of `runImpl`),
much faster than would ever be useful in practice. aggressive stats
updates can even impede progress due to terminal or network delays.

Change-Id: Ifba755a2569f73c919b1fbb06a142c0951395d6d
2024-10-01 11:55:29 +00:00
eldritch horrors 732de75f67 libstore: remove Worker::wakeUp()
Worker::run() is now entirely based on the kj event loop and promises,
so we need not handle awakeness of goals manually any more. every goal
can instead, once it has finished a partial work call, defer itself to
being called again in the next iteration of the loop. same end effect.

Change-Id: I320eee2fa60bcebaabd74d1323fa96d1402c1d15
2024-10-01 13:55:03 +02:00
eldritch horrors d5db0b1abc libstore: turn periodic gc attempt into a promise
notably we will check whether we want to do GC at all only once during
startup, and we'll only attempt GC every ten seconds rather than every
time a goal has finished a partial work call. this shouldn't cause any
problems in practice since relying on auto-gc is not deterministic and
stores in which builds can fill all remaining free space in merely ten
seconds are severely troubled even when gargage collection runs a lot.

Change-Id: I1175a56bf7f4e531f8be90157ad88750ff2ddec4
2024-10-01 11:36:45 +00:00
eldritch horrors b0c7c1ec66 libstore: turn Worker::run() main loop into a promise
Change-Id: Ib112ea9a3e67d5cb3d7d0ded30bbd25c96262470
2024-10-01 11:36:45 +00:00
eldritch horrors d31310bf59 libstore: turn waitForInput into a promise
Change-Id: I8355d8d3f6c43a812990c1912b048e5735b07f7b
2024-10-01 11:36:45 +00:00
Raito Bezarius 8e05cc1e6c Revert "libstore: remove worker removeGoal"
Revert submission 1946

Reason for revert: regression in building (found via bisection)

Reported by users:
> error: path '/nix/store/04ca5xwvasz6s3jg0k7njz6rzi0d225w-jq-1.7.1-dev' does not exist in the store

Reverted changes: /q/submissionid:1946

Change-Id: I6f1a4b2f7d7ef5ca430e477fc32bca62fd97036b
2024-10-01 11:07:57 +00:00
Jonas ChevalierandGerrit Code Review a16ceb9411 Merge "fix(nix fmt): remove the default "." argument" into main 2024-09-30 16:10:32 +00:00
eldritch horrors aa33c34c9b libstore: merge ContinueImmediately and StillAlive
nothing needs to signal being still active but not actively pollable,
only that immediate polling for the next goal work phase is in order.

Change-Id: Ia43c1015e94ba4f5f6b9cb92943da608c4a01555
2024-09-29 15:29:56 +00:00
eldritch horrors ccd2862666 libstore: remove worker removeGoal
this was immensely inefficient on large caches, as can exist when many
derivations are buildable simultaneously. since we have smart pointers
to goals we can do cache maintenance in goal deleters instead, and use
the exact iterators instead of doing a linear search. this *does* rely
on goals being deleted to remove them from the cache, which isn't true
for toplevel goals. those would have previously been removed when done
in all cases, removing the cache entry when keep-going is set. this is
arguably incorrect since it might result in those goals being retried,
although that could only happen with dynamic derivations or the likes.
(luckily dynamic derivations not complete enough to allow this at all)

Change-Id: I8e750b868393588c33e4829333d370f2c509ce99
2024-09-29 15:29:56 +00:00
eldritch horrors 47ddd11933 libstore: extract a real makeGoalCommon
makeDerivationGoalCommon had the right idea, but it didn't quite go far
enough. let's do the rest and remove the remaining factory duplication.

Change-Id: I1fe32446bdfb501e81df56226fd962f85720725b
2024-09-29 15:07:30 +00:00
eldritch horrors 7f4f86795c libstore: remove Goal::key
this was a debugging aid from day one that should not have any impact on
build semantics, and if it *does* have an impact on build semantics then
build semantics are seriously broken. keeping the order imposed by these
keys will be impossible once we let a real event loop schedule our jobs.

Change-Id: I5c313324e1f213ab6453d82f41ae5e59de809a5b
2024-09-29 14:29:14 +00:00
eldritch horrors a5240b23ab libstore: make non-cache goal pointers strong
without circular references we do not need weak goal pointers except for
caches, which should not prevent goal destructors running. caches though
cannot create circular references even when they keep strong references.
if we removed goals from caches when their work() is fully finished, not
when their destructors are run, we could keep strong pointers in caches.
since we do not gain much from this we keep those pointers weak for now.

Change-Id: I1d4a6850ff5e264443c90eb4531da89f5e97a3a0
2024-09-29 14:29:14 +00:00
eldritch horrors 8fb642b6e0 libstore: remove Goal::WaitForWorld
have DerivationGoal and its subclasses produce a wrapper promise for
their intermediate results instead, and return this wrapper promise.
Worker already handles promises that do not complete immediately, so
we do not have to duplicate this into an entire result type variant.

Change-Id: Iae8dbf63cfc742afda4d415922a29ac5a3f39348
2024-09-29 14:29:14 +00:00
eldritch horrors 1a52e4f755 libstore: fix build tests
the new event loop could very occasionally notice that a dependency of
some goal has failed, process the failure, cause the depending goal to
fail accordingly, and in the doing of the latter two steps let further
dependencies that previously have not been reported as failed do their
reporting anyway. in such cases a goal could fail with "1 dependencies
failed", but more than one dependency failure message was shown. we'll
now report the correct number of failed dependency goals in all cases.

Change-Id: I5aa95dcb2db4de4fd5fee8acbf5db833531d81a8
2024-09-29 13:17:15 +00:00
eldritch horrors 3f7519526f libstore: have makeLocalDerivationGoal return unique_ptrs
these can be unique rather than shared because shared_ptr has a
converting constructor. preparatory refactor for something else
and not necessary on its own, and the extra allocations we must
do for shared_ptr control blocks isn't usually relevant anyway.

Change-Id: I5391715545240c6ec8e83a031206edafdfc6462f
2024-09-29 12:09:24 +00:00
Maximilian BoschandGerrit Code Review 289e7a6b5a Merge "libfetchers/git: restore compat with builtins.fetchGit from 2.3" into main 2024-09-29 08:56:16 +00:00
Olivia CrainandGerrit Code Review f12b60273b Merge changes I5c640824,I09ffc92e,I259583b7 into main
* changes:
  build: require meson 1.4.0 or newer
  build: fix deprecated uses of configure_file
  build: install html manual without using install_subdir
2024-09-28 23:41:30 +00:00
Maximilian Bosch 04daff94e3 libfetchers/git: restore compat with builtins.fetchGit from 2.3
Since fb38459d6e, each `ref` is appended
with `refs/heads` unless it starts with `refs/` already. This regressed
two use-cases that worked fine before:

* Specifying a commit hash as `ref`: now, if `ref` looks like a commit
  hash it will be directly passed to `git fetch`.

* Specifying a tag without `refs/tags` as prefix: now, the fetcher prepends
  `refs/*` to a ref that doesn't start with `refs/` and doesn't look
  like a commit hash. That way, both a branch and a tag specified in
  `ref` can be fetched.

  The order of preference in git is

  * file in `refs/` (e.g. `HEAD`)
  * file in `refs/tags/`
  * file in `refs/heads` (i.e. a branch)

  After fetching `refs/*`, ref is resolved the same way as git does.

Change-Id: Idd49b97cbdc8c6fdc8faa5a48bef3dec25e4ccc3
2024-09-28 14:52:06 +02:00
Olivia Crain 4780dd6bc4 build: let meson add compiler flags for libstdc++ assertions
We have manually enabled libstdc++ assertions since cl/797. Meson 1.4.0
(the minimum version we mandate) enables this by default, so we can
remove the enabling compiler flag from the list of project arguments.

With this patch, `-D_GLIBCXX_ASSERTIONS=1` is still present in the
compile command logs when building with both gccStdenv and clangStdenv.

See: https://gerrit.lix.systems/c/lix/+/797
See: https://mesonbuild.com/Release-notes-for-1-4-0.html#ndebug-setting-now-controls-c-stdlib-assertions
Change-Id: I53483fadfe5cbd11ba35544b437d3a9ee8031631
2024-09-27 12:26:07 -05:00
Olivia Crain b86863d935 build: require meson 1.4.0 or newer
This was already the de facto requirement, we use the method `full_path`
on a file object (introduced in Meson 1.4.0) in the functional test
suite's build.

This version of Meson is in NixOS 24.05, so there should be no
compatibility issues should this make it into a backported release of
Lix.

CC: https://git.lix.systems/lix-project/lix/issues/247
Change-Id: I5c640824807353b6eb4287e7ed09c4e89a4bdde2
2024-09-27 11:57:53 -05:00
Olivia Crain 624f44bf25 build: fix deprecated uses of configure_file
Using `configure_file` to copy files has been deprecated since Meson 0.64.0.
The intended replacement is the `fs.copyfile` method.

This removes the following deprecation warning that arises when a minimum
Meson version is specified:

``
Project [...] uses feature deprecated since '0.64.0': copy arg in configure_file. Use fs.copyfile instead
``

Change-Id: I09ffc92e96311ef9ed594343a0a16d51e74b114a
2024-09-27 11:55:32 -05:00
Olivia Crain 0e6b3435a1 build: install html manual without using install_subdir
In Meson, `install_subdir` is meant to be used with directories in the source
directory. When using it to install the HTML manual, we provide it with a path
under the build directory.

We should instead specify an install directory for the HTML manual as part of
the custom target that builds it.

What we do currently isn't broken, just semantically incorrect. Changing it does
get rid of the following deprecation warning, though:

``
Project [...] uses feature deprecated since '0.60.0': install_subdir with empty directory. It worked by accident and is buggy. Use install_emptydir instead.
``

Change-Id: I259583b7bdff8ecbb3b342653d70dc5f034c7fad
2024-09-27 11:55:28 -05:00
eldritch horrors ae5d8dae1b libstore: turn Goal::WaitForGoals into a promise
also gets rid of explicit strong references to dependencies of any goal,
and weak references to dependers as well. those are now only held within
promises representing goal completion and thus independent of the goal's
relation to each other. the weak references to dependers was only needed
for notifications, and that's much better handled entirely by kj itself.

Change-Id: I00d06df9090f8d6336ee4bb0c1313a7052fb016b
2024-09-27 16:40:27 +02:00
eldritch horrors 852da07b67 libstore: replace Goal::WaitForSlot with semaphores
now that we have an event loop in the worker we can use it and its
magical execution suspending properties to replace the slot counts
we managed explicitly with semaphores and raii tokens. technically
this would not have needed an event loop base to be doable, but it
is a whole lot easier to wait for a token to be available if there
is a callback mechanism ready for use that doesn't require a whole
damn dedicated abstract method in Goal to work, and specific calls
to that dedicated method strewn all over the worker implementation

Change-Id: I1da7cf386d94e2bbf2dba9b53ff51dbce6a0cff7
2024-09-27 16:40:27 +02:00
eldritch horrors bf32085d63 libstore: simplify Worker::waitForInput
with waitForAWhile turned into promised the core functionality of
waitForInput is now merely to let gc run every so often if needed

Change-Id: I68da342bbc1d67653901cf4502dabfa5bc947628
2024-09-27 16:40:26 +02:00
eldritch horrors cd1ceffb0e libstore: make waiting for a while a promise
this simplifies waitForInput quite a lot, and at the same time makes
polling less thundering-herd-y. it even fixes early polling wakeups!

Change-Id: I6dfa62ce91729b8880342117d71af5ae33366414
2024-09-27 16:39:33 +02:00
eldritch horrors 0478949c72 libstore: turn builder output processing into event loop
this removes the rather janky did-you-mean-async poll loop we had so
far. sadly kj does not play well with pty file descriptors, so we do
have to add our own async input stream that does not eat pty EIO and
turns it into an exception. that's still a *lot* better than the old
code, and using a real even loop makes everything else easier later.

Change-Id: Idd7e0428c59758602cc530bcad224cd2fed4c15e
2024-09-27 16:38:16 +02:00
zimbatmandJade Lovelace 2265536e85 fix(nix fmt): remove the default "." argument
When `nix fmt` is called without an argument, Nix appends the "." argument before calling the formatter. The comment in the code is:
> Format the current flake out of the box

This also happens when formatting sub-folders.

This means that the formatter is now unable to distinguish, as an interface, whether the "." argument is coming from the flake or the user's intent to format the current folder. This decision should be up to the formatter.

Treefmt, for example, will automatically look up the project's root and format all the files. This is the desired behaviour. But because the "." argument is passed, it cannot function as expected.

Upstream-PR: https://github.com/nixos/nix/pull/11438

Change-Id: I60fb6b3ed4ec1b24f81b5f0d76c0be98470817ce
2024-09-26 14:32:29 -07:00
jadeandGerrit Code Review 14dc84ed03 Merge changes Iaa2e0e9d,Ia973420f into main
* changes:
  Fix passing custom CA files into the builtin:fetchurl sandbox
  [security] builtin:fetchurl: Enable TLS verification
2024-09-26 20:53:46 +00:00
eldritch horrorsandGerrit Code Review 619a93bd54 Merge "libutil: add async collection mechanism" into main 2024-09-26 17:23:52 +00:00
jadeandGerrit Code Review 5dc7671d81 Merge "fmt: fail hard on bad format strings going into nix::fmt too" into main 2024-09-26 17:07:29 +00:00
jadeandGerrit Code Review b6038e988d Merge "main: log stack traces for std::terminate" into main 2024-09-26 17:06:01 +00:00
eldritch horrors 531d040e8c libutil: add async collection mechanism
like kj::joinPromisesFailFast this allows waiting for the results of
multiple promises at once, but unlike it not all input promises must
be complete (or any of them failed) for results to become available.

Change-Id: I0e4a37e7bd90651d56b33d0bc5afbadc56cde70c
2024-09-26 16:56:08 +00:00
eldritch horrors ca9256a789 libutil: add an async semaphore implementation
like a normal semaphore, but with awaitable acquire actions. this is
primarily intended as an intermediate concurrency limiting device in
the Worker code, but it may find other uses over time. we do not use
std::counting_semaphore as a base because the counter of that is not
inspectable as will be needed for Worker. we also do not need atomic
operations for cross-thread consistency since we don't have multiple
threads (thanks to kj event loops being confined to a single thread)

Change-Id: Ie2bcb107f3a2c0185138330f7cbba4cec6cbdd95
2024-09-26 16:32:02 +00:00
eldritch horrors 4b66e1e24f fix internal-api-docs build
this one is also run from a gcc stdenv.

Change-Id: I91ff6915c6689ece15224f348f54367cff5d2b5a
2024-09-26 16:11:43 +00:00
Puck Meerburg 37b22dae04 Fix passing custom CA files into the builtin:fetchurl sandbox
Without this, verifying TLS certificates would fail on macOS, as well
as any system that doesn't have a certificate file at /etc/ssl/certs/ca-certificates.crt,
which includes e.g. Fedora.

Change-Id: Iaa2e0e9db3747645b5482c82e3e0e4e8f229f5f9
2024-09-26 15:25:28 +00:00
Maximilian BoschandGerrit Code Review 31954b5136 Merge "flake: use clangStdenv for overlays.default" into main 2024-09-26 07:13:41 +00:00
Jade Lovelace acf963468f flake.lock: update everything
Periodic updating of everything.

Change-Id: Ie006fd7c2dc0725309dea831685ea7b24b569df5
2024-09-25 18:42:16 -07:00
Eelco DolstraandJade Lovelace c1631b0a39 [security] builtin:fetchurl: Enable TLS verification
This is better for privacy and to avoid leaking netrc credentials in a
MITM attack, but also the assumption that we check the hash no longer
holds in some cases (in particular for impure derivations).

Partially reverts https://github.com/NixOS/nix/commit/5db358d4d78aea7204a8f22c5bf2a309267ee038.

(cherry picked from commit c04bc17a5a0fdcb725a11ef6541f94730112e7b6)
(cherry picked from commit f2f47fa725fc87bfb536de171a2ea81f2789c9fb)
(cherry picked from commit 7b39cd631e0d3c3d238015c6f450c59bbc9cbc5b)

Upstream-PR: https://github.com/NixOS/nix/pull/11585

Change-Id: Ia973420f6098113da05a594d48394ce1fe41fbb9
2024-09-25 18:40:58 -07:00
Jade Lovelace aca19187d0 fmt: fail hard on bad format strings going into nix::fmt too
Previously we would only crash the program for bad HintFmt calls.
nix::fmt should also crash.

Change-Id: I4ba0abeb8557b208bd9c0be624c022a60446ef7e
2024-09-25 15:20:48 -07:00
Jade Lovelace 19e0ce2c03 main: log stack traces for std::terminate
These stack traces kind of suck for the reasons mentioned on the
CppTrace page here (no symbols for inline functions is a major one):
https://github.com/jeremy-rifkin/cpptrace

I would consider using CppTrace if it were packaged, but to be honest, I
think that the more reasonable option is actually to move entirely to
out-of-process crash handling and symbolization.

The reason for this is that if you want to generate anything of
substance on SIGSEGV or really any deadly signal, you are stuck in
async-signal-safe land, which is not a place to be trying to run a
symbolizer. LLVM does it anyway, probably carefully, and chromium *can*
do it on debug builds but in general uses crashpad:
https://source.chromium.org/chromium/chromium/src/+/main:base/debug/stack_trace_posix.cc;l=974;drc=82dff63dbf9db05e9274e11d9128af7b9f51ceaa;bpv=1;bpt=1

However, some stack traces are better than *no* stack traces when we get
mystery exceptions falling out the bottom of the program. I've also
promoted the path for "mystery exceptions falling out the bottom of the
program" to hard crash and generate a core dump because although there's
been some months since the last one of these, these are nonetheless
always *atrociously* diagnosed.

We can't improve the crash handling further until either we use Crashpad
(which involves more C++ deps, no thanks) or we put in the ostensibly
work in progress Rust minidump infrastructure, in which case we need to
finish full support for Rust in libutil first.

Sample report:

Lix crashed. This is a bug. We would appreciate if you report it at https://git.lix.systems/lix-project/lix/issues with the following information included:

Exception: std::runtime_error: lol
Stack trace:
 0# nix::printStackTrace() in /home/jade/lix/lix3/build/src/nix/../libutil/liblixutil.so
 1# 0x000073C9862331F2 in /home/jade/lix/lix3/build/src/nix/../libmain/liblixmain.so
 2# 0x000073C985F2E21A in /nix/store/p44qan69linp3ii0xrviypsw2j4qdcp2-gcc-13.2.0-lib/lib/libstdc++.so.6
 3# 0x000073C985F2E285 in /nix/store/p44qan69linp3ii0xrviypsw2j4qdcp2-gcc-13.2.0-lib/lib/libstdc++.so.6
 4# nix::handleExceptions(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&, std::function<void ()>) in /home/jade/lix/lix3/build/src/nix/../libmain/liblixmain.so
 5# 0x00005CF65B6B048B in /home/jade/lix/lix3/build/src/nix/nix
 6# 0x000073C985C8810E in /nix/store/dbcw19dshdwnxdv5q2g6wldj6syyvq7l-glibc-2.39-52/lib/libc.so.6
 7# __libc_start_main in /nix/store/dbcw19dshdwnxdv5q2g6wldj6syyvq7l-glibc-2.39-52/lib/libc.so.6
 8# 0x00005CF65B610335 in /home/jade/lix/lix3/build/src/nix/nix

Change-Id: I1a9f6d349b617fd7145a37159b78ecb9382cb4e9
2024-09-25 14:03:45 -07:00
jadeandGerrit Code Review 8a6b84df14 Merge "package.nix: fix cross for editline" into main 2024-09-25 20:23:39 +00:00
Maximilian Bosch eccbe9586a flake: use clangStdenv for overlays.default
We don't support GCC anymore for building, so the overlay currently
fails to evaluate with

    error: assertion '((stdenv).cc.isClang || lintInsteadOfBuild)' failed

`clangStdenv` seems like a reasonable default now.

Noticed while upgrading Lix for our Hydra fork.

Change-Id: I948a7c03b3e5648fc7c596f96e1b8053a9e7f92f
2024-09-25 18:31:34 +02:00
Olivia Crain 2f794733b2 internal-api-docs: allow Doxygen to build regardless of workdir
Previously, Doxygen needed to be ran from the project's source root dir
due to the relative paths in the config's `INPUT` tag. We now preprocess
the relative paths by prefixing them with the absolute path of the
project's source root dir. The HTML output remains unchanged.

Fixes: https://git.lix.systems/lix-project/lix/issues/240
Change-Id: I85f099c22bfc5fdbf26be27c2db7dcbc8155c8b2
2024-09-24 13:26:22 -05:00
alois31andGerrit Code Review 5f298f74c9 Merge "local-store: make extended attribute handling more robust" into main 2024-09-21 07:55:13 +00:00
jadeandGerrit Code Review 79246a3733 Merge "util: fix brotli decompression of empty input" into main 2024-09-18 23:36:25 +00:00
Jade Lovelace 789b19a0cf util: fix brotli decompression of empty input
This caused an infinite loop before since it would just keep asking the
underlying source for more data.

In practice this happened because an HTTP server served a
response to a HEAD request (for which curl will not retrieve any body or
call our write callback function) with Content-Encoding: br, leading to
decompressing nothing at all and going into an infinite loop.

This adds a test to make sure none of our compression methods do that
again, as well as just patching the HTTP client to never feed empty data
into a compression algorithm (since they absolutely have the right to
throw CompressionError on unexpectedly-short streams!).

Reported on Matrix: https://matrix.to/#/!lymvtcwDJ7ZA9Npq:lix.systems/$8BWQR_zKxCQDJ40C5NnDo4bQPId3pZ_aoDj2ANP7Itc?via=lix.systems&via=matrix.org&via=tchncs.de

Change-Id: I027566e280f0f569fdb8df40e5ecbf46c211dad1
2024-09-18 15:37:29 -07:00
rebecca “wiggles” turnerandGerrit Code Review 0943b214c9 Merge "tests/compression: rewrite" into main 2024-09-18 20:38:18 +00:00
Alois Wohlschlager 2afdf1ed66 path-info: wipe the progress bar before printing
The legitimate output of `nix path-info` may visually interfere with the
progress bar, by appending to stale progress output before the latter has been
erased. Conveniently, all expensive operations (evaluation or building) have
already been performed before, so we can simply wipe the progress bar at this
point to fix the issue.

Fixes: https://git.lix.systems/lix-project/lix/issues/343
Change-Id: Id9a807a5c882295b3e6fbf841f9c15dc96f67f6e
2024-09-18 19:26:40 +02:00
Jade Lovelace ed381cd58a package.nix: fix cross for editline
editline's dep on ncurses is a runtime one, so it should be in
buildInputs, not nativeBuildInputs.

CC: https://git.lix.systems/lix-project/lix/issues/527
Change-Id: I631c192a55677b0cc77faa7511986f1fa2205e91
2024-09-17 20:43:21 -07:00
Jade Lovelace 4046e019ca tests/compression: rewrite
This test suite was in desperate need of using the parameterization
available with gtest, and was a bunch of useless duplicated code. At
least now it's not duplicated code, though it still probably should be
more full of property tests.

Change-Id: Ia8ccee7ef4f02b2fa40417b79aa8c8f0626ea479
2024-09-17 19:07:48 -07:00
rebecca “wiggles” turnerandGerrit Code Review 8ab5743904 Merge "Remove readline support" into main 2024-09-17 16:36:34 +00:00
Rebecca Turner 7ae0409989 Remove readline support
Lix cannot be built with GNU readline, and we would "rather not" be GPL.

Change-Id: I0e86f0f10dab966ab1d1d467fb61fd2de50c00de
2024-09-16 10:48:20 -07:00
Maximilian Bosch 80202e3ca3 common-eval-args: raise warning if --arg isn't a valid Nix identifier
See https://git.lix.systems/lix-project/lix/issues/496.

The core idea is to be able to do e.g.

    nix-instantiate -A some-nonfree-thing --arg config.allowUnfree true

which is currently not possible since `config.allowUnfree` is
interpreted as attribute name with a dot in it.

In order to change that (probably), Jade suggested to find out if there
are any folks out there relying on this behavior.

For such a use-case, it may still be possible to accept strings, i.e.
`--arg '"config.allowUnfree"'.

Change-Id: I986c73619fbd87a95b55e2f0ac03feaed3de2d2d
2024-09-15 16:52:30 +02:00
Jade Lovelace 727258241f fix: docs issue template was busted
Apparently forgejo has a more creative interpretation of \(\) than I was
hoping in their markdown parser and thought it was maths. I have no idea
then how you put a link in parens next to another square-bracket link,
but I am not going to worry about it.

There were several more typos, which I also fixed.

Fixes: https://git.lix.systems/lix-project/lix/issues/517
Change-Id: I6b144c6881f92ca60ba72a304ce7a0bcb9c6659a
2024-09-14 19:28:46 +00:00
jadeandGerrit Code Review 5246cea6c8 Merge "store: add a hint on how to fix Lix installs broken by macOS Sequoia" into main 2024-09-14 19:28:24 +00:00
jadeandGerrit Code Review 8f88590d13 Merge changes Ia1481da4,Ifca1d74d into main
* changes:
  archive: refactor bad mutable-state API in the NAR parse listener
  archive: rename ParseSink to NARParseVisitor
2024-09-14 19:26:08 +00:00
Alois Wohlschlager 3f07c65510 local-store: make extended attribute handling more robust
* Move the extended attribute deletion after the hardlink sanity check. We
  shouldn't be removing extended attributes on random files.
* Make the entity owner-writable before attempting to remove extended
  attributes, since this operation usually requires write access on the file,
  and we shouldn't fail xattr deletion on a file that has been made unwritable
  by the builder or a previous canonicalisation pass.

Fixes: https://git.lix.systems/lix-project/lix/issues/507
Change-Id: I7e6ccb71649185764cd5210f4a4794ee174afea6
2024-09-14 10:36:22 +02:00
Jade Lovelace b7fc37b015 store: add a hint on how to fix Lix installs broken by macOS Sequoia
This is not a detailed diagnosis, and it's not worth writing one, tbh.
This error basically never happens in normal operation, so diagnosing it
by changing the error on macOS is good enough.

Relevant: https://git.lix.systems/lix-project/lix-installer/issues/24
Relevant: https://git.lix.systems/lix-project/lix-installer/issues/18
Relevant: https://git.lix.systems/lix-project/lix/issues/521

Change-Id: I03701f917d116575c72a97502b8e1617679447f2
2024-09-14 07:31:30 +00:00
Jade Lovelace ca1dc3f70b archive: refactor bad mutable-state API in the NAR parse listener
Remove the mutable state stuff that assumes that one file is being
written a time. It's true that we don't write multiple files
interleaved, but that mutable state is evil.

Change-Id: Ia1481da48255d901e4b09a9b783e7af44fae8cff
2024-09-13 17:11:43 -07:00
alois31andGerrit Code Review b2fc007811 Merge "fish-completion: leave the shell prompt intact" into main 2024-09-12 06:14:22 +00:00
Alois Wohlschlager 82aa1ccab4 fish-completion: leave the shell prompt intact
When generating shell completions, no logging output should be visible because
it would destroy the shell prompt. Originally this was attempted to be done by
simply disabling the progress bar (ca946860ce),
since the situation is particularly bad there (the screen clearing required for
the rendering ends up erasing the shell prompt). Due to overlooking the
implementation of this hack, it was accidentally undone during a later change
(0dd1d8ca1c).
Since even with the hack correctly in place, it is still possible to mess up
the prompt by logging output (for example warnings for disabled experimental
features, or messages generated by `builtins.trace`), simply send it to the bit
bucket where it belongs. This was already done for bash and zsh
(9d840758a8), and it seems that fish was simply
missed at that time. The last trace of the no-longer-working and obsolete hack
is deleted too.

Fixes: https://git.lix.systems/lix-project/lix/issues/513
Change-Id: I59f1ebf90903034e2059298fa8d76bf970bc3315
2024-09-11 19:03:11 +02:00
Rebecca Turner df0137226d editline: Vendor cl/1883 patch to recognize Alt+Left/Alt+Right
This vendors the patch added in cl/1883 to avoid GitHub
garbage-collecting the commits we're referring to.

As @emilazy pointed out on GitHub:

> GitHub can garbage‐collect unmerged PR commits if they are later
> force‐pushed, which means that code review in upstreams can cause
> Nixpkgs builds to fail to reproduce in future.

See: https://github.com/NixOS/nixpkgs/pull/341131#discussion_r1753046220
See: https://github.com/troglobit/editline/pull/70
See: https://gerrit.lix.systems/c/lix/+/1883

Change-Id: Ifff522f7f23310d6dbe9efc72fd40be5500ae872
2024-09-11 09:35:00 -07:00
Jade Lovelace 81c2e0ac8e archive: rename ParseSink to NARParseVisitor
- Rename the listener to not be called a "sink". If it were a "sink" it
  would be eating bytes and conform with any of the Nix sink stuff
  (maybe FileHandle should be a Sink itself! but that's a later CL's
  problem). This is a parser listener.
- Move the RetrieveRegularNARSink thing into store-api.cc, which is its
  only usage, and fix it to actually do what it is stated to do: crash
  if its invariants are violated.

  It's, of course, used to erm, unpack single-file NAR files, generated
  via a horrible contraption of sources and sinks that looks like a
  plumbing blueprint. Refactoring that is a future task.
- Add a description of the invariants of NARParseVisitor in preparation
  of refactoring it.

Change-Id: Ifca1d74d2947204a1f66349772e54dad0743e944
2024-09-11 01:10:49 -07:00
rebecca “wiggles” turnerandGerrit Code Review 24db81eaf2 Merge "repl: Patch editline to recognize Meta-Left & Meta-Right" into main 2024-09-11 01:02:27 +00:00
rebecca “wiggles” turnerandGerrit Code Review cc183fdbc1 Merge "repl-overlays: Provide an elaborate example" into main 2024-09-10 00:17:01 +00:00
rebecca “wiggles” turnerandGerrit Code Review f5ae72d445 Merge "Add getCwd" into main 2024-09-10 00:10:40 +00:00
Rebecca Turner 6de6cae3e7 repl: Patch editline to recognize Meta-Left & Meta-Right
This applies https://github.com/troglobit/editline/pull/70 to our build
of editline, which translates `meta-left` and `meta-right` into
`fd_word` and `bk_word`. This makes `nix repl` soooo much nicer to use!

Note: My terminal renders `meta-left` as `\e\e[C` and `meta-right` as
`\e\e[D`.

Closes https://git.lix.systems/lix-project/lix/issues/501

Change-Id: I048b10cf17231bbf4e6bf38e1d1d8572cedaa194
2024-09-09 15:34:50 -07:00
alois31andGerrit Code Review 8f7ab26f96 Merge changes If8ec210f,I6e2851b2 into main
* changes:
  libfetchers: serialise accept-flake-config properly
  libstore: declare SandboxMode JSON serialisation in the header
2024-09-09 16:14:23 +00:00
eldritch horrors c14486ae8d forbid gcc for compilation, only allow clang
while gcc 12 and older miscompile our generators, gcc 13 and older
outright crash on kj coroutines. (newer gcc versions may fix this)

Change-Id: I19f12c8c147239680eb0fa5a84ef5c7de38c9263
2024-09-09 01:48:20 +00:00
alois31andGerrit Code Review e9505dcc5a Merge "libmain/progress-bar: erase all lines of the multi-line format" into main 2024-09-08 13:42:17 +00:00
eldritch horrors f2a49032a6 libstore: turn Worker in a kj event loop user
using a proper event loop basis we no longer have to worry about most of
the intricacies of poll(), or platform-dependent replacements for it. we
may even be able to use the event loop and its promise system for all of
our scheduling in the future. we don't do any real async processing yet,
this is just preparation to separate the first such change from the huge
api design difference with the async framework we chose (kj from capnp):

kj::Promise, unlike std::future, doesn't return exceptions unmangled. it
instead wraps any non-kj exception into a kj exception, erasing all type
information and preserving mostly the what() string in the process. this
makes sense in the capnp rpc use case where unrestricted exception types
can't be transferred, and since it moves error handling styles closer to
a world we'd actually like there's no harm in doing it only here for now

Change-Id: I20f888de74d525fb2db36ca30ebba4bcfe9cc838
2024-09-08 01:57:48 +00:00
eldritch horrors 92eccfbd68 libutil: add a result type using boost outcome
we're using boost::outcome rather than leaf or stl types because stl
types are not available everywhere and leaf does not provide its own
storage for error values, relying on thread-locals and the stack. if
we want to use promises we won't have a stack and would have to wrap
everything into leaf-specific allocating wrappers, so outcome it is.

Change-Id: I35111a1f9ed517e7f12a839e2162b1ba6a993f8f
2024-09-08 01:57:48 +00:00
Alois Wohlschlager 4715d557ef libmain/progress-bar: erase all lines of the multi-line format
When the multi-line log format is enabled, the progress bar usually occupies
multiple lines on the screen. When stopping the progress bar, only the last
line was wiped, leaving all others visible on the screen. Erase all lines
belonging to the progress bar to prevent these leftovers.
Asking the user for input is theoretically affected by a similar issue, but
this is not observed in practice since the only place where the user is asked
(whether configuration options coming from flakes should be accepted) does not
actually have multiple lines on the progress bar. However, there is no real
reason to not fix this either, so let's do it anyway.

Change-Id: Iaa5a701874fca32e6f06d85912835d86b8fa7a16
2024-09-07 10:37:12 +02:00
alois31andGerrit Code Review 991d8ce275 Merge "Stop the logger in legacy commands again" into main 2024-09-06 17:07:16 +00:00
rebecca “wiggles” turnerandGerrit Code Review 72589e7032 Merge "Test including relative paths in configuration" into main 2024-09-03 18:48:47 +00:00
rebecca “wiggles” turnerandGerrit Code Review 644176a631 Merge "Expand comment on std::string operator+" into main 2024-09-03 18:48:37 +00:00
Alois Wohlschlager 63ee2cdda3 libfetchers: serialise accept-flake-config properly
The AcceptFlakeConfig type used was missing its JSON serialisation definition,
so it was incorrectly serialised as an integer, ending up that way for example
in the nix.conf manual page. Declare a proper serialisation.

Change-Id: If8ec210f9d4dd42fe480c4e97d0a4920eb66a01e
2024-09-02 18:50:15 +02:00
Alois Wohlschlager d7c37324bb libstore: declare SandboxMode JSON serialisation in the header
The JSON serialisation should be declared in the header so that all translation
units can see it when needed, even though it seems that it has not been used
anywhere else so far. Unfortunately, this means we cannot use the
NLOHMANN_JSON_SERIALIZE_ENUM convenience macro, since it uses a slightly
different signature, but the code is not too bad either.

Change-Id: I6e2851b250e0b53114d2fecb8011ff1ea9379d0f
2024-09-02 18:50:14 +02:00
Rebecca Turner 75c0de3e3c Test including relative paths in configuration
Change-Id: If6c69a5e16d1ccd223fba392890f08f0032fb754
2024-09-01 15:52:48 -07:00
Rebecca Turner fc4a160878 repl-overlays: Provide an elaborate example
This is the repl overlay from my dotfiles, which I think provides a
reasonable and ergonomic set of variables. We can iterate on this over
time, or (perhaps?) provide a sentinel value like `repl-overlays =
<DEFAULT>` to include a "suggested default" overlay like this one.

Change-Id: I8eba3934c50fbac8367111103e66c7375b8d134e
2024-09-01 15:30:58 -07:00
Rebecca Turner b7b1b9723f Clarify that diff-hook no longer needs to be an absolute path
See: https://gerrit.lix.systems/c/lix/+/1864
Change-Id: Ic70bfe42b261a83f2cb68b8f102833b739b8e03a
2024-09-01 15:20:09 -07:00
Rebecca Turner 9d8f433246 Expand comment on std::string operator+
Nuts!

Change-Id: Ib5bc0606d7c86e57ef76dd7bcc89dce91bd3d50a
2024-09-01 15:10:31 -07:00
rebecca “wiggles” turnerandGerrit Code Review 02eb07cfd5 Merge changes I5566a985,I88cf53d3 into main
* changes:
  Support relative and `~/` paths in config settings
  Thread `ApplyConfigOptions` through config parsing
2024-09-01 22:06:36 +00:00
eldritch horrors d75df91f74 libstore: add build result to Goal::Finished
it just makes sense to have it too, rather than just the pass/fail
information we keep so far. once we turn goals into something more
promise-shaped it'll also help detangle the current data flow mess

Change-Id: I915cf04d177cad849ea7a5833215d795326f1946
2024-08-30 19:01:30 +02:00
eldritch horrors a385c5935a libstore: rename Goal::Finished::result to exitCode
the more useful type for `result` is BuildResult.

Change-Id: If93d9384e8d686eb63b33320f1d565f9b9afbf3a
2024-08-30 19:01:30 +02:00
eldritch horrors dc0cace604 libstore: remove queryMissing call from Worker
it doesn't have a purpose except cache priming, which is largely
irrelevant by default (since another code path already runs this
exact query). our store implementations do not benefit that much
from this either, and the more bursty load may indeed harm them.

Change-Id: I1cc12f8c21cede42524317736d5987f1e43fc9c9
2024-08-30 19:01:30 +02:00
eldritch horrors e0fd0ba211 libstore: use notifications for stats counters
updating statistics *immediately* when any counter changes declutters
things somewhat and makes useful status reports less dependent on the
current worker main loop. using callbacks will make it easier to move
the worker loop into kj entirely, using only promises for scheduling.

Change-Id: I695dfa83111b1ec09b1a54cff268f3c1d7743ed6
2024-08-30 19:01:30 +02:00
eldritch horrors c2b90d235f libstore: don't ContinueImmediately where we can tail call
there's no reason to go through the event loop in these cases. returning
ContinueImmediately here is just a very convoluted way of jumping to the
state we've just set after unwinding one frame of the stack, which never
matters in the cases changed here because there are no live RAII guards.

Change-Id: I7c00948c22e3caf35e934c1a14ffd2d40efc5547
2024-08-30 19:01:30 +02:00
eldritch horrors e55ec75619 libstore: print dependency errors from DerivationGoal
this is not ideal, but it's better than having this stuck in the worker
loop itself. setting ex on all failing goals is not problematic because
only toplevel goals can ever be observable, all the others are ignored.
notably only derivation goals ever set `ex`, substitution goals do not.

Change-Id: I02e2164487b2955df053fef3c8e774d557aa638a
2024-08-30 11:13:07 +00:00
eldritch horrors 869666cb65 libstore: hide Worker goal factory methods
this doesn't serve a great purpose yet except to confine construction of
goals to the stack frame of Worker::run() and its child frames. we don't
need this yet (and the goal constructors remain fully visible), but in a
future change that fully removes the current worker loop we'll need some
way of knowing which goals are top-level goals without passing the goals
themselves around. once that's possible we can remove visible goals as a
concept and rely on build result futures and a scheduler built upon them

Change-Id: Ia73cdeffcfb9ba1ce9d69b702dc0bc637a4c4ce6
2024-08-30 10:18:28 +00:00
eldritch horrors a5c1e73fa8 libstore: add "is dependency" info to goal
whether goal errors are reported via the `ex` member or just printed to
the log depends on whether the goal is a toplevel goal or a dependency.
if goals are aware of this themselves we can move error printing out of
the worker loop, and since a running worker can only be used by running
goals it's totally sufficient to keep a `Worker::running` flag for this

Change-Id: I6b5cbe6eccee1afa5fde80653c4b968554ddd16f
2024-08-30 10:18:28 +00:00
Jade Lovelace bb161a96cf manual: note that __sandboxProfile allows bypassing the darwin sandbox
(but only if it is set to relaxed. no security hole here.)

Thanks to lilyball for pointing out this omission in the docs.

Change-Id: I2408a943bfe817fe660fe1c8fefef898aaf5f7e9
2024-08-29 17:06:39 -07:00
Jade LovelaceandRebecca Turner a510d17484 build-time: hide boost stacktrace in a .cc file
Saves about 16s of CPU time. Not a lot but not nothing. Feels more like
the principle of the thing.

Change-Id: I0992d4024317c20d6985a7977d5649edfb9f46bb
2024-08-28 09:55:09 -07:00
Jade LovelaceandRebecca Turner 04f8a14833 tree-wide: shuffle headers around for about 30s compile time
This didn't really feel so worth it afterwards, but I did untangle a
bunch of stuff that should not have been tangled.

The general gist of this change is that variant bullshit was causing a
bunch of compile time, and it seems like the only way to deal with
variant induced compile time is to keep variant types out of headers.
Explicit template instantiation seems to do nothing for them.

I also seem to have gotten some back-end time improvement from
explicitly instantiating regex, but I don't know why. There is no
corresponding front-end time improvement from it: regex is still at the
top of the sinners list.

**** Templates that took longest to instantiate:
 15231 ms: std::basic_regex<char>::_M_compile (28 times, avg 543 ms)
 15066 ms: std::__detail::_Compiler<std::regex_traits<char>>::_Compiler (28 times, avg 538 ms)
 12571 ms: std::__detail::_Compiler<std::regex_traits<char>>::_M_disjunction (28 times, avg 448 ms)
 12454 ms: std::__detail::_Compiler<std::regex_traits<char>>::_M_alternative (28 times, avg 444 ms)
 12225 ms: std::__detail::_Compiler<std::regex_traits<char>>::_M_term (28 times, avg 436 ms)
 11363 ms: nlohmann::basic_json<>::parse<const char *> (21 times, avg 541 ms)
 10628 ms: nlohmann::basic_json<>::basic_json (109 times, avg 97 ms)
 10134 ms: std::__detail::_Compiler<std::regex_traits<char>>::_M_atom (28 times, avg 361 ms)

Back-end time before messing with the regex:
**** Function sets that took longest to compile / optimize:
  8076 ms: void boost::io::detail::put<$>(boost::io::detail::put_holder<$> cons... (177 times, avg 45 ms)
  4382 ms: std::_Rb_tree<$>::_M_erase(std::_Rb_tree_node<$>*) (1247 times, avg 3 ms)
  3137 ms: boost::stacktrace::detail::to_string_impl_base<boost::stacktrace::de... (137 times, avg 22 ms)
  2896 ms: void boost::io::detail::mk_str<$>(std::__cxx11::basic_string<$>&, ch... (177 times, avg 16 ms)
  2304 ms: std::_Rb_tree<$>::_M_get_insert_hint_unique_pos(std::_Rb_tree_const_... (210 times, avg 10 ms)
  2116 ms: bool std::__detail::_Compiler<$>::_M_expression_term<$>(std::__detai... (112 times, avg 18 ms)
  2051 ms: std::_Rb_tree_iterator<$> std::_Rb_tree<$>::_M_emplace_hint_unique<$... (244 times, avg 8 ms)
  2037 ms: toml::result<$> toml::detail::sequence<$>::invoke<$>(toml::detail::l... (93 times, avg 21 ms)
  1928 ms: std::__detail::_Compiler<$>::_M_quantifier() (28 times, avg 68 ms)
  1859 ms: nlohmann::json_abi_v3_11_3::detail::serializer<$>::dump(nlohmann::js... (41 times, avg 45 ms)
  1824 ms: std::_Function_handler<$>::_M_manager(std::_Any_data&, std::_Any_dat... (973 times, avg 1 ms)
  1810 ms: std::__detail::_BracketMatcher<$>::_BracketMatcher(std::__detail::_B... (112 times, avg 16 ms)
  1793 ms: nix::fetchers::GitInputScheme::fetch(nix::ref<$>, nix::fetchers::Inp... (1 times, avg 1793 ms)
  1759 ms: std::_Rb_tree<$>::_M_get_insert_unique_pos(std::__cxx11::basic_strin... (281 times, avg 6 ms)
  1722 ms: bool nlohmann::json_abi_v3_11_3::detail::parser<$>::sax_parse_intern... (19 times, avg 90 ms)
  1677 ms: boost::io::basic_altstringbuf<$>::overflow(int) (194 times, avg 8 ms)
  1674 ms: std::__cxx11::basic_string<$>::_M_mutate(unsigned long, unsigned lon... (249 times, avg 6 ms)
  1660 ms: std::_Rb_tree_node<$>* std::_Rb_tree<$>::_M_copy<$>(std::_Rb_tree_no... (304 times, avg 5 ms)
  1599 ms: bool nlohmann::json_abi_v3_11_3::detail::parser<$>::sax_parse_intern... (19 times, avg 84 ms)
  1568 ms: void std::__detail::_Compiler<$>::_M_insert_bracket_matcher<$>(bool) (112 times, avg 14 ms)
  1541 ms: std::__shared_ptr<$>::~__shared_ptr() (531 times, avg 2 ms)
  1539 ms: nlohmann::json_abi_v3_11_3::detail::serializer<$>::dump_escaped(std:... (41 times, avg 37 ms)
  1471 ms: void std::__detail::_Compiler<$>::_M_insert_character_class_matcher<... (112 times, avg 13 ms)

After messing with the regex (notice std::__detail::_Compiler vanishes
here, but I don't know why):

**** Function sets that took longest to compile / optimize:
  8054 ms: void boost::io::detail::put<$>(boost::io::detail::put_holder<$> cons... (177 times, avg 45 ms)
  4313 ms: std::_Rb_tree<$>::_M_erase(std::_Rb_tree_node<$>*) (1217 times, avg 3 ms)
  3259 ms: boost::stacktrace::detail::to_string_impl_base<boost::stacktrace::de... (137 times, avg 23 ms)
  3045 ms: void boost::io::detail::mk_str<$>(std::__cxx11::basic_string<$>&, ch... (177 times, avg 17 ms)
  2314 ms: std::_Rb_tree<$>::_M_get_insert_hint_unique_pos(std::_Rb_tree_const_... (207 times, avg 11 ms)
  1923 ms: std::_Rb_tree_iterator<$> std::_Rb_tree<$>::_M_emplace_hint_unique<$... (216 times, avg 8 ms)
  1817 ms: bool nlohmann::json_abi_v3_11_3::detail::parser<$>::sax_parse_intern... (18 times, avg 100 ms)
  1816 ms: toml::result<$> toml::detail::sequence<$>::invoke<$>(toml::detail::l... (93 times, avg 19 ms)
  1788 ms: nlohmann::json_abi_v3_11_3::detail::serializer<$>::dump(nlohmann::js... (40 times, avg 44 ms)
  1749 ms: std::_Rb_tree<$>::_M_get_insert_unique_pos(std::__cxx11::basic_strin... (278 times, avg 6 ms)
  1724 ms: std::__cxx11::basic_string<$>::_M_mutate(unsigned long, unsigned lon... (248 times, avg 6 ms)
  1697 ms: boost::io::basic_altstringbuf<$>::overflow(int) (194 times, avg 8 ms)
  1684 ms: nix::fetchers::GitInputScheme::fetch(nix::ref<$>, nix::fetchers::Inp... (1 times, avg 1684 ms)
  1680 ms: std::_Rb_tree_node<$>* std::_Rb_tree<$>::_M_copy<$>(std::_Rb_tree_no... (303 times, avg 5 ms)
  1589 ms: bool nlohmann::json_abi_v3_11_3::detail::parser<$>::sax_parse_intern... (18 times, avg 88 ms)
  1483 ms: non-virtual thunk to boost::wrapexcept<$>::~wrapexcept() (181 times, avg 8 ms)
  1447 ms: nlohmann::json_abi_v3_11_3::detail::serializer<$>::dump_escaped(std:... (40 times, avg 36 ms)
  1441 ms: std::__shared_ptr<$>::~__shared_ptr() (496 times, avg 2 ms)
  1420 ms: boost::stacktrace::basic_stacktrace<$>::init(unsigned long, unsigned... (137 times, avg 10 ms)
  1396 ms: boost::basic_format<$>::~basic_format() (194 times, avg 7 ms)
  1290 ms: std::__cxx11::basic_string<$>::_M_replace_cold(char*, unsigned long,... (231 times, avg 5 ms)
  1258 ms: std::vector<$>::~vector() (354 times, avg 3 ms)
  1222 ms: std::__cxx11::basic_string<$>::_M_replace(unsigned long, unsigned lo... (231 times, avg 5 ms)
  1194 ms: std::_Rb_tree<$>::_M_get_insert_hint_unique_pos(std::_Rb_tree_const_... (49 times, avg 24 ms)
  1186 ms: bool tao::pegtl::internal::sor<$>::match<$>(std::integer_sequence<$>... (1 times, avg 1186 ms)
  1149 ms: std::__detail::_Executor<$>::_M_dfs(std::__detail::_Executor<$>::_Ma... (70 times, avg 16 ms)
  1123 ms: toml::detail::sequence<$>::invoke(toml::detail::location&) (69 times, avg 16 ms)
  1110 ms: nlohmann::json_abi_v3_11_3::basic_json<$>::json_value::destroy(nlohm... (55 times, avg 20 ms)
  1079 ms: std::_Function_handler<$>::_M_manager(std::_Any_data&, std::_Any_dat... (541 times, avg 1 ms)
  1033 ms: nlohmann::json_abi_v3_11_3::detail::lexer<$>::scan_number() (20 times, avg 51 ms)

Change-Id: I10af282bcd4fc39c2d3caae3453e599e4639c70b
2024-08-28 09:55:05 -07:00
Jade LovelaceandRebecca Turner e6f2af06e6 clang-tidy: fix the fact that we are not linting headers properly
This, however, took fixing a pile of lints that we predictably missed
because of this bug.

Change-Id: I92c36feb4a03f62bc594c2051c7bd7418d13fb08
2024-08-28 09:52:08 -07:00
Jade LovelaceandRebecca Turner 4d89844207 build: remove about 30 cpu-sec of compile time by explicit instantiation
Apparently the fmt contraption has some extremely popular overloads, and
the boost stuff in there gets built approximately infinite times in
every compilation unit.

Change-Id: Ideba2db7d6bf8559e4d91974bab636f5ed106198
2024-08-28 09:52:05 -07:00
rebecca “wiggles” turnerandGerrit Code Review 422550fd68 Merge "libstore: remove static initializers for Store registrations" into main 2024-08-28 16:43:22 +00:00
jadeandGerrit Code Review 5d31e889d7 Merge "treewide: fix a bunch of lints" into main 2024-08-28 03:40:27 +00:00
Pierre BourdonandRebecca Turner 4f02255c20 libstore: remove static initializers for Store registrations
Ref #359.

Change-Id: Ia45530ddee25fa9fc399ff10738bb0d8bbc8b221
2024-08-26 16:27:31 -07:00
rebecca “wiggles” turnerandGerrit Code Review 0dc486a5bf Merge "Fix comment in getHome" into main 2024-08-26 23:17:04 +00:00
Jade LovelaceandRebecca Turner 0cc285f87b treewide: fix a bunch of lints
Fixes:
- Identifiers starting with _ are prohibited
- Some driveby header dependency cleaning which wound up with doing some
  extra fixups.
- Fucking C style casts, man. C++ made these 1000% worse by letting you
  also do memory corruption with them with references.
  - Remove casts to Expr * where ExprBlackHole is an incomplete type by
    introducing an explicitly-cast eBlackHoleAddr as Expr *.
  - An incredibly illegal cast of the text bytes of the StorePath hash
    into a size_t directly. You can't DO THAT.

    Replaced with actually parsing the hash so we get 100% of the bits
    being entropy, then memcpying the start of the hash. If this shows
    up in a profile we should just make the hash parser faster with a
    lookup table or something sensible like that.
  - This horrendous bit of UB which I thankfully slapped a deprecation
    warning on, built, and it didn't trigger anywhere so it was dead
    code and I just deleted it. But holy crap you *cannot* do that.

    inline void mkString(const Symbol & s)
    {
        mkString(((const std::string &) s).c_str());
    }
- Some wrong lints. Lots of wrong macro lints, one wrong
  suspicious-sizeof lint triggered by the template being instantiated
  with only pointers, but the calculation being correct for both
  pointers and not-pointers.
- Exceptions in destructors strike again. I tried to catch the
  exceptions that might actually happen rather than all the exceptions
  imaginable. We can let the runtime hard-kill it on other exceptions
  imo.

Change-Id: I71761620846cba64d66ee7ca231b20c061e69710
2024-08-26 16:13:03 -07:00
Rebecca Turner ca08f1217d rowan: 0.15.15 -> 0.15.16
This fixes an ambiguous pointer comparison warning.

See: https://github.com/rust-analyzer/rowan/pull/162
Change-Id: Iaac2c8cab0051eb97211893ad547d8dfa8fda560
2024-08-26 11:34:43 -07:00
Rebecca Turner 742303dc3a Add getCwd
It's nice for this to be a separate function and not just inline in
`absPath`.

Prepared as part of cl/1865, though I don't think I actually ended up
using it there.

Change-Id: I24d9d4a984cee0af587010baf04b3939a1c147ec
2024-08-26 11:22:07 -07:00
Rebecca Turner 690f07272e Support relative and ~/ paths in config settings
Change-Id: I5566a9858ba255f4ac5051d1368c7dfb24460f0a
2024-08-25 15:54:22 -07:00
Rebecca Turner 5fc6fcb310 Thread ApplyConfigOptions through config parsing
This makes no changes to logic but makes the `ApplyConfigOptions` value
available to consumers.

Change-Id: I88cf53d38faac8472c556aee55c13d0acbd1e5db
2024-08-25 15:54:22 -07:00
eldritch horrors b6884388a1 add dedicated test for hash mismatch url reporting
the current test relies on derivation build order being deterministic,
which will not be a reasonable expectation for all that long any more.

Change-Id: I9be44a7725185f614a9a4c724045b8b1e6962c03
2024-08-25 22:21:32 +00:00
rebecca “wiggles” turnerandGerrit Code Review 0582999bd1 Merge "Add ApplyConfigOptions" into main 2024-08-25 22:06:45 +00:00
eldritch horrors 398894b856 libstore: make Goal::ex a shared_ptr
this makes WorkResult copyable, and just all around easier to deal with.
in the future we'll need this to let Goal::work() return a promise for a
WorkResult (or even just a Finished) that can be awaited by other goals.

Change-Id: Ic5a1ce04c5a0f8e683bd00a2ed2b77a2e28989c1
2024-08-25 21:21:55 +00:00
eldritch horrors 30a87b4cd5 libstore: remove unused Goal ctor parameter
Change-Id: I9345fe272d6df5bd592621ce2da369fc1cd36d6d
2024-08-25 20:40:19 +00:00
jadeandGerrit Code Review 72f91767a8 Merge "fix: good errors for failures caused by allowSubstitutes" into main 2024-08-25 20:00:58 +00:00
jadeandGerrit Code Review 3bf8819fa2 Merge changes Ief8e8ebc,Id3135db0,If1e76169 into main
* changes:
  libutil: delete unused boost context cruft
  build: remove approximately 400 seconds of CPU time (30%)
  fix: use http proxy for s3 access
2024-08-25 19:59:46 +00:00
Rebecca Turner c300efc0e1 Add ApplyConfigOptions
Change-Id: Ic876bcabd0b68e579bbd30ca1755919df43d4813
2024-08-25 12:18:20 -07:00
eldritch horrors cae260a158 libstore: diagnose local build failure in goal
this should be done where we're actually trying to build something, not
in the main worker loop that shouldn't have to be aware of such details

Change-Id: I07276740c0e2e5591a8ce4828a4bfc705396527e
2024-08-25 19:55:47 +02:00
eldritch horrors 04b591dc1d devShell: can we have debuggers?
macos: we have debuggers at home

(the debuggers at home: entitled little brats)

Change-Id: Iefd4b5880da97846a81d601db05d2b46530a2b58
2024-08-24 21:34:13 +02:00
Jade Lovelace 686120ee4a fix: good errors for failures caused by allowSubstitutes
This caused an absolute saga which I would not like anyone else to have
to experience. Let's put in a laser targeted error message that
diagnoses this exact problem.

Fixes: https://git.lix.systems/lix-project/lix/issues/484
Change-Id: I2a79f04aeb4a1b67c10115e5e39501d958836298
2024-08-23 17:49:15 -07:00
Rebecca Turner fabc9f29b8 Fix comment in getHome
The logic in the comment is the opposite of the truth.

Change-Id: I64add84539209782ffa46431f3db1fb306d90b3f
2024-08-23 15:15:21 -07:00
rebecca “wiggles” turnerandGerrit Code Review c5949bfe31 Merge "libutil/config: unify path setting types" into main 2024-08-23 22:09:11 +00:00
Jade Lovelace 7e677d15a4 libutil: delete unused boost context cruft
This was from before we got rid of the boost coroutines. Now we don't
need any of this code.

Change-Id: Ief8e8ebc184f02f48e30cb253a66b540faa56329
2024-08-23 13:23:33 -07:00
Jade Lovelace af546be205 build: remove approximately 400 seconds of CPU time (30%)
This took parsing time from 1421s or so to 1060s or so. The reason is
entirely nlohmann. All of the stuff below is just Obliterated because it's
built in the PCH instead:

**** Templates that took longest to instantiate:
219051 ms: nlohmann::basic_json<>::parse<const char *> (276 times, avg 793 ms)
169675 ms: nlohmann::basic_json<>::basic_json (1127 times, avg 150 ms)
129416 ms: nlohmann::detail::parser<nlohmann::basic_json<>, nlohmann::detail::i... (276 times, avg 468 ms)
 98155 ms: nlohmann::detail::parser<nlohmann::basic_json<>, nlohmann::detail::i... (276 times, avg 355 ms)
 81322 ms: nlohmann::basic_json<>::json_value::json_value (1405 times, avg 57 ms)
 53531 ms: nlohmann::detail::json_sax_dom_callback_parser<nlohmann::basic_json<... (276 times, avg 193 ms)

clang-only. This brings the clang build time to not far from *half* of
the gcc build time.

Also, clang does not enjoy so much to miscompile coroutines. Maybe we
should just be clang-only.

Change-Id: Id3135db0094e4560830674090e32e6da2c22fcc6
2024-08-23 13:23:33 -07:00
Jade Lovelace 9aacf425dc fix: use http proxy for s3 access
I don't know why the AWS sdk disabled it by default. It would be nice
to have test coverage of the s3 store or proxies, but neither currently
exist.

Fixes: https://git.lix.systems/lix-project/lix/issues/433
Change-Id: If1e76169a3d66dbec2e926af0d0d0eccf983b97b
2024-08-23 13:23:33 -07:00
Rebecca Turner 9845637359 lix-clang-tidy: Require Clang >= 16
Nixpkgs Clang on macOS is 16, not 17.

nix-repl> packages.aarch64-darwin.nix-clangStdenv.stdenv.cc
«derivation /nix/store/ycych9qpim4r42hjkznl8f6zmj0jns45-clang-wrapper-16.0.6.drv»

nix-repl> packages.x86_64-linux.nix-clangStdenv.stdenv.cc
«derivation /nix/store/y48dhgidb2vs230r9ayim14q61xwcdg9-clang-wrapper-17.0.6.drv»

Change-Id: Ib267b8882f80eef4db665fb9df50ae285ea68b2b
2024-08-23 12:17:01 -07:00
jadeandGerrit Code Review 87fd6e0095 Merge "Revert "libexpr: Replace regex engine with boost::regex"" into main 2024-08-22 22:34:10 +00:00
jadeandGerrit Code Review 9896d309cb Revert "libexpr: Replace regex engine with boost::regex"
This reverts commit 447212fa65.

Reason for revert: Regression in eval behaviour bug-compatibility.

Expected behaviour (Nix 2.18.5, macOS and Linux [libstdc++/libc++]):

```
nix-repl> builtins.match "\\.*(.*)" ".keep"
[ "keep" ]

nix-repl> builtins.match "(\\.*)(.*)" ".keep"
[ "." "keep" ]
```

Actual behaviour (boost::regex):

```
nix-repl> builtins.match "\\.*(.*)" ".keep"
[ ".keep" ]

nix-repl> builtins.match "(\\.*)(.*)" ".keep"
[
  "."
  "keep"
]
```

Bug: https://git.lix.systems/lix-project/lix/issues/483
Change-Id: Id462eb8586dcd54856cf095f09b3e3a216955b60
2024-08-22 18:35:11 +00:00
sugar🍬🍭🏳️‍⚧️andGerrit Code Review f2e7f8bab8 Merge "libexpr: Replace regex engine with boost::regex" into main 2024-08-22 07:20:00 +00:00
sugar 447212fa65 libexpr: Replace regex engine with boost::regex
This avoids C++'s standard library regexes, which aren't the same
across platforms, and have many other issues, like using stack
so much that they stack overflow when processing a lot of data.

To avoid backwards and forward compatibility issues, regexes are
processed using a function converting libstdc++ regexes into Boost
regexes, escaping characters that Boost needs to have escaped, and
rejecting features that Boost has and libstdc++ doesn't.

Related context:

- Original failed attempt to use `boost::regex` in CppNix, failed due to
  boost icu dependency being large (disabling ICU is no longer necessary
  because linking ICU requires using a different header file,
  `boost/regex/icu.hpp`): https://github.com/NixOS/nix/pull/3826

- An attempt to use PCRE, rejected due to providing less backwards
  compatibility with `std::regex` than `boost::regex`:
  https://github.com/NixOS/nix/pull/7336

- Second attempt to use `boost::regex`, failed due to `}` regex failing
  to compile (dealt with by writing a wrapper that parses a regular
  expression and escapes `}` characters):
  https://github.com/NixOS/nix/pull/7762

Closes #34. Closes #476.

Change-Id: Ieb0eb9e270a93e4c7eed412ba4f9f96cb00a5fa4
2024-08-22 03:17:55 +02:00
Jade Lovelace 651cc0e5b4 fix: build with meson 1.5 also
nixpkgs delivered us the untimely gift of a meson 1.5 upgrade, which
*does* make our lives easier by allowing us to delete wrap generation
code, but it does so at the cost of renaming all rust crates in such a
way that the wrap logic cannot tolerate the new names on the old meson
version 😭.

It also means that support burden for this is going to be atrocious
until we either give in and vendor meson 1.5 or we make a CI target for
it. Neither seems appealing, though the latter is not super absurd for
ensuring we don't break nixpkgs unstable.

This commit causes meson 1.5 to ignore the .wrap files in subprojects/
entirely (since they have the wrong names lol) and instead use
Cargo.lock, so it now hard-depends on our workspace reshuffling
improvement.

It also deletes the hack that we were using to get the sources of Cargo
deps into meson by using a feature that went unnoticed when this code
was originally written: MESON_PACKAGE_CACHE_DIR:
https://github.com/mesonbuild/meson/blob/8a202de6ec763284cbb7160b9d43d5e7e0703f19/mesonbuild/wrap/wrap.py#L490-L502

Change-Id: I7a28f12fc2812c6ed7537b60bc3025c141a05874
2024-08-21 17:09:10 +00:00
Jade Lovelace dba615098d build: move to a Cargo workspace
This is purely to let Cargo's dependency resolver do stuff for us, we do
not actually intend to build this stuff with Cargo to begin with.

Change-Id: I4c08d55595c7c27b7096375022581e1e34308a87
2024-08-21 17:09:10 +00:00
Alois Wohlschlager e3c289dbe9 libutil/config: unify path setting types
There have been multiple setting types for paths that are supposed to be
canonicalised, depending on whether zero or one, one, or any number of paths is
to be specified. Naturally, they behaved in slightly different ways in the
code. Simplify things by unifying them and removing special behaviour (mainly
the "multiple paths type can coerce to boolean" thing).

Change-Id: I7c1ce95e9c8e1829a866fb37d679e167811e9705
2024-08-21 17:57:23 +02:00
Alois Wohlschlager de552c42cb Stop the logger in legacy commands again
Commit 0dd1d8ca1c included an accidental revert
of 1461e6cdda (actually slightly worse), leading
to the progress bar not being stopped properly when a legacy command was
invoked with `--log-format bar` (or similar options that show a progress bar).
Move the progress bar stopping code to its proper place again to fix this
regression.

Change-Id: I676333da096d5990b717a387924bb988c9b73fab
2024-08-21 17:28:42 +02:00
piegamesandGerrit Code Review e38410799b Merge "libexpr: Soft-deprecate ancient let syntax" into main 2024-08-21 11:22:48 +00:00
piegames 0edfea450b libexpr: Soft-deprecate ancient let syntax
Change-Id: I6802b26f038578870ea1fa1ed298f0c4b1f29c4a
2024-08-21 12:59:03 +02:00
jadeandGerrit Code Review 3cbbe22fab Merge "flake: fix compiler warning" into main 2024-08-21 10:44:01 +00:00
piegames 0a8888d1c7 treewide: Stop using ancient let syntax
Shows for how long these tests have not been touched by anyone …

Change-Id: I3d0c1209a86283ddb012db4e7d45073264fdd0eb
2024-08-21 06:55:52 +00:00
piegames 7210ed1b87 libexpr: Soft-deprecate __overrides
Change-Id: I787e69e1dad6edc5ccdb747b74a9ccd6e8e13bb3
2024-08-21 06:55:52 +00:00
Jade Lovelace c25c43d8c8 flake: fix compiler warning
GCC was complaining, rightfully, about mixed-sign comparisons in there.
I removed some extra sign mixing too.

Change-Id: I949a618c7405c23d4dc3fd17440ea2d7b5c22c9d
2024-08-20 16:13:17 -07:00
Audrey DutcherandGerrit Code Review ac6974777e Merge "tests/functional/restricted: Don't use a process substitution" into main 2024-08-20 22:51:38 +00:00
Jade LovelaceandQyriad 736b5d5913 lix-doc: move under src/
This is required to make more meson stuff easier/possible, and honestly
it *is* now Lix sources anyhow.

Change-Id: Ia6c38fabce9aa5c53768745ee38c5cf344f5c226
2024-08-20 13:38:46 -06:00
Qyriad 95863b258b build: build lix-doc with Meson! 🎉
lix-doc is now built with Meson, with lix-doc's dependencies built as
Meson subprojects, either fetched on demand with .wrap files, or fetched
in advance by Nix with importCargoLock. It even builds statically.

Fixes #256.

Co-authored-by: Lunaphied <lunaphied@lunaphied.me>
Co-authored-by: Jade Lovelace <lix@jade.fyi>

Change-Id: I3a4731ff13278e7117e0316bc0d7169e85f5eb0c
2024-08-20 17:21:13 +00:00
YurekaandGerrit Code Review f1533160aa Merge "libutil: fix conditional for close_range availability" into main 2024-08-20 07:55:01 +00:00
Yureka df49d37b71 libutil: fix conditional for close_range availability
This check is wrong and would cause the close_range() function being called even when it's not available

Change-Id: Ide65b36830e705fe772196c37349873353622761
2024-08-20 08:58:25 +02:00
Audrey Dutcher ae628d4af2 tests/functional/restricted: Don't use a process substitution
The <() process substitution syntax doesn't work for this one testcase
in bash for FreeBSD. The exact reason for this is unknown, possibly to
do with pipe vs file vs fifo EOF behavior. The prior behavior was this
test hanging forever, with no children of the bash process.

Change-Id: I71822a4b9dea6059b34300568256c5b7848109ac
2024-08-19 20:37:51 -07:00
Maximilian Bosch 040e783232 flake: don't refetch unmodified inputs by recursive follows
Closes #460

I managed to trigger the issue by having the following inputs (shortened):

    authentik-nix.url = "github:nix-community/authentik-nix";
    authentik-nix.inputs.poetry2nix.inputs.nixpkgs.follows = "nixpkgs";

When evaluating this using

    nix-eval-jobs --flake .#hydraJobs

I got the following error:

    error: cannot update unlocked flake input 'authentik-nix/poetry2nix' in pure mode

The issue we have here is that `authentik-nix/poetry2nix` was written
into the `overrideMap` which caused Nix to assume it's a new input and
tried to refetch it (#460) or errored out in pure mode
(nix-eval-jobs / Hydra).

The testcase unfortunately only involves checking for the output log
and makes sure that something *is* logged on the first fetch so that
the test doesn't rot when the logging changes since I didn't
manage to trigger the error above with the reproducer from #460. In
fact, I only managed to trigger the `cannot update unlocked flake input`
error in this context with `nix-eval-jobs`.

Change-Id: Ifd00091eec9a0067ed4bb3e5765a15d027328807
2024-08-19 19:57:12 +00:00
eldritch horrors e727dbc3a3 libstore: un-enable_shared_from_this Goal
it's no longer needed for anything, and not even a great idea.

Change-Id: Ia7a59e1e3f9d8f4ad2ac3b054e38485157c210a6
2024-08-19 09:13:44 +00:00
eldritch horrorsandjade b40369942c libstore: make Worker::childStarted private
this can be a proper WorkResult now. childTerminated is unfortunately a
lot more stubborn and won't be made private for quite a while yet. once
we can get rid of the Worker poll loop that *should* be possible though

Change-Id: I2218df202da5cb84e852f6a37e4c20367495b617
2024-08-19 09:13:44 +00:00
eldritch horrors fca523d661 libstore: turn HookReply into a variant type
we'll need this once we want to pass extra information out of accepting
replies, such as fd sets or possibly even async output reader promises.

Change-Id: I5e2f18cdb80b0d2faf3067703cc18bd263329b3f
2024-08-19 09:13:44 +00:00
eldritch horrors 5e9db09761 libstore: downsize hook pipes
don't keep fds open we're not using. currently this does not cause any
problems, but it does increase the size of our fd table needlessly and
in the future, when we have proper async processing, having builderOut
open in the daemon once the hook has been fully started is problematic

Change-Id: I6e7fb773b280b042873103638d3e04272ca1e4fc
2024-08-19 09:13:44 +00:00
eldritch horrors e513cd2beb libstore: run childStarted as late as possible
otherwise we *technically* give away the output fds before we've read them.

Change-Id: I6ad0d6a1bb553ecfcdd7708f50d34142a425374d
2024-08-19 09:13:44 +00:00
eldritch horrors fb8eb539fc libstore: move respect-timeoutiness to goal method
this is useless to do on the face of it, but it'll make it easier to
convert the entire output handling to use async io and promises soon

Change-Id: I2d1eb62c4bbf8f57bd558b9599c08710a389b1a8
2024-08-19 09:13:44 +00:00
jadeandGerrit Code Review 3d14567d0b Merge "doc: fix broken meson deps for various manuals outputs" into main 2024-08-19 04:30:39 +00:00
jadeandGerrit Code Review 925e08b858 Merge "build: limit clang-tidy concurrency and respect NIX_BUILD_CORES" into main 2024-08-19 02:55:48 +00:00
eldritch horrors 5cbca85535 libstore: clarify that build log fd and hook log fd are different
only DerivationGoal can set the hook to anything at all. it always sets
buildOutFD to something that is not related to fromHook in any way, and
mixing the two would have rather dire consequences for log consistency.

Change-Id: Ida86727fd1cd5e1ecd78f07f3bde330a346658a8
2024-08-18 22:44:11 +00:00
Jade Lovelace ecfe9345cf build: limit clang-tidy concurrency and respect NIX_BUILD_CORES
Apparently it was impolite to lint with 128 jobs on our CI machine with
128 threads. Let's fix it.

Change-Id: I9ca7306294c6773c6f233690ba49d45a1da6bf7a
2024-08-18 15:39:05 -07:00
Jade Lovelace 84543b459c doc: fix broken meson deps for various manuals outputs
This is incredibly haunted, but it can happen that you change libutil,
breaking the generation of the .json files, which then does not rebuild
the files. I don't expect they are slow to build, so it does not seem so
bad to just rebuild them every time instead of extracting a list of all
the possible deps.

We want to delete this nonsense anyway and replace it with generated
code.

Change-Id: Ia576d1a3bdee48fbaefbb5ac194354428d179a84
2024-08-18 15:19:15 -07:00
eldritch horrors e2d330aeed libstore: remove DerivationGoal::isReadDesc
all derivation goals need a log fd of some description. let's save this
single fd in a dedicated pointer field for all subclasses so that later
we have just the one spot to change if we turn this into async promises

Change-Id: If223adf90909247363fb823d751cae34d25d0c0b
2024-08-18 22:04:06 +00:00
piegames 007211e7a2 libutil: Optimize feature checks
Instead of doing a linear search on an std::set, we use a bitset enum.

Change-Id: Ide537f6cffdd16d06e59aaeb2e4ac0acb6493421
2024-08-18 16:56:49 +00:00
eldritch horrors 7506d680ac libstore: don't ignore max-build-log-size for ssh-ng
Change-Id: Ieab14662bea6e6f5533325f0e945147be998f9a2
2024-08-18 09:10:05 +00:00
eldritch horrors 38f550708d libstore: add explicit in-build-slot-ness to goals
we don't need to expose information about how busy a Worker is if the
worker can instead tell its work items whether they are in a slot. in
the future we might use this to not start items waiting for a slot if
no slots are currently available, but that requires more preparation.

Change-Id: Ibe01ac536da7e6d6f80520164117c43e772f9bd9
2024-08-18 09:10:05 +00:00
eldritch horrors 176e1058f1 libstore: remove method without definition
Change-Id: I676411752a4b1777045d7211ac1176693f1a3d7d
2024-08-18 09:10:05 +00:00
eldritch horrors 91a74ba82a libstore: remove unused includes in worker code
Change-Id: I6c7fccc4e710e23a22faae2669cb75f2f6da27b4
2024-08-18 09:10:05 +00:00
eldritch horrors b66fd9ff4b libstore: make Worker::removeGoal private
Change-Id: I8583d9ff752f702a10ec52b0330b0d4d4d2614fa
2024-08-18 09:10:05 +00:00
piegames 278fddc317 libexpr: Deprecate URL literals
Closes #437.

Change-Id: I9f67fc965bb4a7e7fd849e5067ac1cb3bab064cd
2024-08-17 20:31:57 +02:00
piegames 49d61b2e4b libexpr: Introduce Deprecated features
They are like experimental features, but opt-in instead of opt-out. They
will allow us to gracefully remove language features. See #437

Change-Id: I9ca04cc48e6926750c4d622c2b229b25cc142c42
2024-08-17 19:47:51 +02:00
piegames 1c080a8239 treewide: Stop using URL literals
They must die

Change-Id: Ibe2b1818b21d98ec1a68836d01d5dad729b8c501
2024-08-17 15:48:10 +00:00
Artemis Tosiniandjade 41a0b08e64 meson: Don't use target_machine
The target_machine variable is meant for the target
of cross compilers. We are not a cross compiler, so
instead reuse our host_machine based checks.

Fixes Linux→FreeBSD cross, since Meson can't figure
out `target_machine.kernel()` in that case.

Fixes: https://git.lix.systems/lix-project/lix/issues/469

Change-Id: Ia46a64c8d507c3b08987a1de1eda171ff5e50df4
2024-08-16 14:24:03 +00:00
Artemis TosiniandGerrit Code Review b016eb0895 Merge "libutil: Add bindPath function from libstore" into main 2024-08-13 19:39:10 +00:00
Jade Lovelace f9a3bf6ccc Update version to 2.92
Change-Id: Ib64d695c50a733e0e739ff193f1ea65ed7cb0a57
2024-08-12 18:06:08 -07:00
Artemis Tosini 3058029fba libutil: Add bindPath function from libstore
bindPath/doBind is a useful function in build that is used in several
parts of LocalDerivationGoal. Moving this function makes it easier to
split LocalDerivationGoal implementation between several files.

Change-Id: Ic5a0768479c153c1aa3ed425f12604b20bbf0f42
2024-07-27 19:40:40 +00:00
1121 changed files with 22927 additions and 16953 deletions
+4
View File
@@ -29,3 +29,7 @@ trim_trailing_whitespace = false
indent_style = space
indent_size = 2
max_line_length = 0
[meson.build]
indent_style = space
indent_size = 2
@@ -2,7 +2,7 @@
name: Missing or incorrect documentation
about: Help us improve the reference manual
title: ''
labels: documentation
labels: docs
assignees: ''
---
@@ -19,10 +19,10 @@ assignees: ''
<!-- make sure this issue is not redundant or obsolete -->
- [ ] checked [latest Lix manual] \([source]\)
- [ ] checked [latest Lix manual] or its [source code]
- [ ] checked [documentation issues] and [recent documentation changes] for possible duplicates
[latest Nix manual]: https://docs.lix.systems/manual/lix/nightly
[source]: https://git.lix.systems/lix-project/lix/src/main/doc/manual/src
[latest Lix manual]: https://docs.lix.systems/manual/lix/nightly
[source code]: https://git.lix.systems/lix-project/lix/src/main/doc/manual/src
[documentation issues]: https://git.lix.systems/lix-project/lix/issues?labels=151&state=all
[recent documentation changes]: https://gerrit.lix.systems/q/p:lix+path:%22%5Edoc/manual/.*%22
+10
View File
@@ -9,6 +9,10 @@ GTAGS
# ccls
/.ccls-cache
# auto-generated compilation database
compile_commands.json
rust-project.json
result
result-*
@@ -29,3 +33,9 @@ buildtime.bin
/.pre-commit-config.yaml
/.nocontribmsg
/release
# Rust build files when using Cargo (not actually supported for building but it spews the files anyway)
/target/
# Python compiled files from the code generators and test suite
*.pyc
+2
View File
@@ -0,0 +1,2 @@
Fiona Behrens <me@kloenk.dev>
Fiona Behrens <me@kloenk.dev> <me@kloenk.de>
+6 -22
View File
@@ -2,12 +2,6 @@
# It is not intended for manual editing.
version = 3
[[package]]
name = "autocfg"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
[[package]]
name = "countme"
version = "3.0.1"
@@ -16,15 +10,15 @@ checksum = "7704b5fdd17b18ae31c4c1da5a2e0305a2bf17b5249300a9ee9ed7b72114c636"
[[package]]
name = "dissimilar"
version = "1.0.7"
version = "1.0.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "86e3bdc80eee6e16b2b6b0f87fbc98c04bee3455e35174c0de1a125d0688c632"
checksum = "59f8e79d1fbf76bdfbde321e902714bf6c49df88a7dda6fc682fc2979226962d"
[[package]]
name = "expect-test"
version = "1.4.1"
version = "1.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "30d9eafeadd538e68fb28016364c9732d78e420b9ff8853fa5e4058861e9f8d3"
checksum = "9e0be0a561335815e06dab7c62e50353134c796e7a6155402a64bcff66b6a5e0"
dependencies = [
"dissimilar",
"once_cell",
@@ -45,15 +39,6 @@ dependencies = [
"rowan",
]
[[package]]
name = "memoffset"
version = "0.9.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "488016bfae457b036d996092f6cb448677611ce4449e970ceaf42695203f218a"
dependencies = [
"autocfg",
]
[[package]]
name = "once_cell"
version = "1.19.0"
@@ -71,13 +56,12 @@ dependencies = [
[[package]]
name = "rowan"
version = "0.15.15"
version = "0.15.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "32a58fa8a7ccff2aec4f39cc45bf5f985cec7125ab271cf681c279fd00192b49"
checksum = "0a542b0253fa46e632d27a1dc5cf7b930de4df8659dc6e720b647fc72147ae3d"
dependencies = [
"countme",
"hashbrown",
"memoffset",
"rustc-hash",
"text-size",
]
+6
View File
@@ -0,0 +1,6 @@
[workspace]
resolver = "2"
members = ["lix/lix-doc"]
[workspace.package]
edition = "2021"
+10 -4
View File
@@ -1,4 +1,5 @@
#!/usr/bin/env bash
#!/usr/bin/env nix-shell
#!nix-shell -i bash -p bash -p hyperfine
set -euo pipefail
shopt -s inherit_errexit
@@ -21,16 +22,21 @@ fi
_exit=""
trap "$_exit" EXIT
# XXX: yes this is very silly. flakes~!!
nix build --impure --expr '(builtins.getFlake "git+file:.").inputs.nixpkgs.outPath' -o bench/nixpkgs
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="--extra-experimental-features 'nix-command flakes'"
flake_args="${flake_args[*]@Q}"
hyperfineArgs=(
--parameter-list BUILD "$(IFS=,; echo "${builds[*]}")"
+10 -21
View File
@@ -32,8 +32,8 @@
hardware = {
enableRedistributableFirmware = true;
cpu.intel.updateMicrocode = true;
opengl.driSupport32Bit = true;
opengl.extraPackages = with pkgs; [
graphics.enable32Bit = true;
graphics.extraPackages = with pkgs; [
vaapiIntel
intel-media-driver
intel-compute-runtime
@@ -93,16 +93,17 @@
i18n = {
defaultLocale = "en_US.UTF-8";
inputMethod.enabled = "ibus";
inputMethod.enable = true;
inputMethod.type = "ibus";
};
services = {
libinput.enable = true;
xserver = {
enable = true;
layout = "us";
xkbVariant = "altgr-intl";
xkbOptions = "ctrl:nocaps";
libinput.enable = true;
xkb.layout = "us";
xkb.variant = "altgr-intl";
xkb.options = "ctrl:nocaps";
wacom.enable = true;
videoDrivers = [ "modesetting" ];
modules = [ pkgs.xf86_input_wacom ];
@@ -121,17 +122,6 @@
'';
};
sound.enable = true;
hardware.pulseaudio = {
enable = true;
package = pkgs.pulseaudioFull;
daemon.config = {
lock-memory = "yes";
realtime-scheduling = "yes";
rlimit-rtprio = "-1";
};
};
programs = {
light.enable = true;
wireshark = {
@@ -146,7 +136,7 @@
fonts.packages = with pkgs; [
font-awesome
noto-fonts
noto-fonts-cjk
noto-fonts-cjk-sans
noto-fonts-emoji
noto-fonts-extra
dejavu_fonts
@@ -239,7 +229,7 @@
file
firefox
fluidsynth
gnome3.adwaita-icon-theme
adwaita-icon-theme
gnuplot
graphviz
helm
@@ -252,7 +242,6 @@
libqalculate
libreoffice
man-pages
nheko
nix-diff
nix-index
nix-output-monitor
+13 -26
View File
@@ -33,32 +33,7 @@ GENERATE_LATEX = NO
# spaces. See also FILE_PATTERNS and EXTENSION_MAPPING
# Note: If this tag is empty the current directory is searched.
# FIXME Make this list more maintainable somehow. We could maybe generate this
# in the Makefile, but we would need to change how `.in` files are preprocessed
# so they can expand variables despite configure variables.
INPUT = \
src/libcmd \
src/libexpr \
src/libexpr/flake \
tests/unit/libexpr \
tests/unit/libexpr/value \
tests/unit/libexpr/test \
tests/unit/libexpr/test/value \
src/libexpr/value \
src/libfetchers \
src/libmain \
src/libstore \
src/libstore/build \
src/libstore/builtins \
tests/unit/libstore \
tests/unit/libstore/test \
src/libutil \
tests/unit/libutil \
tests/unit/libutil/test \
src/nix \
src/nix-env \
src/nix-store
INPUT = @INPUT_PATHS@
# If the MACRO_EXPANSION tag is set to YES, doxygen will expand all macro names
# in the source code. If set to NO, only conditional compilation will be
@@ -97,3 +72,15 @@ EXPAND_AS_DEFINED = \
DECLARE_WORKER_SERIALISER \
DECLARE_SERVE_SERIALISER \
LENGTH_PREFIXED_PROTO_HELPER
# The STRIP_FROM_PATH tag can be used to strip a user-defined part of the path.
# Stripping is only done if one of the specified strings matches the left-hand
# part of the path. The tag can be used to show relative paths in the file list.
# If left blank the directory from which doxygen is run is used as the path to
# strip.
#
# Note that you can specify absolute paths here, but also relative paths, which
# will be relative from the directory where doxygen is started.
# This tag requires that the tag FULL_PATH_NAMES is set to YES.
STRIP_FROM_PATH = "@PROJECT_SOURCE_ROOT@"
+36 -10
View File
@@ -1,3 +1,35 @@
internal_api_sources = [
'lix/libcmd',
'lix/libexpr',
'lix/libexpr/flake',
'tests/unit/libexpr',
'tests/unit/libexpr/value',
'tests/unit/libexpr/test',
'tests/unit/libexpr/test/value',
'lix/libexpr/value',
'lix/libfetchers',
'lix/libmain',
'lix/libstore',
'lix/libstore/build',
'lix/libstore/builtins',
'tests/unit/libstore',
'tests/unit/libstore/test',
'lix/libutil',
'tests/unit/libutil',
'tests/unit/libutil/test',
'lix/nix',
'lix/nix-env',
'lix/nix-store',
]
# We feed Doxygen absolute paths so it can be invoked from any working directory.
internal_api_sources_absolute = []
foreach src : internal_api_sources
internal_api_sources_absolute += '"' + (meson.project_source_root() / src) + '"'
endforeach
internal_api_sources_oneline = ' \\\n '.join(internal_api_sources_absolute)
doxygen_cfg = configure_file(
input : 'doxygen.cfg.in',
output : 'doxygen.cfg',
@@ -5,22 +37,16 @@ doxygen_cfg = configure_file(
'PACKAGE_VERSION': meson.project_version(),
'RAPIDCHECK_HEADERS': rapidcheck_meson.get_variable('includedir'),
'docdir' : meson.current_build_dir(),
'INPUT_PATHS' : internal_api_sources_oneline,
'PROJECT_SOURCE_ROOT' : meson.project_source_root(),
},
)
internal_api_docs = custom_target(
'internal-api-docs',
command : [
bash,
# Meson can you please just give us a `workdir` argument to custom targets...
'-c',
# We have to prefix the doxygen_cfg path with the project build root
# because of the cd in front.
'cd @0@ && @1@ @2@/@INPUT0@'.format(
meson.project_source_root(),
doxygen.full_path(),
meson.project_build_root(),
),
doxygen.full_path(),
'@INPUT0@',
],
input : [
doxygen_cfg,
+35
View File
@@ -40,6 +40,11 @@ artemist:
display_name: Artemis Tosini
forgejo: artemist
bb010g:
display_name: Dusk Banks
forgejo: bb010g
github: bb010g
cole-h:
display_name: Cole Helbling
github: cole-h
@@ -57,6 +62,9 @@ ericson:
display_name: John Ericson
github: ericson2314
gilice:
forgejo: gilice
goldstein:
display_name: goldstein
forgejo: goldstein
@@ -70,6 +78,10 @@ horrors:
iFreilicht:
github: iFreilicht
ian-h-chamberlain:
forgejo: ian-h-chamberlain
github: ian-h-chamberlain
isabelroses:
forgejo: isabelroses
github: isabelroses
@@ -78,13 +90,28 @@ jade:
forgejo: jade
github: lf-
just1602:
forgejo: just1602
kiara:
github: KiaraGrouwstra
kjeremy:
github: kjeremy
kloenk:
display_name: Fiona Behrens
forgejo: kloenk
github: kloenk
lheckemann:
forgejo: lheckemann
github: lheckemann
lilyball:
forgejo: lilyball
github: lilyball
lovesegfault:
github: lovesegfault
@@ -141,9 +168,17 @@ valentin:
display_name: Valentin Gagarin
github: fricklerhandwerk
vigress8:
display_name: Vigress
forgejo: vigress8
github: vigress8
winter:
forgejo: winter
github: winterqt
yshui:
github: yshui
zimbatm:
github: zimbatm
-37
View File
@@ -1,37 +0,0 @@
let
inherit (builtins) concatStringsSep attrValues mapAttrs;
inherit (import ./utils.nix) optionalString squash;
in
builtinsInfo:
let
showBuiltin =
name:
{
doc,
type,
impure-only,
}:
let
type' = optionalString (type != null) " (${type})";
impureNotice = optionalString impure-only ''
> **Note**
>
> Not available in [pure evaluation mode](@docroot@/command-ref/conf-file.md#conf-pure-eval).
'';
in
squash ''
<dt id="builtins-${name}">
<a href="#builtins-${name}"><code>${name}</code></a>${type'}
</dt>
<dd>
${doc}
${impureNotice}
</dd>
'';
in
concatStringsSep "\n" (attrValues (mapAttrs showBuiltin builtinsInfo))
-35
View File
@@ -1,35 +0,0 @@
let
inherit (builtins) concatStringsSep attrValues mapAttrs;
inherit (import ./utils.nix) optionalString squash;
in
builtinsInfo:
let
showBuiltin =
name:
{
doc,
args,
arity,
experimental-feature,
}:
let
experimentalNotice = optionalString (experimental-feature != null) ''
This function is only available if the [${experimental-feature}](@docroot@/contributing/experimental-features.md#xp-feature-${experimental-feature}) experimental feature is enabled.
'';
in
squash ''
<dt id="builtins-${name}">
<a href="#builtins-${name}"><code>${name} ${listArgs args}</code></a>
</dt>
<dd>
${doc}
${experimentalNotice}
</dd>
'';
listArgs = args: concatStringsSep " " (map (s: "<var>${s}</var>") args);
in
concatStringsSep "\n" (attrValues (mapAttrs showBuiltin builtinsInfo))
+130 -22
View File
@@ -1,26 +1,134 @@
with builtins;
let
inherit (builtins)
attrNames
attrValues
fromJSON
listToAttrs
mapAttrs
concatStringsSep
concatMap
length
lessThan
replaceStrings
sort
;
inherit (import ./utils.nix)
concatStrings
optionalString
filterAttrs
trim
squash
unique
showSettings
;
splitLines = s: filter (x: !isList x) (split "\n" s);
concatStrings = concatStringsSep "";
replaceStringsRec =
from: to: string:
# recursively replace occurrences of `from` with `to` within `string`
# example:
# replaceStringRec "--" "-" "hello-----world"
# => "hello-world"
let
replaced = replaceStrings [ from ] [ to ] string;
in
if replaced == string then string else replaceStringsRec from to replaced;
squash = replaceStringsRec "\n\n\n" "\n\n";
trim =
string:
# trim trailing spaces and squash non-leading spaces
let
trimLine =
line:
let
# separate leading spaces from the rest
parts = split "(^ *)" line;
spaces = head (elemAt parts 1);
rest = elemAt parts 2;
# drop trailing spaces
body = head (split " *$" rest);
in
spaces + replaceStringsRec " " " " body;
in
concatStringsSep "\n" (map trimLine (splitLines string));
# FIXME: O(n^2)
unique = foldl' (acc: e: if elem e acc then acc else acc ++ [ e ]) [ ];
nameValuePair = name: value: { inherit name value; };
filterAttrs =
pred: set:
listToAttrs (
concatMap (
name:
let
v = set.${name};
in
if pred name v then [ (nameValuePair name v) ] else [ ]
) (attrNames set)
);
optionalString = cond: string: if cond then string else "";
showSetting =
{ inlineHTML }:
name:
{
description,
documentDefault,
defaultValue,
aliases,
value,
experimentalFeature,
}:
let
result = squash ''
- ${
if inlineHTML then ''<span id="conf-${name}">[`${name}`](#conf-${name})</span>'' else ''`${name}`''
}
${indent " " body}
'';
experimentalFeatureNote = optionalString (experimentalFeature != null) ''
> **Warning**
> This setting is part of an
> [experimental feature](@docroot@/contributing/experimental-features.md).
To change this setting, you need to make sure the corresponding experimental feature,
[`${experimentalFeature}`](@docroot@/contributing/experimental-features.md#xp-feature-${experimentalFeature}),
is enabled.
For example, include the following in [`nix.conf`](#):
```
extra-experimental-features = ${experimentalFeature}
${name} = ...
```
'';
# separate body to cleanly handle indentation
body = ''
${description}
${experimentalFeatureNote}
**Default:** ${showDefault documentDefault defaultValue}
${showAliases aliases}
'';
showDefault =
documentDefault: defaultValue:
if documentDefault then
# a StringMap value type is specified as a string, but
# this shows the value type. The empty stringmap is `null` in
# JSON, but that converts to `{ }` here.
if defaultValue == "" || defaultValue == [ ] || isAttrs defaultValue then
"*empty*"
else if isBool defaultValue then
if defaultValue then "`true`" else "`false`"
else
"`${toString defaultValue}`"
else
"*machine-specific*";
showAliases =
aliases:
optionalString (aliases != [ ])
"**Deprecated alias:** ${(concatStringsSep ", " (map (s: "`${s}`") aliases))}";
in
result;
indent =
prefix: s: concatStringsSep "\n" (map (x: if x == "" then x else "${prefix}${x}") (splitLines s));
showSettings =
args: settingsInfo: concatStrings (attrValues (mapAttrs (showSetting args) settingsInfo));
in
inlineHTML: commandDump:
@@ -1,9 +0,0 @@
with builtins;
with import ./utils.nix;
let
showExperimentalFeature = name: doc: ''
- [`${name}`](@docroot@/contributing/experimental-features.md#xp-feature-${name})
'';
in
xps: indent " " (concatStrings (attrValues (mapAttrs showExperimentalFeature xps)))
-13
View File
@@ -1,13 +0,0 @@
with builtins;
with import ./utils.nix;
let
showExperimentalFeature =
name: doc:
squash ''
## [`${name}`]{#xp-feature-${name}}
${doc}
'';
in
xps: (concatStringsSep "\n" (attrValues (mapAttrs showExperimentalFeature xps)))
+10 -55
View File
@@ -15,67 +15,21 @@ nix_eval_for_docs_common = nix_for_docs + [
]
nix_eval_for_docs = nix_eval_for_docs_common + '--raw'
conf_file_json = custom_target(
command : nix_for_docs + [ 'show-config', '--json' ],
capture : true,
output : 'conf-file.json',
env : nix_env_for_docs,
)
nix_conf_file_md_body = custom_target(
command : nix_eval_for_docs + [
'--expr',
'(import @INPUT0@).showSettings { inlineHTML = true; } (builtins.fromJSON (builtins.readFile @INPUT1@))',
],
capture : true,
input : [
'utils.nix',
conf_file_json,
],
output : 'conf-file.md.body',
env : nix_env_for_docs,
)
nix_conf_file_md = custom_target(
command : [ 'cat', '@INPUT@' ],
capture : true,
input : [
'src/command-ref/conf-file.md',
nix_conf_file_md_body,
],
output : 'conf-file.md',
)
nix_exp_features_json = custom_target(
command : [ nix, '__dump-xp-features' ],
capture : true,
output : 'xp-features.json',
)
language_json = custom_target(
command: [nix, '__dump-language'],
output : 'language.json',
capture : true,
env : nix_env_for_docs,
)
nix3_cli_json = custom_target(
command : [ nix, '__dump-cli' ],
capture : true,
output : 'nix.json',
env : nix_env_for_docs,
# FIXME: put the actual lib targets in here? meson have introspection challenge 2024 though.
build_always_stale : true,
)
generate_manual_deps = files(
'generate-deps.py',
)
# Generates builtins.md and builtin-constants.md.
subdir('src/language')
# Generates new-cli pages, experimental-features-shortlist.md, and conf-file.md.
# Generates new-cli pages and conf-file.md.
subdir('src/command-ref')
# Generates experimental-feature-descriptions.md.
subdir('src/contributing')
# Generates rl-next-generated.md.
subdir('src/release-notes')
@@ -106,6 +60,8 @@ manual = custom_target(
nix3_cli_files,
experimental_features_shortlist_md,
experimental_feature_descriptions_md,
deprecated_features_shortlist_md,
deprecated_feature_descriptions_md,
conf_file_md,
builtins_md,
builtin_constants_md,
@@ -116,20 +72,19 @@ manual = custom_target(
'manual',
'markdown',
],
install : true,
install_dir : [
datadir / 'doc/nix',
false,
],
depfile : 'manual.d',
env : {
'RUST_LOG': 'info',
'MDBOOK_SUBSTITUTE_SEARCH': meson.current_build_dir() / 'src',
},
)
manual_html = manual[0]
manual_md = manual[1]
install_subdir(
manual_html.full_path(),
install_dir : datadir / 'doc/nix',
)
nix_nested_manpages = [
[ 'nix-env',
[
+2
View File
@@ -192,11 +192,13 @@
- [Hacking](contributing/hacking.md)
- [Testing](contributing/testing.md)
- [Experimental Features](contributing/experimental-features.md)
- [Deprecated Features](contributing/deprecated-features.md)
- [CLI guideline](contributing/cli-guideline.md)
- [C++ style guide](contributing/cxx.md)
- [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.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)
- [Nix 2.18 (2023-09-20)](release-notes/rl-2.18.md)
+7 -1
View File
@@ -34,9 +34,15 @@ keep-outputs = true # Nice for developers
keep-derivations = true # Idem
```
Other files can be included with a line like `include <path>`, where `<path>` is interpreted relative to the current configuration file.
Other files can be included with a line like `include <path>`.
A missing file is an error unless `!include` is used instead.
Paths in `include`s and option values are interpreted relative to the current configuration file.
In user configuration files, paths starting with `~/` are tilde expanded (by replacing the tilde by the value of `$HOME`).
Only user configuration files (like `$XDG_CONFIG_HOME/nix/nix.conf` or the files listed in `$NIX_USER_CONF_FILES`) can use tilde paths relative to your home directory.
Configuration listed in the `$NIX_CONFIG` environment variable may not use relative paths.
A configuration setting usually overrides any previous value.
However, for settings that take a list of items, you can prefix the name of the setting by `extra-` to *append* to the previous value.
+15 -28
View File
@@ -1,23 +1,3 @@
xp_features_json = custom_target(
command : [nix, '__dump-xp-features'],
capture : true,
output : 'xp-features.json',
)
experimental_features_shortlist_md = custom_target(
command : nix_eval_for_docs + [
'--expr',
'import @INPUT0@ (builtins.fromJSON (builtins.readFile @INPUT1@))',
],
input : [
'../../generate-xp-features-shortlist.nix',
xp_features_json,
],
capture : true,
output : 'experimental-features-shortlist.md',
env : nix_env_for_docs,
)
# Intermediate step for manpage generation.
# This splorks the output of generate-manpage.nix as JSON,
# which gets written as a directory tree below.
@@ -51,16 +31,23 @@ nix3_cli_files = custom_target(
conf_file_md = custom_target(
command : [
nix_eval_for_docs,
'--expr',
'(import @INPUT0@).showSettings { inlineHTML = true; } (builtins.fromJSON (builtins.readFile @INPUT1@))',
python.full_path(),
'@SOURCE_ROOT@/lix/code-generation/build_settings.py',
'--kernel', host_machine.system(),
'--docs', '@OUTPUT@',
'--experimental-features', '@SOURCE_ROOT@/lix/libutil/experimental-features',
'@INPUT@',
],
capture : true,
input : [
'../../utils.nix',
conf_file_json,
experimental_features_shortlist_md,
libexpr_setting_definitions,
libfetchers_setting_definitions,
file_transfer_setting_definitions,
libstore_setting_definitions,
archive_setting_definitions,
feature_setting_definitions,
logging_setting_definitions,
daemon_setting_definitions,
develop_settings_definitions,
],
output : 'conf-file.md',
env : nix_env_for_docs,
)
@@ -0,0 +1,37 @@
This section describes the notion of *deprecated features*, and how it fits into the big picture of the development of Lix.
# What are deprecated features?
Deprecated features are disabled by default, with the intent to eventually remove them.
Users must explicitly enable them to keep using them, by toggling the associated [deprecated feature flags](@docroot@/command-ref/conf-file.md#conf-deprecated-features).
This allows backwards compatibility and a graceful transition away from undesired features.
# Which features can be deprecated?
Undesired features should be soft-deprecated by yielding a warning when used for a significant amount of time before the can be deprecated.
Legacy obsolete feature with little to no usage may go through this process faster.
Deprecated features should have a migration path to a preferred alternative.
# Lifecycle of a deprecated feature
This description is not normative, but a feature removal may roughly happen like this:
1. Add a warning when the feature is being used.
2. Disable the feature by default, putting it behind a deprecated feature flag.
- If disabling the feature started out as an opt-in experimental feature, turn that experimental flag into a no-op or remove it entirely.
For example, `--extra-experimental-features=no-url-literals` becomes `--extra-deprecated-features=url-literals`.
3. Decide on a time frame for how long that feature will still be supported for backwards compatibility, and clearly communicate that in the error messages.
- Sometimes, automatic migration to alternatives is possible, and such should be provided if possible
- At least one NixOS release cycle should be the minimum
4. Finally remove the feature entirely, only keeping the error message for those still using it.
# Relation to language versioning
Obviously, removing anything breaks backwards compatibility.
In an ideal world, we'd have SemVer controls over the language and its features, cleanly allowing us to make breaking changes.
See https://wiki.lix.systems/books/lix-contributors/page/language-versioning and [RFC 137](https://github.com/nixos/rfcs/pull/137) for efforts on that.
However, we do not live in such an ideal world, and currently this goal is so far away, that "just disable it with some back-compat for a couple of years" is the most realistic solution, especially for comparatively minor changes.
# Currently available deprecated features
{{#include @generated@/../../../lix/libutil/deprecated-feature-descriptions.md}}
@@ -99,4 +99,4 @@ This means that experimental features and RFCs are orthogonal mechanisms, and ca
# Currently available experimental features
{{#include @generated@/contributing/experimental-feature-descriptions.md}}
{{#include @generated@/../../../lix/libutil/experimental-feature-descriptions.md}}
+76 -3
View File
@@ -102,14 +102,14 @@ $ meson compile -C build nixexpr
All targets may be addressed as their output, relative to the build directory, e.g.:
```bash
$ meson compile -C build src/libexpr/liblixexpr.so
$ meson compile -C build lix/libexpr/liblixexpr.so
```
But Meson does not consider intermediate files like object files targets.
To build a specific object file, use Ninja directly and specify the output file relative to the build directory:
```bash
$ ninja -C build src/libexpr/liblixexpr.so.p/nixexpr.cc.o
$ ninja -C build lix/libexpr/liblixexpr.so.p/nixexpr.cc.o
```
To inspect the canonical source of truth on what the state of the buildsystem configuration is, use:
@@ -137,7 +137,7 @@ 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`
@@ -399,3 +399,76 @@ The following properties are supported:
Releases have a precomputed `rl-MAJOR.MINOR.md`, and no `rl-next.md`.
Set `buildUnreleasedNotes = true;` in `flake.nix` to build the release notes on the fly.
## Adding experimental or deprecated features, global settings, or builtins
Experimental and deprecated features, global settings, and builtins are generally referenced both in the code and in the documentation.
To prevent duplication or divergence, they are defined in data files, and a script generates the necessary glue.
The data file format is similar to the release notes: it consists of a YAML metadata header, followed by the documentation in Markdown format.
### Experimental or deprecated features
Experimental and deprecated features support the following metadata properties:
* `name` (required): user-facing name of the feature, to be used in `nix.conf` options and on the command line.
This should also be the stem of the file name (with extension `md`).
* `internalName` (required): identifier used to refer to the feature inside the C++ code.
Experimental feature data files should live in `lix/libutil/experimental-features`, and deprecated features in `lix/libutil/deprecated-features`.
They must be listed in the `experimental_feature_definitions` or `deprecated_feature_definitions` lists in `lix/libutil/meson.build` respectively to be considered by the build system.
### Global settings
Global settings support the following metadata properties:
* `name` (required): user-facing name of the setting, to be used as key in `nix.conf` and in the `--option` command line argument.
* `internalName` (required): identifier used to refer to the setting inside the C++ code.
* `platforms` (optional): a list specifying the platforms on which this setting is available.
If not specified, it is available on all platforms.
Valid platform names are `darwin`, `linux`.
* `type` (optional): C++ type of the setting value.
This specifies the setting object type as `Setting<T>`; if more control is required, use `settingType` instead.
* `settingType` (required if `type` is not specified): C++ type of the setting object.
* `default` (optional): default value of the setting.
`null`, truth values, integers, strings and lists are supported as long as the correct YAML type is used, `type` is not taken into account).
Other types, machine-dependent values or non-standard representations must be handled using `defaultExpr` and `defaultText` instead.
* `defaultExpr` (required if `default` is not specified): a string containing the C++ expression representing the default value.
* `defaultText` (required if `default` is not specified): a string containing the Markdown expression representing the default value in the documentation.
Literal values are conventionally surrounded by backticks, and a system-dependent value is signaled by `*machine-specific*`.
* `aliases` (optional): a list of secondary user-facing names under which the setting is available.
Defaults to empty if not specified.
* `experimentalFeature` (optional): the user-facing name of the experimental feature which needs to be enabled to change the setting.
If not specified, no experimental feature is required.
* `deprecated` (optional): whether the setting is deprecated and shown as such in the documentation for `nix.conf`.
Defaults to false if not specified.
Settings are not collected in a single place in the source tree, so an appropriate place needs to be found for the setting to live.
Look for related setting definition files under second-level subdirectories of `lix` whose name includes `settings`.
Then add the new file there, and don't forget to register it in the appropriate `meson.build` file.
### Builtin functions
The following metadata properties are supported for builtin functions:
* `name` (required): the language-facing name (as a member of the `builtins` attribute set) of the function.
* `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 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 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`.
### Builtin constants
The following metadata properties are supported for builtin constants:
* `name` (required): the language-facing name (as a member of the `builtins` attribute set) of the constant.
* `type` (required): the Nix language type of the constant; the C++ type is automatically derived.
* `constructorArgs` (optional): list of strings containing C++ expressions passed as arguments to the appropriate `Value` constructor.
If the value computation is more complex, `implementation` can be used instead.
* `implementation` (required if `constructorArgs` is not specified): string containing a C++ expressing computing the value of the constant.
* `impure` (optional): whether the constant is considered impure.
Impure constants are not available when pure evaluation mode is activated.
Defaults to `false` when not specified.
New builtin constant definition files must be added to `lix/libexpr/builtin-constants` and registered in the `builtin_constant_definitions` list in `lix/libexpr/meson.build`.
-15
View File
@@ -1,15 +0,0 @@
# Intermediate step for experimental-feature-descriptions.md.
# This splorks the output of generate-xp-features.nix as JSON,
# which gets written as a directory tree below.
experimental_feature_descriptions_md = custom_target(
command : nix_eval_for_docs + [
'--expr',
'import @INPUT0@ (builtins.fromJSON (builtins.readFile @INPUT1@))',
],
input : [
'../../generate-xp-features.nix',
xp_features_json,
],
capture : true,
output : 'experimental-feature-descriptions.md',
)
+7 -7
View File
@@ -13,7 +13,7 @@ The unit tests are defined using the [googletest] and [rapidcheck] frameworks.
>
> ```
>
> ├── src
> ├── lix
> │   ├── libexpr
> │   │   ├── …
> │   │   ├── value
@@ -46,10 +46,10 @@ The unit tests are defined using the [googletest] and [rapidcheck] frameworks.
> … … … … … …
> ```
The unit tests for each Lix library (`liblixexpr`, `liblixstore`, etc..) live inside a directory `src/${library_shortname}/tests` within the directory for the library (`src/${library_shortname}`).
The unit tests for each Lix library (`liblixexpr`, `liblixstore`, etc..) live inside a directory `lix/${library_shortname}/tests` within the directory for the library (`lix/${library_shortname}`).
The data is in `tests/unit/LIBNAME/data/LIBNAME`, with one subdir per library, with the same name as where the code goes.
For example, `liblixstore` code is in `src/libstore`, and its test data is in `tests/unit/libstore/data/libstore`.
For example, `liblixstore` code is in `lix/libstore`, and its test data is in `tests/unit/libstore/data/libstore`.
The path to the unit test data directory is passed to the unit test executable with the environment variable `_NIX_TEST_UNIT_DATA`.
### Running tests
@@ -345,7 +345,7 @@ rg '(?:[^A-Za-z]|^)(_[A-Z][^-\[ }/:");$(]+)' -r '$1' --no-filename --only-matchi
rg '\$\{?([A-Z][^-\[ }/:");]+)' -r '$1' --no-filename --only-matching tests | sort -u > vars.txt
```
I grepped `src/` for `get[eE]nv\("` to find the mentions in Lix code.
I grepped `lix/` for `get[eE]nv\("` to find the mentions in Lix code.
### Used by Lix testing support code
@@ -361,8 +361,8 @@ I grepped `src/` for `get[eE]nv\("` to find the mentions in Lix code.
- `_NIX_FORCE_HTTP` - Forces file URIs to be treated as remote ones.
Used by `src/libfetchers/git.cc`, `src/libstore/http-binary-cache-store.cc`,
`src/libstore/local-binary-cache-store.cc`. Seems to be for forcing Git
Used by `lix/libfetchers/git.cc`, `lix/libstore/http-binary-cache-store.cc`,
`lix/libstore/local-binary-cache-store.cc`. Seems to be for forcing Git
clones of `git+file://` URLs, making the HTTP binary
cache store accept `file://` URLs (presumably passing them to curl?), and
unknown reasons for the local binary cache.
@@ -374,7 +374,7 @@ I grepped `src/` for `get[eE]nv\("` to find the mentions in Lix code.
`structuredAttrs` documentation.
- `NIX_BIN_DIR`, `NIX_STORE_DIR` (or its inconsistently-used old alias `NIX_STORE`), `NIX_DATA_DIR`,
`NIX_LOG_DIR`, `NIX_LOG_DIR`, `NIX_STATE_DIR`, `NIX_CONF_DIR` -
Overrides compile-time configuration of various locations used by Lix. See `src/libstore/globals.cc`.
Overrides compile-time configuration of various locations used by Lix. See `lix/libstore/globals.cc`.
**Expected value**: a directory
- `NIX_DAEMON_SOCKET_PATH` (optional) - Overrides the daemon socket path from `$NIX_STATE_DIR/daemon-socket/socket`.
@@ -1,6 +1,6 @@
# Installing a Binary Distribution
See https://lix.systems/install/ for more details.
See <https://lix.systems/install/> for more details.
<!--
+4 -1
View File
@@ -36,7 +36,10 @@ All users of the Lix daemon may do the following to bring things into the Nix st
- Input-addressed, so they are run in the sandbox with no network access, with the following exceptions:
- The (poorly named, since it is not *just* about chroot) property `__noChroot` is set on the derivation and `sandbox` is set to `relaxed`.
- On macOS, the derivation property `__darwinAllowLocalNetworking` allows network access to localhost from input-addressed derivations regardless of the `sandbox` setting value. This property exists with such semantics because macOS has no network namespace equivalent to isolate individual processes' localhost networking.
- On macOS, the derivation property `__darwinAllowLocalNetworking` allows network access to localhost from input-addressed derivations regardless of the `sandbox` setting value.
This property exists with such semantics because macOS has no network namespace equivalent to isolate individual processes' localhost networking.
- On macOS, the derivation property `__sandboxProfile` accepts extra sandbox profile S-expressions, allowing derivations to bypass arbitrary parts of the sandbox without altogether disabling it.
This is only permitted when `sandbox` is set to `relaxed`.
- Output-addressed, so they are run with network access but their result must match an expected hash.
Trusted users may set any setting, including `sandbox = false`, so the sandbox state can be different at runtime from what is described in `nix.conf` for builds invoked with such settings.
+1 -1
View File
@@ -4,7 +4,7 @@ These constants are built into the Nix language evaluator:
<dl>
{{#include @generated@/language/builtin-constants.md}}
{{#include @generated@/../../../lix/libexpr/builtin-constants.md}}
</dl>
+1 -1
View File
@@ -15,6 +15,6 @@ For convenience, some built-ins can be accessed directly:
<dd><p><var>derivation</var> is described in
<a href="derivations.md">its own section</a>.</p></dd>
{{#include @generated@/language/builtins.md}}
{{#include @generated@/../../../lix/libexpr/builtins.md}}
</dl>
-27
View File
@@ -1,27 +0,0 @@
builtins_md = custom_target(
command : nix_eval_for_docs + [
'--expr',
'import @INPUT0@ (builtins.fromJSON (builtins.readFile @INPUT1@)).builtins',
],
capture : true,
input : [
'../../generate-builtins.nix',
language_json,
],
output : 'builtins.md',
env : nix_env_for_docs,
)
builtin_constants_md = custom_target(
command : nix_eval_for_docs + [
'--expr',
'import @INPUT0@ (builtins.fromJSON (builtins.readFile @INPUT1@)).constants',
],
capture : true,
input : [
'../../generate-builtin-constants.nix',
language_json,
],
output : 'builtin-constants.md',
env : nix_env_for_docs,
)
+2 -2
View File
@@ -26,8 +26,8 @@
| Logical conjunction (`AND`) | *bool* `&&` *bool* | left | 12 |
| Logical disjunction (`OR`) | *bool* <code>\|\|</code> *bool* | left | 13 |
| [Logical implication] | *bool* `->` *bool* | none | 14 |
| \[Experimental\] [Function piping] | *expr* |> *func* | left | 15 |
| \[Experimental\] [Function piping] | *expr* <| *func* | right | 16 |
| \[Experimental\] [Function piping] | *expr* `\|>` *func* | left | 15 |
| \[Experimental\] [Function piping] | *expr* `<\|` *func* | right | 16 |
[string]: ./values.md#type-string
[path]: ./values.md#type-path
-6
View File
@@ -77,12 +77,6 @@
}
```
Finally, as a convenience, *URIs* as defined in appendix B of
[RFC 2396](http://www.ietf.org/rfc/rfc2396.txt) can be written *as
is*, without quotes. For instance, the string
`"http://example.org/foo.tar.bz2"` can also be written as
`http://example.org/foo.tar.bz2`.
- <a id="type-number" href="#type-number">Number</a>
Numbers, which can be *integers* (like `123`) or *floating point*
-110
View File
@@ -1,114 +1,4 @@
# Lix 2.91 "Dragon's Breath" (2024-08-12)
# Lix 2.91.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/3420](https://gerrit.lix.systems/c/lix/+/3420) [cl/3524](https://gerrit.lix.systems/c/lix/+/3524) [cl/3523](https://gerrit.lix.systems/c/lix/+/3523) [cl/3522](https://gerrit.lix.systems/c/lix/+/3522)
Following the initial mitigation of **CVE-2025-52992** in `cl/3420`, 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**.
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/3502](https://gerrit.lix.systems/c/lix/+/3502)
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.91.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/3418](https://gerrit.lix.systems/c/lix/+/3418)
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/3420](https://gerrit.lix.systems/c/lix/+/3420)
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/3419](https://gerrit.lix.systems/c/lix/+/3419)
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.
# Lix 2.91.1 (2024-10-18)
## Fixes
- `<nix/fetchurl.nix>` now uses TLS verification [#11585](https://github.com/NixOS/nix/pull/11585)
Previously `<nix/fetchurl.nix>` did not do TLS verification. This was because the Nix sandbox in the past did not have access to TLS certificates, and Nix checks the hash of the fetched file anyway. However, this can expose authentication data from `netrc` and URLs to man-in-the-middle attackers. In addition, Nix now in some cases (such as when using impure derivations) does *not* check the hash. Therefore we have now enabled TLS verification. This means that downloads by `<nix/fetchurl.nix>` will now fail if you're fetching from a HTTPS server that does not have a valid certificate.
`<nix/fetchurl.nix>` is also known as the builtin derivation builder `builtin:fetchurl`. It's not to be confused with the evaluation-time function `builtins.fetchurl`, which was not affected by this issue.
Many thanks to [Eelco Dolstra](https://github.com/edolstra) for this.
# Lix 2.91.0 (2024-08-12)
+561
View File
@@ -0,0 +1,561 @@
# 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)
## Breaking Changes
- Deprecated language features [fj#437](https://git.lix.systems/lix-project/lix/issues/437) [#861](https://github.com/NixOS/nix/issues/861) [cl/1785](https://gerrit.lix.systems/c/lix/+/1785) [cl/1736](https://gerrit.lix.systems/c/lix/+/1736) [cl/1735](https://gerrit.lix.systems/c/lix/+/1735) [cl/1744](https://gerrit.lix.systems/c/lix/+/1744) [cl/2206](https://gerrit.lix.systems/c/lix/+/2206)
A system for deprecation (and then the planned removal) of undesired language features has been put into place.
It is controlled via feature flags much like experimental features, except that the deprecations are enabled default,
and can be disabled via the flags for backwards compatibility (opt-out with `--extra-deprecated-features` or the Nix configuration file).
- `url-literals`: **URL literals** have long been obsolete and discouraged of use, and now they are officially deprecated.
This means that all URLs must be properly put within quotes like all other strings.
- `rec-set-overrides`: **__overrides** is an old arcane syntax which has not been in use for more than a decade.
It is soft-deprecated with a warning only, with the plan to turn that into an error in a future release.
- `ancient-let`: **The old `let` syntax** (`let { body = …; … }`) is soft-deprecated with a warning as well. Use the regular `let … in` instead.
- `shadow-internal-symbols`: Arithmetic expressions like `5 - 3` internally expand to `__sub 5 3`, where `__sub` maps to a subtraction builtin. Shadowing such a symbols would affect the evaluation of such operations, but in a very inconsistent way, and is therefore deprecated now. **Affected symbols are:** `__sub`, `__mul`, `__div` and `__lessThan`. Note that these symbols may still be used as variable names as long as they do not shadow internal operations, so e.g. `let __sub = x: y: x + y; in __sub 3 5` remains valid code.
- **Call to action:** If you have any use cases or workflows that depend on being able to override the `__nixPath` and `__findFile` symbols, please reach out to us. We want to eventually deprecate overriding these as well, and need input on how to design a better alternative.
Many thanks to [piegames](https://git.lix.systems/piegames) and [eldritch horrors](https://git.lix.systems/pennae) for this.
- transfers no longer allow arbitrary url schemas [cl/2106](https://gerrit.lix.systems/c/lix/+/2106)
Lix no longer allows transfers using arbitrary url schemas. Only `http://`, `https://`, `ftp://`, `ftps://`, and `file://` urls are supported going forward. This affects `builtins.fetchurl`, `<nix/fetchurl.nix>`, transfers to and from binary caches, and all other uses of the internal file transfer code. Flake inputs using multi-protocol schemas (e.g. `git+ssh`) are not affected as those use external utilities to transfer data.
The `s3://` scheme is not affected at all by this change and continues to work if S3 support is built into Lix.
Many thanks to [eldritch horrors](https://git.lix.systems/pennae) for this.
- Removing the `.` default argument passed to the `nix fmt` formatter [#11438](https://github.com/NixOS/nix/pull/11438) [cl/1902](https://gerrit.lix.systems/c/lix/+/1902)
The underlying formatter no longer receives the ". " default argument when `nix fmt` is called with no arguments.
This change was necessary as the formatter wasn't able to distinguish between
a user wanting to format the current folder with `nix fmt .` or the generic
`nix fmt`.
The default behaviour is now the responsibility of the formatter itself, and
allows tools such as treefmt to format the whole tree instead of only the
current directory and below.
This may cause issues with some formatters: nixfmt, nixpkgs-fmt and alejandra currently format stdin when no arguments are passed.
Here is a small wrapper example that will restore the previous behaviour for such a formatter:
```nix
{
outputs = { self, nixpkgs, systems }:
let
eachSystem = nixpkgs.lib.genAttrs (import systems) (system: nixpkgs.legacyPackages.${system});
in
{
formatter = eachSystem (pkgs:
pkgs.writeShellScriptBin "formatter" ''
if [[ $# = 0 ]]; set -- .; fi
exec "${pkgs.nixfmt-rfc-style}/bin/nixfmt "$@"
'');
};
}
```
Many thanks to [zimbatm](https://github.com/zimbatm) for this.
## Features
- Relative and tilde paths in configuration [fj#482](https://git.lix.systems/lix-project/lix/issues/482) [cl/1851](https://gerrit.lix.systems/c/lix/+/1851) [cl/1863](https://gerrit.lix.systems/c/lix/+/1863) [cl/1864](https://gerrit.lix.systems/c/lix/+/1864)
[Configuration settings](@docroot@/command-ref/conf-file.md) can now refer to
files with paths relative to the file they're written in or relative to your
home directory (with `~/`).
This makes settings like
[`repl-overlays`](@docroot@/command-ref/conf-file.md#conf-repl-overlays) and
[`secret-key-files`](@docroot@/command-ref/conf-file.md#conf-repl-overlays)
much easier to set, especially if you'd like to refer to files in an existing
dotfiles repo cloned into your home directory.
If you put `repl-overlays = repl.nix` in your `~/.config/nix/nix.conf`, it'll
load `~/.config/nix/repl.nix`. Similarly, you can set `repl-overlays =
~/.dotfiles/repl.nix` to load a file relative to your home directory.
Configuration files can also
[`include`](@docroot@/command-ref/conf-file.md#file-format) paths relative to
your home directory.
Only user configuration files (like `$XDG_CONFIG_HOME/nix/nix.conf` or the
files listed in `$NIX_USER_CONF_FILES`) can use tilde paths relative to your
home directory. Configuration listed in the `$NIX_CONFIG` environment variable
may not use relative paths.
Many thanks to [wiggles](https://git.lix.systems/rbt) for this.
## Improvements
- Improved error messages for bad attr paths [cl/2277](https://gerrit.lix.systems/c/lix/+/2277) [cl/2280](https://gerrit.lix.systems/c/lix/+/2280)
Lix now includes much more detail when a bad attribute path is accessed at the command line:
```
» nix eval -f '<nixpkgs>' lixVersions.lix_2_92
error: attribute 'lix_2_92' in selection path 'lixVersions.lix_2_92' not found
Did you mean one of lix_2_90 or lix_2_91?
```
After:
```
» nix eval --impure -f '<nixpkgs>' lixVersions.lix_2_92
error: attribute 'lix_2_92' in selection path 'lixVersions.lix_2_92' not found inside path 'lixVersions', whose contents are: { __unfix__ = «lambda @ /nix/store/hfz1qqd0z8amlgn8qwich1dvkmldik36-source/lib/fixed-points.nix:
447:7»; buildLix = «thunk»; extend = «thunk»; latest = «thunk»; lix_2_90 = «thunk»; lix_2_91 = «thunk»; override = «thunk»; overrideDerivation = «thunk»; recurseForDerivations = true; stable = «thunk»; }
Did you mean one of lix_2_90 or lix_2_91?
```
This should avoid some unnecessary trips to the repl or to the debugger by giving some information about the value being selected on that was unexpected.
Many thanks to [jade](https://git.lix.systems/jade) for this.
- Small error message improvements [cl/2185](https://gerrit.lix.systems/c/lix/+/2185) [cl/2187](https://gerrit.lix.systems/c/lix/+/2187)
When an attribute selection fails, the error message now correctly points to the attribute in the chain that failed instead of at the beginning of the entire chain.
```diff
error: attribute 'x' missing
- at /pwd/lang/eval-fail-remove.nix:4:3:
+ at /pwd/lang/eval-fail-remove.nix:4:29:
3| in
4| (removeAttrs attrs ["x"]).x
- | ^
+ | ^
5|
```
Failed asserts don't print the failed assertion expression anymore in the error message. That code was buggy and the information was redundant anyways, given that the error position already more accurately shows what exactly failed.
Many thanks to [piegames](https://git.lix.systems/piegames) for this.
- Improvements to interactive flake config [cl/2066](https://gerrit.lix.systems/c/lix/+/2066)
If `accept-flake-config` is set to `ask` and a `flake.nix` defines `nixConfig`,
Lix will ask on the CLI which of these settings should be used for the command.
Now, it's possible to answer with `N` (as opposed to `n` to only reject the setting
that is asked for) to reject _all untrusted_ entries from the flake's `nixConf`
section.
Many thanks to [ma27](https://git.lix.systems/ma27) for this.
- `nix --version` now shows details about the installation by default [fj#620](https://git.lix.systems/lix-project/lix/issues/620) [cl/2365](https://gerrit.lix.systems/c/lix/+/2365)
This happened with `nix-env --version` by default, but due to [oddities around the nix3 CLI's verbosity](https://gerrit.lix.systems/c/lix/+/1370), it used to be `nix --verbose --version`.
No longer:
```
$ nix --version
nix (Lix, like Nix) 2.92.0-dev-pre20250117-0d14c2b
System type: x86_64-linux
Additional system types: i686-linux, x86_64-v1-linux, x86_64-v2-linux, x86_64-v3-linux
Features: gc, signed-caches
System configuration file: /etc/nix/nix.conf
User configuration files: /home/jade/.config/nix/nix.conf:/etc/xdg/nix/nix.conf
Store directory: /nix/store
State directory: /nix/var/nix
Data directory: /nix/store/rliimcnqkplrqdgm4z6yqclpr6c32wh6-lix-2.92.0-dev-pre20250117-0d14c2b/share
```
Many thanks to [just1602](https://git.lix.systems/just1602) for this.
- `nix repl` correctly tab-completes attribute names that require quotes [cl/1783](https://gerrit.lix.systems/c/lix/+/1783)
The REPL (`nix repl`) now includes quotes as part of attribute names while completing with `<TAB>`,
if necessary. For example, attribute names like `"hello@example.com"` or `"hello world"` would
be suggested without quotes, resulting in invalid syntax.
Many thanks to [ian-h-chamberlain](https://git.lix.systems/ian-h-chamberlain) for this.
- Reproducibility check builds now report all differing outputs [cl/2069](https://gerrit.lix.systems/c/lix/+/2069)
`nix-build --check` allows rerunning the build of an already-built derivation to check that it produces the same output again.
If a multiple-output derivation with impure behaviour is built with `--check`, only the first output would be shown in the resulting error message (and kept for comparison):
```
error: derivation '/nix/store/4spy3nz1661zm15gkybsy1h5f36aliwx-python3.11-test-1.0.0.drv' may not be deterministic: output '/nix/store/ccqcp01zg18wp9iadzmzimqzdi3ll08d-python3.11-test
-1.0.0-dist' differs from '/nix/store/ccqcp01zg18wp9iadzmzimqzdi3ll08d-python3.11-test-1.0.0-dist.check'
```
Now, all differing outputs are kept and reported:
```
error: derivation '4spy3nz1661zm15gkybsy1h5f36aliwx-python3.11-test-1.0.0.drv' may not be deterministic: outputs differ
output differs: output '/nix/store/ccqcp01zg18wp9iadzmzimqzdi3ll08d-python3.11-test-1.0.0-dist' differs from '/nix/store/ccqcp01zg18wp9iadzmzimqzdi3ll08d-python3.11-test-1.0.0-dist.check'
output differs: output '/nix/store/yl59v08356i841c560alb0zmk7q16klb-python3.11-test-1.0.0' differs from '/nix/store/yl59v08356i841c560alb0zmk7q16klb-python3.11-test-1.0.0.check'
```
Many thanks to [lheckemann](https://git.lix.systems/lheckemann) for this.
- Some Lix crashes now produce reporting instructions and a stack trace, then abort [cl/1854](https://gerrit.lix.systems/c/lix/+/1854)
Lix, being a C++ program, can crash in a few kinds of ways.
It can obviously do a memory access violation, which will generate a core dump and thus be relatively debuggable.
But, worse, it could throw an unhandled exception, and, in the past, we would just show the message but not where it comes from, in spite of this always being a bug, since we expect all such errors to be translated to a Lix specific error.
Now the latter kind of bug should print reporting instructions, a rudimentary stack trace and (depending on system configuration) generate a core dump.
Sample output:
```
Lix crashed. This is a bug. We would appreciate if you report it along with what caused it at https://git.lix.systems/lix-project/lix/issues with the following information included:
Exception: std::runtime_error: test exception
Stack trace:
0# nix::printStackTrace() in /home/jade/lix/lix3/build/lix/nix/../libutil/liblixutil.so
1# 0x000073C9862331F2 in /home/jade/lix/lix3/build/lix/nix/../libmain/liblixmain.so
2# 0x000073C985F2E21A in /nix/store/p44qan69linp3ii0xrviypsw2j4qdcp2-gcc-13.2.0-lib/lib/libstdc++.so.6
3# 0x000073C985F2E285 in /nix/store/p44qan69linp3ii0xrviypsw2j4qdcp2-gcc-13.2.0-lib/lib/libstdc++.so.6
4# nix::handleExceptions(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&, std::function<void ()>) in /home/jade/lix/lix3/build/lix/nix/../libmain/liblixmain.so
...
```
Many thanks to [jade](https://git.lix.systems/jade) for this.
- Add a `temp-dir` setting to set the temporary directory location [#7731](https://github.com/NixOS/nix/issues/7731) [#8995](https://github.com/NixOS/nix/issues/8995) [fj#112](https://git.lix.systems/lix-project/lix/issues/112) [fj#253](https://git.lix.systems/lix-project/lix/issues/253) [cl/2103](https://gerrit.lix.systems/c/lix/+/2103)
[`temp-dir`](@docroot@/command-ref/conf-file.md#conf-temp-dir) can now be set in the Nix
configuration to change the temporary directory. This can be used to relocate all temporary files
to another filesystem without affecting the `TMPDIR` env var inherited by interactive
`nix-shell`/`nix shell` shells or `nix run` commands.
Also on macOS, the `TMPDIR` env var is no longer unset for interactive shells when pointing
to a per-session `/var/folders/` directory.
Many thanks to [lilyball](https://git.lix.systems/lilyball) for this.
## Fixes
- Build failures caused by `allowSubstitutes = false` while being the wrong system now produce a decent error [fj#484](https://git.lix.systems/lix-project/lix/issues/484) [cl/1841](https://gerrit.lix.systems/c/lix/+/1841)
Nix allows derivations to set `allowSubstitutes = false` in order to force them to be built locally without querying substituters for them.
This is useful for derivations that are very fast to build (especially if they produce large output).
However, this can shoot you in the foot if the derivation *has* to be substituted such as if the derivation is for another architecture, which is what `--always-allow-substitutes` is for.
Perhaps such derivations that are known to be impossible to build locally should ignore `allowSubstitutes` (irrespective of remote builders) in the future, but this at least reports the failure and solution directly.
```
$ nix build -f fail.nix
error: a 'unicornsandrainbows-linux' with features {} is required to build '/nix/store/...-meow.drv', but I am a 'x86_64-linux' with features {...}
Hint: the failing derivation has allowSubstitutes set to false, forcing it to be built rather than substituted.
Passing --always-allow-substitutes to force substitution may resolve this failure if the path is available in a substituter.
```
Many thanks to [jade](https://git.lix.systems/jade) for this.
- `Alt+Left` and `Alt+Right` go back/forwards by words in `nix repl` [fj#501](https://git.lix.systems/lix-project/lix/issues/501) [cl/1883](https://gerrit.lix.systems/c/lix/+/1883)
`nix repl` now recognizes `Alt+Left` and `Alt+Right` for navigating by words
when entering input in `nix repl` on more terminals/platforms.
Many thanks to [wiggles](https://git.lix.systems/rbt) for this.
- Ctrl-C stops Nix commands much more reliably and responsively [#7245](https://github.com/NixOS/nix/issues/7245) [fj#393](https://git.lix.systems/lix-project/lix/issues/393) [#11618](https://github.com/NixOS/nix/pull/11618) [cl/2016](https://gerrit.lix.systems/c/lix/+/2016)
CTRL-C will now stop Nix commands much more reliably and responsively. While
there are still some cases where a Nix command can be slow or unresponsive
following a `SIGINT` (please report these as issues!), the vast majority of
signals will now cause the Nix command to quit quickly and consistently.
Many thanks to [Robert Hensing](https://github.com/roberth) and [wiggles](https://git.lix.systems/rbt) for this.
- restore backwards-compatibility of `builtins.fetchGit` with Nix 2.3 [#5291](https://github.com/NixOS/nix/issues/5291) [#5128](https://github.com/NixOS/nix/issues/5128)
Compatibility with `builtins.fetchGit` from Nix 2.3 has been restored as follows:
* Until now, each `ref` was prefixed with `refs/heads` unless it starts with `refs/` itself.
Now, this is not done if the `ref` looks like a commit hash.
* Specifying `builtins.fetchGit { ref = "a-tag"; /* … */ }` was broken because `refs/heads` was appended.
Now, the fetcher doesn't turn a ref into `refs/heads/ref`, but into `refs/*/ref`. That way,
the value in `ref` can be either a tag or a branch.
* The ref resolution happens the same way as in git:
* If `refs/ref` exists, it's used.
* If a tag `refs/tags/ref` exists, it's used.
* If a branch `refs/heads/ref` exists, it's used.
Many thanks to [ma27](https://git.lix.systems/ma27) for this.
- Flakes/restrict-eval no longer allow reading contents of impure paths
Flakes and `--restrict-eval` now correctly restrict access to paths as intended.
In prior versions since at least 2.18, `nix eval --raw .#lol` for the following flake didn't throw an error and acted as if `--impure` was passed.
Thanks to the person who reported this for telling us about it.
This was handled as a low-severity security bug, but is not a violation of the [documented security model](../installation/multi-user.md) as untrusted Nix code should be assumed to have the privileges of the user running the evaluator.
To report a security bug, email a report to `security at lix dot systems`.
```nix
{
inputs = {};
outputs = {...}: {
lol = builtins.readFile "${/etc/passwd}";
};
}
```
Many thanks to [eldritch horrors](https://git.lix.systems/pennae) for this.
- HTTP proxy environment variables are now respected for S3 binary cache stores [fj#433](https://git.lix.systems/lix-project/lix/issues/433) [cl/1788](https://gerrit.lix.systems/c/lix/+/1788)
Due to "legacy reasons" (according to the AWS C++ SDK docs), the AWS SDK ignores system proxy configuration by default.
We turned it back on.
Many thanks to [jade](https://git.lix.systems/jade) for this.
- Fix potential store corruption with auto-optimise-store [#7273](https://github.com/NixOS/nix/issues/7273) [cl/2100](https://gerrit.lix.systems/c/lix/+/2100)
Optimising store paths (and other operations involving temporary files) no longer use `random(3)`
to generate filenames. On darwin systems this was observed to potentially cause store corruption
when using [`auto-optimise-store`](@docroot@/command-ref/conf-file.md#conf-auto-optimise-store),
though this corruption was possible on any system whose `random(3)` does not have locking around
the global state.
Many thanks to [lilyball](https://git.lix.systems/lilyball) for this.
- Change `nix-build -o ""` to behave like `--no-out-link` [cl/2103](https://gerrit.lix.systems/c/lix/+/2103)
[`nix-build`](@docroot@/command-ref/nix-build.md) now treats <code>[--out-link](@docroot@/command-ref/nix-build.md#opt-out-link) ''</code>
the same as [`--no-out-link`](@docroot@/command-ref/nix-build.md#opt-no-out-link). This matches
[`nix build`](@docroot@/command-ref/new-cli/nix3-build.md) behavior. Previously when building the default output it
would have resulted in throwing an error saying the current working directory already exists, and when building any
other output it would have resulted in a symlink starting with a hyphen such as `-doc`, which is a footgun for
terminal commands.
Many thanks to [lilyball](https://git.lix.systems/lilyball) for this.
- Ignore broken `/etc/ssl/certs/ca-certificates.crt` symlink [fj#560](https://git.lix.systems/lix-project/lix/issues/560) [cl/2144](https://gerrit.lix.systems/c/lix/+/2144)
[`ssl-cert-file`](@docroot@/command-ref/conf-file.md#conf-ssl-cert-file) now checks its default
value for a broken symlink before using it. This fixes a problem on macOS where uninstalling
nix-darwin may leave behind a broken symlink at `/etc/ssl/certs/ca-certificates.crt` that was
stopping Lix from using the cert at `/nix/var/nix/profiles/default/etc/ssl/certs/ca-bundle.crt`.
Many thanks to [lilyball](https://git.lix.systems/lilyball) for this.
- `<nix/fetchurl.nix>` now uses TLS verification [#11585](https://github.com/NixOS/nix/pull/11585)
Previously `<nix/fetchurl.nix>` did not do TLS verification. This was because the Nix sandbox in the past did not have access to TLS certificates, and Nix checks the hash of the fetched file anyway. However, this can expose authentication data from `netrc` and URLs to man-in-the-middle attackers. In addition, Nix now in some cases (such as when using impure derivations) does *not* check the hash. Therefore we have now enabled TLS verification. This means that downloads by `<nix/fetchurl.nix>` will now fail if you're fetching from a HTTPS server that does not have a valid certificate.
`<nix/fetchurl.nix>` is also known as the builtin derivation builder `builtin:fetchurl`. It's not to be confused with the evaluation-time function `builtins.fetchurl`, which was not affected by this issue.
Many thanks to [Eelco Dolstra](https://github.com/edolstra) for this.
## Packaging
- readline support removed [cl/1885](https://gerrit.lix.systems/c/lix/+/1885)
Support for building Lix with [`readline`][readline] instead of
[`editline`][editline] has been removed. `readline` support hasn't worked for a
long time (attempting to use it would lead to build errors) and would make Lix
subject to the GPL if it did work. In the future, we're hoping to replace
`editline` with [`rustyline`][rustyline] for improved ergonomics in the `nix
repl`.
[readline]: https://en.wikipedia.org/wiki/GNU_Readline
[editline]: https://github.com/troglobit/editline
[rustyline]: https://github.com/kkawakam/rustyline
Many thanks to [wiggles](https://git.lix.systems/rbt) for this.
## Development
- Includes are now qualified with library name everywhere [cl/2178](https://gerrit.lix.systems/c/lix/+/2178) [cl/2362](https://gerrit.lix.systems/c/lix/+/2362)
The Lix includes have all been rearranged to be of the form `"lix/libexpr/foo.hh"` instead of `"foo.hh"`.
This was already supported externally for a migration period, but it is now being applied to all the internal usages within Lix itself.
The goal of this change is to both clarify where a file is from and to avoid polluting global include paths with things like `config.h` that might conflict with other projects.
Lix 2.92 removes support for the old `"foo.hh"` include form either internally or externally (that is, via pkg-config for things linking to Lix).
For other details, see the release notes of Lix 2.90.0, under "Rename all the libraries" in Breaking Changes.
To fix an external project with sources in `src` which has a separate build directory (such that headers are in `../src` relative to where the compiler is running), use a checkout of Lix 2.91 to run the following:
```
lix_root=$HOME/lix
(cd $lix_root && nix develop -c 'meson setup build && ninja -C build subprojects/lix-clang-tidy/liblix-clang-tidy.so')
run-clang-tidy -checks='-*,lix-fixincludes' -load=$lix_root/build/subprojects/lix-clang-tidy/liblix-clang-tidy.so -p build/ -header-filter '\.\./src/.*\.h' -fix src
```
Many thanks to [jade](https://git.lix.systems/jade) for this.
- The beginnings of a new pytest-based functional test suite [cl/2036](https://gerrit.lix.systems/c/lix/+/2036) [cl/2037](https://gerrit.lix.systems/c/lix/+/2037)
The existing integration/functional test suite is based on a large volume of shell scripts.
This often makes it somewhat challenging to debug at the best of times.
The goal of the pytest test suite is to make tests have more obvious dependencies on files and to make tests more concise and easier to write, as well as making new testing methods like snapshot testing easy.
Many thanks to [jade](https://git.lix.systems/jade) for this.
- Dependency on monolithic coreutils removed [cl/2108](https://gerrit.lix.systems/c/lix/+/2108)
Previously, the build erroneously depended on a `coreutils` binary, which requires `coreutils` to be built with a specific configuration. This was only used in one test and was not required to be a single binary. This dependency is removed now.
Many thanks to [Vigress](https://git.lix.systems/vigress8) for this.
- All Lix threads are named [cl/2210](https://gerrit.lix.systems/c/lix/+/2210)
Lix now sets thread names on all of its secondary threads, which will make debugger usage slightly nicer and easier.
```
(gdb) info thr
Id Target Id Frame
* 1 LWP 3719283 "nix-daemon" 0x00007e558587da0f in accept ()
from target:/nix/store/c10zhkbp6jmyh0xc5kd123ga8yy2p4hk-glibc-2.39-52/lib/libc.so.6
2 LWP 3719284 "signal handler" 0x00007e55857b2bea in sigtimedwait ()
from target:/nix/store/c10zhkbp6jmyh0xc5kd123ga8yy2p4hk-glibc-2.39-52/lib/libc.so.6
```
Many thanks to [jade](https://git.lix.systems/jade) for this.
- Set `X-GitHub-Api-Version` header [fj#255](https://git.lix.systems/lix-project/lix/issues/255) [cl/1925](https://gerrit.lix.systems/c/lix/+/1925)
Sets the `X-GitHub-Api-Version` header to `2022-11-28` for calls to the
GitHub API.
This follows the later version as per
https://docs.github.com/en/rest/about-the-rest-api/api-versions?apiVersion=2022-11-28.
This affected the check on whether to use the API versus unauthenticated
calls as well, given the headers would no longer be empty if the
authentication token were missing.
The workaround to this used here is to use a check similar to an existing
check for the token.
In the current implementation, headers are (still) similarly sent to
non-authenticated as well as GitHub on-prem calls.
For what it's worth, manual curl calls with such a header seemed to
break nor unauthenticated calls nor ones to the github.com API.
Many thanks to [kiara](https://github.com/KiaraGrouwstra) for this.
## Miscellany
- Drop support for `xz` and `bzip2` Content-Encoding [cl/2134](https://gerrit.lix.systems/c/lix/+/2134)
Lix no longer supports the non-standard HTTP Content-Encoding values `xz` and `bzip2`.
We do not expect this to cause any problems in practice since these encodings *aren't*
standard, and any server delivering them anyway without being asked to is already well
and truly set on the path of causing inexplicable client breakages.
Lix's ability to decompress files compressed with `xz` or `bzip2` is unaffected. We're
only bringing Lix more in line with the HTTP standard; all post-transfer data handling
remains as it was before.
Many thanks to [eldritch horrors](https://git.lix.systems/pennae) for this.
-139
View File
@@ -1,139 +0,0 @@
with builtins;
rec {
splitLines = s: filter (x: !isList x) (split "\n" s);
concatStrings = concatStringsSep "";
attrsToList =
a:
map (name: {
inherit name;
value = a.${name};
}) (builtins.attrNames a);
replaceStringsRec =
from: to: string:
# recursively replace occurrences of `from` with `to` within `string`
# example:
# replaceStringRec "--" "-" "hello-----world"
# => "hello-world"
let
replaced = replaceStrings [ from ] [ to ] string;
in
if replaced == string then string else replaceStringsRec from to replaced;
squash = replaceStringsRec "\n\n\n" "\n\n";
trim =
string:
# trim trailing spaces and squash non-leading spaces
let
trimLine =
line:
let
# separate leading spaces from the rest
parts = split "(^ *)" line;
spaces = head (elemAt parts 1);
rest = elemAt parts 2;
# drop trailing spaces
body = head (split " *$" rest);
in
spaces + replaceStringsRec " " " " body;
in
concatStringsSep "\n" (map trimLine (splitLines string));
# FIXME: O(n^2)
unique = foldl' (acc: e: if elem e acc then acc else acc ++ [ e ]) [ ];
nameValuePair = name: value: { inherit name value; };
filterAttrs =
pred: set:
listToAttrs (
concatMap (
name:
let
v = set.${name};
in
if pred name v then [ (nameValuePair name v) ] else [ ]
) (attrNames set)
);
optionalString = cond: string: if cond then string else "";
showSetting =
{ inlineHTML }:
name:
{
description,
documentDefault,
defaultValue,
aliases,
value,
experimentalFeature,
}:
let
result = squash ''
- ${
if inlineHTML then ''<span id="conf-${name}">[`${name}`](#conf-${name})</span>'' else ''`${name}`''
}
${indent " " body}
'';
experimentalFeatureNote = optionalString (experimentalFeature != null) ''
> **Warning**
> This setting is part of an
> [experimental feature](@docroot@/contributing/experimental-features.md).
To change this setting, you need to make sure the corresponding experimental feature,
[`${experimentalFeature}`](@docroot@/contributing/experimental-features.md#xp-feature-${experimentalFeature}),
is enabled.
For example, include the following in [`nix.conf`](#):
```
extra-experimental-features = ${experimentalFeature}
${name} = ...
```
'';
# separate body to cleanly handle indentation
body = ''
${description}
${experimentalFeatureNote}
**Default:** ${showDefault documentDefault defaultValue}
${showAliases aliases}
'';
showDefault =
documentDefault: defaultValue:
if documentDefault then
# a StringMap value type is specified as a string, but
# this shows the value type. The empty stringmap is `null` in
# JSON, but that converts to `{ }` here.
if defaultValue == "" || defaultValue == [ ] || isAttrs defaultValue then
"*empty*"
else if isBool defaultValue then
if defaultValue then "`true`" else "`false`"
else
"`${toString defaultValue}`"
else
"*machine-specific*";
showAliases =
aliases:
optionalString (aliases != [ ])
"**Deprecated alias:** ${(concatStringsSep ", " (map (s: "`${s}`") aliases))}";
in
result;
indent =
prefix: s: concatStringsSep "\n" (map (x: if x == "" then x else "${prefix}${x}") (splitLines s));
showSettings =
args: settingsInfo: concatStrings (attrValues (mapAttrs (showSetting args) settingsInfo));
}
+39 -38
View File
@@ -62,37 +62,38 @@ let
++ autoLayered
++ extraPkgs;
users = {
users =
{
root = {
uid = 0;
shell = "${pkgs.bashInteractive}/bin/bash";
home = "/root";
gid = 0;
groups = [ "root" ];
description = "System administrator";
};
nobody = {
uid = 65534;
shell = "${pkgs.shadow}/bin/nologin";
home = "/var/empty";
gid = 65534;
groups = [ "nobody" ];
description = "Unprivileged account (don't use!)";
};
}
// lib.listToAttrs (
map (n: {
name = "nixbld${toString n}";
value = {
uid = 30000 + n;
gid = 30000;
groups = [ "nixbld" ];
description = "Nix build user ${toString n}";
root = {
uid = 0;
shell = "${pkgs.bashInteractive}/bin/bash";
home = "/root";
gid = 0;
groups = [ "root" ];
description = "System administrator";
};
}) (lib.lists.range 1 32)
);
nobody = {
uid = 65534;
shell = "${pkgs.shadow}/bin/nologin";
home = "/var/empty";
gid = 65534;
groups = [ "nobody" ];
description = "Unprivileged account (don't use!)";
};
}
// lib.listToAttrs (
map (n: {
name = "nixbld${toString n}";
value = {
uid = 30000 + n;
gid = 30000;
groups = [ "nixbld" ];
description = "Nix build user ${toString n}";
};
}) (lib.lists.range 1 32)
);
groups = {
root.gid = 0;
@@ -192,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}";
@@ -358,10 +361,8 @@ 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.";
}
// lib.optionalAttrs (lixRevision != null) { "org.opencontainers.image.revision" = lixRevision; };
"org.opencontainers.image.description" = "Minimal Lix container image, with some batteries included.";
} // lib.optionalAttrs (lixRevision != null) { "org.opencontainers.image.revision" = lixRevision; };
};
meta = {
Generated
+8 -67
View File
@@ -16,30 +16,14 @@
"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": {
"lastModified": 1749158376,
"narHash": "sha256-uirStFNxauh0lxzBowcp28X+Sq7JgsBIDnbwbAfZwf8=",
"lastModified": 1724996935,
"narHash": "sha256-njRK9vvZ1JJsP8oV2OgkBrpJhgQezI03S7gzskCcHos=",
"owner": "nlewo",
"repo": "nix2container",
"rev": "0f8974c58755dba441df03598eefd1e1cd50e341",
"rev": "fa6bb0a1159f55d071ba99331355955ae30b3401",
"type": "github"
},
"original": {
@@ -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": 1757198069,
"narHash": "sha256-m3VUcOD4rTs8J7S+3dOjWMrAjw6RcITC3XYQ98zhEFs=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "0747026fc57ecb9c28901c7f7a2b5dc40e8af43c",
"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"
}
+24 -47
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,
}:
@@ -111,9 +99,10 @@
];
stdenvs = [
"gccStdenv"
# see assertion in package.nix why these two are disabled
# "stdenv"
# "gccStdenv"
"clangStdenv"
"stdenv"
"libcxxStdenv"
"ccacheStdenv"
];
@@ -133,7 +122,11 @@
name = "${stdenvName}Packages";
value = f stdenvName;
}) stdenvs
);
)
// {
# TODO delete this and reënable gcc stdenvs once gcc compiles kj coros correctly
stdenvPackages = f "clangStdenv";
};
# Memoize nixpkgs for different platforms for efficiency.
nixpkgsFor = forAllSystems (
@@ -145,14 +138,7 @@
localSystem = {
inherit system;
};
crossSystem =
if crossSystem == null then
null
else
{
system = crossSystem;
}
// lib.optionalAttrs (crossSystem == "x86_64-freebsd") { useLLVM = true; };
crossSystem = if crossSystem == null then null else { system = crossSystem; };
overlays = [ (overlayFor (p: p.${stdenv})) ];
};
stdenvs = forAllStdenvs (make-pkgs null);
@@ -161,7 +147,7 @@
{
inherit stdenvs native;
static = native.pkgsStatic;
cross = forAllCrossSystems (crossSystem: make-pkgs crossSystem "stdenv");
cross = forAllCrossSystems (crossSystem: make-pkgs crossSystem "clangStdenv");
}
);
@@ -173,16 +159,6 @@
{
nixStable = prev.nix;
nixVersions = prev.nixVersions // {
nix_2_3 = prev.nixVersions.nix_2_3.overrideAttrs (old: {
meta = old.meta // {
knownVulnerabilities = [ ];
};
});
# Nix 2.18 has been removed from Nixpkgs ≥ 25.05, so we need to reintroduce it ourselves for our tests.
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;
@@ -234,7 +210,7 @@
# A Nixpkgs overlay that overrides the 'nix' and
# 'nix.perl-bindings' packages.
overlays.default = overlayFor (p: p.stdenv);
overlays.default = overlayFor (p: p.clangStdenv);
hydraJobs = {
# Binary package for various platforms.
@@ -286,6 +262,8 @@
nix = pkgs.callPackage ./package.nix {
inherit versionSuffix officialRelease buildUnreleasedNotes;
inherit (pkgs) build-release-notes;
# Required since we don't support gcc stdenv
stdenv = pkgs.clangStdenv;
internalApiDocs = true;
busybox-sandbox-shell = pkgs.busybox-sandbox-shell;
};
@@ -343,6 +321,8 @@
inherit (nixpkgs) pkgs;
in
pkgs.callPackage ./package.nix {
# Required since we don't support gcc stdenv
stdenv = pkgs.clangStdenv;
versionSuffix = "";
lintInsteadOfBuild = true;
};
@@ -368,16 +348,13 @@
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" { inherit nix; }).attrpathsSuperset {
evalSystem = system;
})
];
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
];
}
);
};
@@ -509,7 +486,7 @@
let
pkgs = nixpkgsFor.${system}.cross.${crossSystem};
in
makeShell pkgs pkgs.stdenv
makeShell pkgs pkgs.clangStdenv
))
// {
default = self.devShells.${system}.native-clangStdenvPackages;
@@ -0,0 +1,93 @@
from typing import List, NamedTuple
from common import cxx_literal, generate_file, load_data
KNOWN_KEYS = set([
'name',
'type',
'constructorArgs',
'implementation',
'impure',
'renameInGlobalScope',
])
class BuiltinConstant(NamedTuple):
name: str
type: str
implementation: str
impure: bool
rename_in_global_scope: bool
documentation: str
def parse(datum):
unknown_keys = set(datum.keys()) - KNOWN_KEYS
if unknown_keys:
raise Exception('unknown keys', unknown_keys)
return BuiltinConstant(
name = datum['name'],
type = datum['type'],
implementation = ('{' + ', '.join([f'NewValueAs::{datum["type"]}', *datum['constructorArgs']]) + '}') if 'constructorArgs' in datum else datum['implementation'],
impure = datum.get('impure', False),
rename_in_global_scope = datum.get('renameInGlobalScope', True),
documentation = datum.content,
)
VALUE_TYPES = {
'attrs': 'nAttrs',
'boolean': 'nBool',
'integer': 'nInt',
'list': 'nList',
'null': 'nNull',
'string': 'nString',
}
HUMAN_TYPES = {
'attrs': 'set',
'boolean': 'Boolean',
'integer': 'integer',
'list': 'list',
'null': 'null',
'string': 'string',
}
def main():
import argparse
ap = argparse.ArgumentParser()
ap.add_argument('--header', help='Path of the header to generate')
ap.add_argument('--docs', help='Path of the documentation file to generate')
ap.add_argument('defs', help='Builtin definition files', nargs='+')
args = ap.parse_args()
builtin_constants = load_data(args.defs, BuiltinConstant.parse)
generate_file(args.header, builtin_constants, lambda constant:
# `builtins` is magic and must come first
'' if constant.name == 'builtins' else constant.name,
lambda constant:
f'''{'if (!evalSettings.pureEval) ' if constant.impure else ''}{{
addConstant({cxx_literal(('__' if constant.rename_in_global_scope else '') + constant.name)}, {constant.implementation}, {{
.type = {VALUE_TYPES[constant.type]},
.doc = {cxx_literal(constant.documentation)},
.impureOnly = {cxx_literal(constant.impure)},
}});
}}
''')
generate_file(args.docs, builtin_constants, lambda constant: constant.name, lambda constant:
f'''<dt id="builtins-{constant.name}">
<a href="#builtins-{constant.name}"><code>{constant.name}</code></a> ({HUMAN_TYPES[constant.type]})
</dt>
<dd>
{constant.documentation}
''' + ('''> **Note**
>
> Not available in [pure evaluation mode](@docroot@/command-ref/conf-file.md#conf-pure-eval).
''' if constant.impure else '') + '''</dd>
''')
if __name__ == '__main__':
main()
+82
View File
@@ -0,0 +1,82 @@
from typing import List, NamedTuple, Optional
from build_experimental_features import ExperimentalFeature
from common import cxx_literal, generate_file, load_data
KNOWN_KEYS = set([
'name',
'implementation',
'renameInGlobalScope',
'args',
'experimentalFeature',
])
class Builtin(NamedTuple):
name: str
implementation: str
rename_in_global_scope: bool
args: List[str]
experimental_feature: Optional[str]
documentation: str
def parse(datum):
unknown_keys = set(datum.keys()) - KNOWN_KEYS
if unknown_keys:
raise Exception('unknown keys', unknown_keys)
return Builtin(
name = datum['name'],
implementation = datum['implementation'] if 'implementation' in datum else f'prim_{datum["name"]}',
rename_in_global_scope = datum.get('renameInGlobalScope', True),
args = datum['args'],
experimental_feature = datum.get('experimentalFeature', None),
documentation = datum.content,
)
def main():
import argparse
ap = argparse.ArgumentParser()
ap.add_argument('--header', help='Path of the header to generate')
ap.add_argument('--docs', help='Path of the documentation file to generate')
ap.add_argument('--experimental-features', help='Directory containing the experimental feature definitions')
ap.add_argument('defs', help='Builtin definition files', nargs='+')
args = ap.parse_args()
builtins = load_data(args.defs, Builtin.parse)
experimental_feature_names = set([builtin.experimental_feature for (_, builtin) in builtins])
experimental_feature_names.discard(None)
experimental_feature_files = [f'{args.experimental_features}/{name}.md' for name in experimental_feature_names]
experimental_features = load_data(experimental_feature_files, ExperimentalFeature.parse)
experimental_features = dict(map(lambda path_and_feature:
(path_and_feature[1].name, f'Xp::{path_and_feature[1].internal_name}'), experimental_features))
experimental_features[None] = 'std::nullopt'
generate_file(args.header, builtins, lambda builtin: builtin.name, lambda builtin:
f'''{'' if builtin.experimental_feature is None else f'if (experimentalFeatureSettings.isEnabled({experimental_features[builtin.experimental_feature]})) '}{{
addPrimOp({{
.name = {cxx_literal(('__' if builtin.rename_in_global_scope else '') + builtin.name)},
.args = {cxx_literal(builtin.args)},
.arity = {len(builtin.args)},
.doc = {cxx_literal(builtin.documentation)},
.fun = {builtin.implementation},
.experimentalFeature = {experimental_features[builtin.experimental_feature]},
}});
}}
''')
generate_file(args.docs, builtins, lambda builtin: builtin.name, lambda builtin:
f'''<dt id="builtins-{builtin.name}">
<a href="#builtins-{builtin.name}"><code>{builtin.name} {' '.join([f'<var>{arg}</var>' for arg in builtin.args])}</code></a>
</dt>
<dd>
{builtin.documentation}
''' + (f'''This function is only available if the [{builtin.experimental_feature}](@docroot@/contributing/experimental-features.md#xp-feature-{builtin.experimental_feature}) experimental feature is enabled.
''' if builtin.experimental_feature is not None else '') + '''</dd>
''')
if __name__ == '__main__':
main()
@@ -0,0 +1,58 @@
from typing import NamedTuple
from common import cxx_literal, generate_file, load_data
KNOWN_KEYS = set([
'name',
'internalName',
])
class ExperimentalFeature(NamedTuple):
name: str
internal_name: str
description: str
def parse(datum):
unknown_keys = set(datum.keys()) - KNOWN_KEYS
if unknown_keys:
raise ValueError('unknown keys', unknown_keys)
return ExperimentalFeature(
name = datum['name'],
internal_name = datum['internalName'],
description = datum.content,
)
def main():
import argparse
ap = argparse.ArgumentParser()
ap.add_argument('--deprecated', action='store_true', help='Generate deprecated features')
ap.add_argument('--header', help='Path of the declaration header to generate')
ap.add_argument('--impl-header', help='Path of the implementation header to generate')
ap.add_argument('--descriptions', help='Path of the description file to generate')
ap.add_argument('--shortlist', help='Path of the shortlist file to generate')
ap.add_argument('defs', help='Experimental feature definition files', nargs='+')
args = ap.parse_args()
features = load_data(args.defs, ExperimentalFeature.parse)
generate_file(args.header, features, lambda feature: feature.name, lambda feature:
f' {feature.internal_name},\n')
generate_file(args.impl_header, features, lambda feature: feature.name, lambda feature:
f''' {{
.tag = {"Dep" if args.deprecated else "Xp"}::{feature.internal_name},
.name = {cxx_literal(feature.name)},
.description = {cxx_literal(feature.description)},
}},
''')
generate_file(args.descriptions, features, lambda feature: feature.name, lambda feature:
f'''## [`{feature.name}`]{{#{"dp" if args.deprecated else "xp"}-feature-{feature.name}}}
{feature.description}
''')
generate_file(args.shortlist, features, lambda feature: feature.name, lambda feature:
f' - [`{feature.name}`](@docroot@/contributing/{"deprecated" if args.deprecated else "experimental"}-features.md#{"dp" if args.deprecated else "xp"}-feature-{feature.name})\n')
if __name__ == '__main__':
main()
+141
View File
@@ -0,0 +1,141 @@
from typing import List, NamedTuple, Optional
from build_experimental_features import ExperimentalFeature
from common import cxx_literal, generate_file, load_data
KNOWN_KEYS = set([
'name',
'internalName',
'platforms',
'type',
'settingType',
'default',
'defaultExpr',
'defaultText',
'aliases',
'experimentalFeature',
'deprecated',
])
class Setting(NamedTuple):
name: str
internal_name: str
description: str
platforms: Optional[List[str]]
setting_type: str
default_expr: str
default_text: str
aliases: List[str]
experimental_feature: Optional[str]
deprecated: bool
def parse(datum):
unknown_keys = set(datum.keys()) - KNOWN_KEYS
if unknown_keys:
raise ValueError('unknown keys', unknown_keys)
default_text = f'`{nix_conf_literal(datum["default"])}`' if 'default' in datum else datum['defaultText']
if default_text == '``':
default_text = '*empty*'
return Setting(
name = datum['name'],
internal_name = datum['internalName'],
description = datum.content,
platforms = datum.get('platforms', None),
setting_type = f'Setting<{datum["type"]}>' if 'type' in datum else datum['settingType'],
default_expr = cxx_literal(datum['default']) if 'default' in datum else datum['defaultExpr'],
default_text = default_text,
aliases = datum.get('aliases', []),
experimental_feature = datum.get('experimentalFeature', None),
deprecated = datum.get('deprecated', False),
)
platform_names = {
'darwin': 'Darwin',
'linux': 'Linux',
}
def nix_conf_literal(v):
if v is None:
return ''
elif isinstance(v, bool) and v == False: # 0 == False
return 'false'
elif isinstance(v, bool) and v == True: # 1 == True
return 'true'
elif isinstance(v, int):
return str(v)
elif isinstance(v, str):
return v
elif isinstance(v, list):
return ' '.join([nix_conf_literal(item) for item in v])
else:
raise NotImplementedError(f'Cannot represent {repr(v)} in nix.conf')
def indent(prefix, body):
return ''.join(['\n' if line == '' else f'{prefix}{line}\n' for line in body.split('\n')])
def main():
import argparse
ap = argparse.ArgumentParser()
ap.add_argument('--kernel', help='Name of the kernel Lix will run on')
ap.add_argument('--header', help='Path of the header to generate')
ap.add_argument('--docs', help='Path of the documentation file to generate')
ap.add_argument('--experimental-features', help='Directory containing the experimental feature definitions')
ap.add_argument('defs', help='Setting definition files', nargs='+')
args = ap.parse_args()
settings = load_data(args.defs, Setting.parse)
experimental_feature_names = set([setting.experimental_feature for (_, setting) in settings])
experimental_feature_names.discard(None)
experimental_feature_files = [f'{args.experimental_features}/{name}.md' for name in experimental_feature_names]
experimental_features = load_data(experimental_feature_files, ExperimentalFeature.parse)
experimental_features = dict(map(lambda path_and_feature:
(path_and_feature[1].name, f'Xp::{path_and_feature[1].internal_name}'), experimental_features))
experimental_features[None] = 'std::nullopt'
generate_file(args.header, settings, lambda setting: setting.name, lambda setting:
f'''{setting.setting_type} {setting.internal_name} {{
this,
{setting.default_expr},
{cxx_literal(setting.name)},
{cxx_literal(setting.description)},
{cxx_literal(setting.aliases)},
true,
{experimental_features[setting.experimental_feature]},
{cxx_literal(setting.deprecated)}
}};
''' if setting.platforms is None or args.kernel in setting.platforms else '')
generate_file(args.docs, settings, lambda setting: setting.name, lambda setting:
f'''- <span id="conf-{setting.name}">[`{setting.name}`](#conf-{setting.name})</span>
{indent(" ", setting.description)}
''' + (f''' > **Note**
> This setting is only available on {', '.join([platform_names[platform] for platform in setting.platforms])} systems.
''' if setting.platforms is not None else '') + (f''' > **Warning**
> This setting is part of an
> [experimental feature](@docroot@/contributing/experimental-features.md).
To change this setting, you need to make sure the corresponding experimental feature,
[`{setting.experimental_feature}`](@docroot@/contributing/experimental-features.md#xp-feature-{setting.experimental_feature}),
is enabled.
For example, include the following in [`nix.conf`](#):
```
extra-experimental-features = {setting.experimental_feature}
{setting.name} = ...
```
''' if setting.experimental_feature is not None else '') + (''' > **Warning**
> This setting is deprecated and will be removed in a future version of Lix.
''' if setting.deprecated else '') + f''' **Default:** {setting.default_text}
''' + (f''' **Deprecated alias:** {', '.join([f'`{item}`' for item in setting.aliases])}
''' if setting.aliases != [] else ''))
if __name__ == '__main__':
main()
+60
View File
@@ -0,0 +1,60 @@
import frontmatter
import pathlib
from collections import defaultdict
def cxx_escape_character(c):
if ord(c) >= 0x20 and ord(c) < 0x7f and c != '"' and c != '?' and c != '\\':
return c
elif c == '\t':
return r'\t'
elif c == '\n':
return r'\n'
elif c == '\r':
return r'\r'
elif c == '"':
return r'\"'
elif c == '?':
return r'\?'
elif c == '\\':
return r'\\'
elif ord(c) <= 0xffff:
return str.format(r'\u{:04x}', ord(c))
else:
return str.format(r'\U{:08x}', ord(c))
def cxx_literal(v):
if v is None:
return 'std::nullopt'
elif isinstance(v, bool) and v == False: # 0 == False
return 'false'
elif isinstance(v, bool) and v == True: # 1 == True
return 'true'
elif isinstance(v, int):
return str(v)
elif isinstance(v, str):
return ''.join(['"', *(cxx_escape_character(c) for c in v), '"'])
elif isinstance(v, list):
return f'{{{", ".join([cxx_literal(item) for item in v])}}}'
else:
raise NotImplementedError(f'cannot represent {repr(v)} in C++')
def load_data(defs, parse_function):
data = []
for path in defs:
try:
datum = frontmatter.load(path)
data.append((path, parse_function(datum)))
except Exception as e:
e.add_note(f'in {path}')
raise
return data
def generate_file(path, data, sort_key_function, generate_function):
if path is not None:
with open(path, 'w') as out:
for path, datum in sorted(data, key=lambda pathAndDatum: sort_key_function(pathAndDatum[1])):
try:
out.write(generate_function(datum))
except Exception as e:
e.add_note(f'in {path}')
raise
@@ -1,32 +1,28 @@
#include <cstdlib>
#include <cstring>
#include <algorithm>
#include <chrono>
#include <set>
#include <memory>
#include <string_view>
#include <tuple>
#include <iomanip>
#if __APPLE__
#include <sys/time.h>
#endif
#include "machines.hh"
#include "shared.hh"
#include "pathlocks.hh"
#include "globals.hh"
#include "serialise.hh"
#include "build-result.hh"
#include "store-api.hh"
#include "derivations.hh"
#include "local-store.hh"
#include "legacy.hh"
#include "experimental-features.hh"
#include "hash.hh"
#include "lix/libstore/machines.hh"
#include "lix/libmain/shared.hh"
#include "lix/libstore/pathlocks.hh"
#include "lix/libstore/globals.hh"
#include "lix/libutil/serialise.hh"
#include "lix/libstore/build-result.hh"
#include "lix/libstore/store-api.hh"
#include "lix/libstore/derivations.hh"
#include "lix/libutil/strings.hh"
#include "lix/libstore/local-store.hh"
#include "lix/libcmd/legacy.hh"
#include "lix/libutil/experimental-features.hh"
#include "lix/libutil/hash.hh"
#include "build-remote.hh"
using namespace nix;
static void handleAlarm(int sig) {
}
namespace nix {
std::string escapeUri(std::string uri)
{
@@ -53,11 +49,11 @@ static AutoCloseFD openSlotLock(const Machine & m, uint64_t slot)
static bool allSupportedLocally(Store & store, const std::set<std::string>& requiredFeatures) {
for (auto & feature : requiredFeatures)
if (!store.systemFeatures.get().count(feature)) return false;
if (!store.config().systemFeatures.get().count(feature)) return false;
return true;
}
static int main_build_remote(int argc, char * * argv)
static int main_build_remote(std::string programName, Strings argv)
{
{
logger = makeJSONLogger(*logger);
@@ -69,10 +65,10 @@ static int main_build_remote(int argc, char * * argv)
/* If we ever use the common args framework, make sure to
remove initPlugins below and initialize settings first.
*/
if (argc != 2)
if (argv.size() != 1)
throw UsageError("called without required arguments");
verbosity = (Verbosity) std::stoll(argv[1]);
verbosity = (Verbosity) std::stoll(argv.front());
FdSource source(STDIN_FILENO);
@@ -94,7 +90,7 @@ static int main_build_remote(int argc, char * * argv)
that gets cleared on reboot, but it wouldn't work on macOS. */
auto currentLoadName = "/current-load";
if (auto localStore = store.dynamic_pointer_cast<LocalFSStore>())
currentLoad = std::string { localStore->stateDir } + currentLoadName;
currentLoad = std::string { localStore->config().stateDir } + currentLoadName;
else
currentLoad = settings.nixStateDir + currentLoadName;
@@ -139,7 +135,7 @@ static int main_build_remote(int argc, char * * argv)
while (true) {
bestSlotLock.reset();
AutoCloseFD lock = openLockFile(currentLoad + "/main-lock", true);
lockFile(lock.get(), ltWrite, true);
lockFile(lock.get(), ltWrite);
bool rightType = false;
@@ -158,7 +154,7 @@ static int main_build_remote(int argc, char * * argv)
uint64_t load = 0;
for (uint64_t slot = 0; slot < m.maxJobs; ++slot) {
auto slotLock = openSlotLock(m, slot);
if (lockFile(slotLock.get(), ltWrite, false)) {
if (tryLockFile(slotLock.get(), ltWrite)) {
if (!free) {
free = std::move(slotLock);
}
@@ -281,12 +277,8 @@ connected:
{
Activity act(*logger, lvlTalkative, actUnknown, fmt("waiting for the upload lock to '%s'", storeUri));
auto old = signal(SIGALRM, handleAlarm);
alarm(15 * 60);
if (!lockFile(uploadLock.get(), ltWrite, true))
if (!unsafeLockFileSingleThreaded(uploadLock.get(), ltWrite, std::chrono::minutes(15)))
printError("somebody is hogging the upload lock for '%s', continuing...");
alarm(0);
signal(SIGALRM, old);
}
auto substitute = settings.buildersUseSubstitutes ? Substitute : NoSubstitute;
@@ -311,7 +303,7 @@ connected:
});
// See the very large comment in `case WorkerProto::Op::BuildDerivation:` in
// `src/libstore/daemon.cc` that explains the trust model here.
// `lix/libstore/daemon.cc` that explains the trust model here.
//
// This condition mirrors that: that code enforces the "rules" outlined there;
// we do the best we can given those "rules".
@@ -391,4 +383,8 @@ connected:
}
}
static RegisterLegacyCommand r_build_remote("build-remote", main_build_remote);
void registerBuildRemote() {
LegacyCommands::add("build-remote", main_build_remote);
}
}
+8
View File
@@ -0,0 +1,8 @@
#pragma once
/// @file
namespace nix {
void registerBuildRemote();
}
@@ -1,5 +1,5 @@
#include "dotgraph.hh"
#include "store-api.hh"
#include "lix/libstore/store-api.hh"
#include <iostream>
@@ -1,7 +1,7 @@
#pragma once
///@file
#include "store-api.hh"
#include "lix/libstore/store-api.hh"
namespace nix {
@@ -1,6 +1,6 @@
#include "graphml.hh"
#include "store-api.hh"
#include "derivations.hh"
#include "lix/libstore/store-api.hh"
#include "lix/libstore/derivations.hh"
#include <iostream>
@@ -1,7 +1,7 @@
#pragma once
///@file
#include "store-api.hh"
#include "lix/libstore/store-api.hh"
namespace nix {
+35
View File
@@ -0,0 +1,35 @@
legacy_include_directories = include_directories('.')
legacy_sources = files(
# `build-remote` is not really legacy (it powers all remote builds), but it's
# not a `nix3` command.
'build-remote.cc',
'dotgraph.cc',
'graphml.cc',
'nix-build.cc',
'nix-channel.cc',
'nix-collect-garbage.cc',
'nix-copy-closure.cc',
'nix-env.cc',
'nix-env.hh',
'nix-instantiate.cc',
'nix-store.cc',
'user-env.cc',
)
legacy_headers = files(
'build-remote.hh',
'nix-build.hh',
'nix-channel.hh',
'nix-collect-garbage.hh',
'nix-copy-closure.hh',
'nix-instantiate.hh',
'nix-store.hh',
)
legacy_generated_headers = [
gen_header.process('buildenv.nix', preserve_path_from: meson.current_source_dir()),
gen_header.process('unpack-channel.nix', preserve_path_from: meson.current_source_dir()),
]
fs.copyfile('unpack-channel.nix')
@@ -9,31 +9,34 @@
#include <nlohmann/json.hpp>
#include "parsed-derivations.hh"
#include "store-api.hh"
#include "local-fs-store.hh"
#include "globals.hh"
#include "current-process.hh"
#include "derivations.hh"
#include "shared.hh"
#include "path-with-outputs.hh"
#include "eval.hh"
#include "eval-inline.hh"
#include "get-drvs.hh"
#include "common-eval-args.hh"
#include "attr-path.hh"
#include "legacy.hh"
#include "shlex.hh"
#include "lix/libstore/parsed-derivations.hh"
#include "lix/libstore/store-api.hh"
#include "lix/libstore/local-fs-store.hh"
#include "lix/libstore/globals.hh"
#include "lix/libutil/current-process.hh"
#include "lix/libstore/derivations.hh"
#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/shlex.hh"
#include "nix-build.hh"
#include "lix/libstore/temporary-dir.hh"
extern char * * environ __attribute__((weak)); // Man what even is this
namespace nix {
using namespace nix;
using namespace std::string_literals;
extern char * * environ __attribute__((weak));
static void main_nix_build(int argc, char * * argv)
static void main_nix_build(std::string programName, Strings argv)
{
auto dryRun = false;
auto runEnv = std::regex_search(argv[0], std::regex("nix-shell$"));
auto runEnv = std::regex_search(programName, std::regex("nix-shell$"));
auto pure = false;
auto fromArgs = false;
auto packages = false;
@@ -53,8 +56,6 @@ static void main_nix_build(int argc, char * * argv)
std::string script;
std::vector<std::string> savedArgs;
AutoDelete tmpDir(createTempDir("", myName));
std::string outLink = "./result";
// List of environment variables kept for --pure
@@ -65,29 +66,24 @@ static void main_nix_build(int argc, char * * argv)
"http_proxy", "https_proxy", "ftp_proxy", "all_proxy", "no_proxy"
};
Strings args;
for (int i = 1; i < argc; ++i)
args.push_back(argv[i]);
// Heuristic to see if we're invoked as a shebang script, namely,
// if we have at least one argument, it's the name of an
// executable file, and it starts with "#!".
if (runEnv && argc > 1) {
script = argv[1];
if (runEnv && !argv.empty()) {
script = argv.front();
try {
auto lines = tokenizeString<Strings>(readFile(script), "\n");
if (std::regex_search(lines.front(), std::regex("^#!"))) {
lines.pop_front();
inShebang = true;
for (int i = 2; i < argc; ++i)
savedArgs.push_back(argv[i]);
args.clear();
savedArgs = {std::next(argv.begin()), argv.end()};
argv.clear();
for (auto line : lines) {
line = chomp(line);
std::smatch match;
if (std::regex_match(line, match, std::regex("^#!\\s*nix-shell\\s+(.*)$")))
for (const auto & word : shell_split(match[1].str()))
args.push_back(word);
argv.push_back(word);
}
}
} catch (SysError &) { }
@@ -100,7 +96,6 @@ static void main_nix_build(int argc, char * * argv)
MyArgs myArgs(myName, [&](Strings::iterator & arg, const Strings::iterator & end) {
if (*arg == "--help") {
deletePath(tmpDir);
showManPage(myName);
}
@@ -111,7 +106,7 @@ static void main_nix_build(int argc, char * * argv)
; // obsolete
else if (*arg == "--no-out-link" || *arg == "--no-link")
outLink = (Path) tmpDir + "/result";
outLink = "";
else if (*arg == "--attr" || *arg == "-A")
attrPaths.push_back(getArg(*arg, arg, end));
@@ -191,23 +186,28 @@ static void main_nix_build(int argc, char * * argv)
return true;
});
myArgs.parseCmdline(args);
myArgs.parseCmdline(argv);
if (packages && fromArgs)
throw UsageError("'-p' and '-E' are mutually exclusive");
AutoDelete tmpDir(createTempDir("", myName));
if (outLink.empty())
outLink = (Path) tmpDir + "/result";
auto store = openStore();
auto evalStore = myArgs.evalStoreUrl ? openStore(*myArgs.evalStoreUrl) : store;
auto state = std::make_unique<EvalState>(myArgs.searchPath, evalStore, store);
state->repair = myArgs.repair;
auto evaluator = std::make_unique<Evaluator>(myArgs.searchPath, evalStore, store);
evaluator->repair = myArgs.repair;
auto state = evaluator->begin();
if (myArgs.repair) buildMode = bmRepair;
auto autoArgs = myArgs.getAutoArgs(*state);
auto autoArgs = myArgs.getAutoArgs(*evaluator);
auto autoArgsWithInNixShell = autoArgs;
if (runEnv) {
auto newArgs = state->buildBindings(autoArgsWithInNixShell->size() + 1);
auto newArgs = evaluator->buildBindings(autoArgsWithInNixShell->size() + 1);
newArgs.alloc("inNixShell").mkBool(true);
for (auto & i : *autoArgs) newArgs.insert(i);
autoArgsWithInNixShell = newArgs.finish();
@@ -237,11 +237,11 @@ static void main_nix_build(int argc, char * * argv)
std::vector<std::reference_wrapper<Expr>> exprs;
if (readStdin)
exprs = {state->parseStdin()};
exprs = {evaluator->parseStdin()};
else
for (auto i : left) {
if (fromArgs)
exprs.push_back(state->parseExprFromString(std::move(i), state->rootPath(CanonPath::fromCwd())));
exprs.push_back(evaluator->parseExprFromString(std::move(i), CanonPath::fromCwd()));
else {
auto absolute = i;
try {
@@ -249,12 +249,16 @@ static void main_nix_build(int argc, char * * argv)
} catch (Error & e) {};
auto [path, outputNames] = parsePathWithOutputs(absolute);
if (evalStore->isStorePath(path) && path.ends_with(".drv"))
drvs.push_back(DrvInfo(*state, evalStore, absolute));
drvs.push_back(DrvInfo(evalStore, absolute));
else
/* If we're in a #! script, interpret filenames
relative to the script. */
exprs.push_back(state->parseExprFromFile(resolveExprPath(state->checkSourcePath(lookupFileArg(*state,
inShebang && !packages ? absPath(i, absPath(dirOf(script))) : i)))));
exprs.push_back(evaluator->parseExprFromFile(
evaluator->paths.resolveExprPath(lookupFileArg(
*evaluator,
inShebang && !packages ? absPath(i, absPath(dirOf(script))) : i
))
));
}
}
@@ -273,7 +277,7 @@ static void main_nix_build(int argc, char * * argv)
bool add = false;
if (v.type() == nFunction && v.lambda.fun->hasFormals()) {
for (auto & i : v.lambda.fun->formals->formals) {
if (state->symbols[i.name] == "inNixShell") {
if (evaluator->symbols[i.name] == "inNixShell") {
add = true;
break;
}
@@ -301,7 +305,7 @@ static void main_nix_build(int argc, char * * argv)
}
}
state->maybePrintStats();
evaluator->maybePrintStats();
auto buildPaths = [&](const std::vector<DerivedPath> & paths) {
/* Note: we do this even when !printMissing to efficiently
@@ -323,7 +327,7 @@ static void main_nix_build(int argc, char * * argv)
throw UsageError("nix-shell requires a single derivation");
auto & drvInfo = drvs.front();
auto drv = evalStore->derivationFromPath(drvInfo.requireDrvPath());
auto drv = evalStore->derivationFromPath(drvInfo.requireDrvPath(*state));
std::vector<DerivedPath> pathsToBuild;
RealisedPath::Set pathsToCopy;
@@ -337,9 +341,9 @@ static void main_nix_build(int argc, char * * argv)
if (!shell) {
try {
auto & expr = state->parseExprFromString(
auto & expr = evaluator->parseExprFromString(
"(import <nixpkgs> {}).bashInteractive",
state->rootPath(CanonPath::fromCwd()));
CanonPath::fromCwd());
Value v;
state->eval(expr, v);
@@ -348,7 +352,7 @@ static void main_nix_build(int argc, char * * argv)
if (!drv)
throw Error("the 'bashInteractive' attribute in <nixpkgs> did not evaluate to a derivation");
auto bashDrv = drv->requireDrvPath();
auto bashDrv = drv->requireDrvPath(*state);
pathsToBuild.push_back(DerivedPath::Built {
.drvPath = makeConstantStorePathRef(bashDrv),
.outputs = OutputsSpec::Names {"out"},
@@ -414,8 +418,6 @@ static void main_nix_build(int argc, char * * argv)
// Set the environment.
auto env = getEnv();
auto tmp = defaultTempDir();
if (pure) {
decltype(env) newEnv;
for (auto & i : env)
@@ -426,8 +428,9 @@ static void main_nix_build(int argc, char * * argv)
env["__ETC_PROFILE_SOURCED"] = "1";
}
env["NIX_BUILD_TOP"] = env["TMPDIR"] = env["TEMPDIR"] = env["TMP"] = env["TEMP"] = tmp;
env["NIX_STORE"] = store->storeDir;
// Don't use defaultTempDir() here! We want to preserve the user's TMPDIR for the shell
env["NIX_BUILD_TOP"] = env["TMPDIR"] = env["TEMPDIR"] = env["TMP"] = env["TEMP"] = getEnvNonEmpty("TMPDIR").value_or("/tmp");
env["NIX_STORE"] = store->config().storeDir;
env["NIX_BUILD_CORES"] = std::to_string(settings.buildCores);
auto passAsFile = tokenizeString<StringSet>(getOr(drv.env, "passAsFile", ""));
@@ -465,7 +468,7 @@ static void main_nix_build(int argc, char * * argv)
for (const auto & [inputDrv, inputNode] : drv.inputDrvs.map)
accumInputClosure(inputDrv, inputNode);
ParsedDerivation parsedDrv(drvInfo.requireDrvPath(), drv);
ParsedDerivation parsedDrv(drvInfo.requireDrvPath(*state), drv);
if (auto structAttrs = parsedDrv.prepareStructuredAttrs(*store, inputs)) {
auto json = structAttrs.value();
@@ -488,42 +491,51 @@ static void main_nix_build(int argc, char * * argv)
environment variables and shell functions. Also don't
lose the current $PATH directories. */
auto rcfile = (Path) tmpDir + "/rc";
auto tz = getEnv("TZ");
std::string rc = fmt(
R"(_nix_shell_clean_tmpdir() { command rm -rf %1%; }; )"s +
(keepTmp ?
"trap _nix_shell_clean_tmpdir EXIT; "
"exitHooks+=(_nix_shell_clean_tmpdir); "
"failureHooks+=(_nix_shell_clean_tmpdir); ":
"_nix_shell_clean_tmpdir; ") +
(pure ? "" : "[ -n \"$PS1\" ] && [ -e ~/.bashrc ] && source ~/.bashrc;") +
"%2%"
// always clear PATH.
// when nix-shell is run impure, we rehydrate it with the `p=$PATH` above
"unset PATH;"
"dontAddDisableDepTrack=1;\n"
+ structuredAttrsRC +
"\n[ -e $stdenv/setup ] && source $stdenv/setup; "
"%3%"
"PATH=%4%:\"$PATH\"; "
"SHELL=%5%; "
"BASH=%5%; "
"set +e; "
R"s([ -n "$PS1" -a -z "$NIX_SHELL_PRESERVE_PROMPT" ] && )s" +
(getuid() == 0 ? R"s(PS1='\n\[\033[1;31m\][nix-shell:\w]\$\[\033[0m\] '; )s"
: R"s(PS1='\n\[\033[1;32m\][nix-shell:\w]\$\[\033[0m\] '; )s") +
"if [ \"$(type -t runHook)\" = function ]; then runHook shellHook; fi; "
"unset NIX_ENFORCE_PURITY; "
"shopt -u nullglob; "
"unset TZ; %6%"
"shopt -s execfail;"
"%7%",
shellEscape(tmpDir),
(pure ? "" : "p=$PATH; "),
(pure ? "" : "PATH=$PATH:$p; unset p; "),
shellEscape(dirOf(*shell)),
shellEscape(*shell),
(getenv("TZ") ? (std::string("export TZ=") + shellEscape(getenv("TZ")) + "; ") : ""),
envCommand);
R"(_nix_shell_clean_tmpdir() { command rm -rf %1%; }; )"
"%2%"
"%3%"
// always clear PATH.
// when nix-shell is run impure, we rehydrate it with the `p=$PATH` above
"unset PATH;"
"dontAddDisableDepTrack=1;\n",
shellEscape(tmpDir),
(keepTmp
? "trap _nix_shell_clean_tmpdir EXIT; "
"exitHooks+=(_nix_shell_clean_tmpdir); "
"failureHooks+=(_nix_shell_clean_tmpdir); "
: "_nix_shell_clean_tmpdir; "),
(pure
? ""
: "[ -n \"$PS1\" ] && [ -e ~/.bashrc ] && source ~/.bashrc; p=$PATH; ")
);
rc += structuredAttrsRC;
rc += fmt(
"\n[ -e $stdenv/setup ] && source $stdenv/setup; "
"%1%"
"PATH=%2%:\"$PATH\"; "
"SHELL=%3%; "
"BASH=%3%; "
"set +e; "
R"s([ -n "$PS1" -a -z "$NIX_SHELL_PRESERVE_PROMPT" ] && )s"
"%4%"
"if [ \"$(type -t runHook)\" = function ]; then runHook shellHook; fi; "
"unset NIX_ENFORCE_PURITY; "
"shopt -u nullglob; "
"unset TZ; %5%"
"shopt -s execfail;"
"%6%",
(pure ? "" : "PATH=$PATH:$p; unset p; "),
shellEscape(dirOf(*shell)),
shellEscape(*shell),
(getuid() == 0 ? R"s(PS1='\n\[\033[1;31m\][nix-shell:\w]\$\[\033[0m\] '; )s"
: R"s(PS1='\n\[\033[1;32m\][nix-shell:\w]\$\[\033[0m\] '; )s"),
(tz.has_value()
? (std::string("export TZ=") + shellEscape(*tz) + "; ")
: ""),
envCommand
);
vomit("Sourcing nix-shell with file %s and contents:\n%s", rcfile, rc);
writeFile(rcfile, rc);
@@ -559,9 +571,9 @@ static void main_nix_build(int argc, char * * argv)
std::map<StorePath, std::pair<size_t, StringSet>> drvMap;
for (auto & drvInfo : drvs) {
auto drvPath = drvInfo.requireDrvPath();
auto drvPath = drvInfo.requireDrvPath(*state);
auto outputName = drvInfo.queryOutputName();
auto outputName = drvInfo.queryOutputName(*state);
if (outputName == "")
throw Error("derivation '%s' lacks an 'outputName' attribute", store->printStorePath(drvPath));
@@ -613,5 +625,9 @@ static void main_nix_build(int argc, char * * argv)
}
}
static RegisterLegacyCommand r_nix_build("nix-build", main_nix_build);
static RegisterLegacyCommand r_nix_shell("nix-shell", main_nix_build);
void registerNixBuildAndNixShell() {
LegacyCommands::add("nix-build", main_nix_build);
LegacyCommands::add("nix-shell", main_nix_build);
}
}
+8
View File
@@ -0,0 +1,8 @@
#pragma once
/// @file
namespace nix {
void registerNixBuildAndNixShell();
}
@@ -1,18 +1,20 @@
#include "profiles.hh"
#include "shared.hh"
#include "globals.hh"
#include "filetransfer.hh"
#include "store-api.hh"
#include "legacy.hh"
#include "fetchers.hh"
#include "eval-settings.hh" // for defexpr
#include "users.hh"
#include "lix/libstore/profiles.hh"
#include "lix/libmain/shared.hh"
#include "lix/libstore/globals.hh"
#include "lix/libstore/filetransfer.hh"
#include "lix/libstore/store-api.hh"
#include "lix/libcmd/legacy.hh"
#include "lix/libfetchers/fetchers.hh"
#include "lix/libexpr/eval-settings.hh" // for defexpr
#include "lix/libstore/temporary-dir.hh"
#include "lix/libutil/users.hh"
#include "nix-channel.hh"
#include <fcntl.h>
#include <regex>
#include <pwd.h>
using namespace nix;
namespace nix {
typedef std::map<std::string, std::string> Channels;
@@ -160,7 +162,7 @@ static void update(const StringSet & channelNames)
replaceSymlink(profile, channelLink);
}
static int main_nix_channel(int argc, char ** argv)
static int main_nix_channel(std::string programName, Strings argv)
{
{
// Figure out the name of the `.nix-channels' file to use
@@ -182,7 +184,7 @@ static int main_nix_channel(int argc, char ** argv)
cRollback
} cmd = cNone;
std::vector<std::string> args;
parseCmdLine(argc, argv, [&](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") {
@@ -205,7 +207,7 @@ static int main_nix_channel(int argc, char ** argv)
args.push_back(std::move(*arg));
}
return true;
});
}).parseCmdline(argv);
switch (cmd) {
case cNone:
@@ -264,4 +266,8 @@ static int main_nix_channel(int argc, char ** argv)
}
}
static RegisterLegacyCommand r_nix_channel("nix-channel", main_nix_channel);
void registerNixChannel() {
LegacyCommands::add("nix-channel", main_nix_channel);
}
}
+8
View File
@@ -0,0 +1,8 @@
#pragma once
/// @file
namespace nix {
void registerNixChannel();
}
@@ -1,17 +1,18 @@
#include "file-system.hh"
#include "store-api.hh"
#include "store-cast.hh"
#include "gc-store.hh"
#include "profiles.hh"
#include "shared.hh"
#include "globals.hh"
#include "legacy.hh"
#include "signals.hh"
#include "lix/libutil/file-system.hh"
#include "lix/libstore/store-api.hh"
#include "lix/libstore/store-cast.hh"
#include "lix/libstore/gc-store.hh"
#include "lix/libstore/profiles.hh"
#include "lix/libmain/shared.hh"
#include "lix/libstore/globals.hh"
#include "lix/libcmd/legacy.hh"
#include "lix/libutil/signals.hh"
#include "nix-collect-garbage.hh"
#include <iostream>
#include <cerrno>
using namespace nix;
namespace nix {
std::string deleteOlderThan;
bool dryRun = false;
@@ -55,14 +56,14 @@ void removeOldGenerations(std::string dir)
}
}
static int main_nix_collect_garbage(int argc, char * * argv)
static int main_nix_collect_garbage(std::string programName, Strings argv)
{
{
bool removeOld = false;
GCOptions options;
parseCmdLine(argc, argv, [&](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")
@@ -78,7 +79,7 @@ static int main_nix_collect_garbage(int argc, char * * argv)
else
return false;
return true;
});
}).parseCmdline(argv);
if (removeOld) {
std::set<Path> dirsToClean = {
@@ -110,4 +111,8 @@ static int main_nix_collect_garbage(int argc, char * * argv)
}
}
static RegisterLegacyCommand r_nix_collect_garbage("nix-collect-garbage", main_nix_collect_garbage);
void registerNixCollectGarbage() {
LegacyCommands::add("nix-collect-garbage", main_nix_collect_garbage);
}
}
+8
View File
@@ -0,0 +1,8 @@
#pragma once
/// @file
namespace nix {
void registerNixCollectGarbage();
}
@@ -1,10 +1,11 @@
#include "shared.hh"
#include "store-api.hh"
#include "legacy.hh"
#include "lix/libmain/shared.hh"
#include "lix/libstore/store-api.hh"
#include "lix/libcmd/legacy.hh"
#include "nix-copy-closure.hh"
using namespace nix;
namespace nix {
static int main_nix_copy_closure(int argc, char ** argv)
static int main_nix_copy_closure(std::string programName, Strings argv)
{
{
auto gzip = false;
@@ -15,7 +16,7 @@ static int main_nix_copy_closure(int argc, char ** argv)
std::string sshHost;
PathSet storePaths;
parseCmdLine(argc, argv, [&](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")
@@ -41,7 +42,7 @@ static int main_nix_copy_closure(int argc, char ** argv)
else
storePaths.insert(*arg);
return true;
});
}).parseCmdline(argv);
if (sshHost.empty())
throw UsageError("no host name specified");
@@ -60,4 +61,8 @@ static int main_nix_copy_closure(int argc, char ** argv)
}
}
static RegisterLegacyCommand r_nix_copy_closure("nix-copy-closure", main_nix_copy_closure);
void registerNixCopyClosure() {
LegacyCommands::add("nix-copy-closure", main_nix_copy_closure);
}
}
+8
View File
@@ -0,0 +1,8 @@
#pragma once
/// @file
namespace nix {
void registerNixCopyClosure();
}
+177 -154
View File
@@ -1,22 +1,24 @@
#include "attr-path.hh"
#include "common-eval-args.hh"
#include "derivations.hh"
#include "terminal.hh"
#include "eval.hh"
#include "get-drvs.hh"
#include "globals.hh"
#include "names.hh"
#include "profiles.hh"
#include "path-with-outputs.hh"
#include "shared.hh"
#include "store-api.hh"
#include "local-fs-store.hh"
#include "lix/libcmd/cmd-profiles.hh"
#include "lix/libexpr/attr-path.hh"
#include "lix/libcmd/common-eval-args.hh"
#include "lix/libstore/derivations.hh"
#include "lix/libutil/terminal.hh"
#include "lix/libexpr/eval.hh"
#include "lix/libexpr/get-drvs.hh"
#include "lix/libstore/globals.hh"
#include "lix/libstore/names.hh"
#include "lix/libstore/profiles.hh"
#include "lix/libstore/path-with-outputs.hh"
#include "lix/libmain/shared.hh"
#include "lix/libstore/store-api.hh"
#include "lix/libstore/local-fs-store.hh"
#include "user-env.hh"
#include "users.hh"
#include "value-to-json.hh"
#include "xml-writer.hh"
#include "legacy.hh"
#include "eval-settings.hh" // for defexpr
#include "lix/libutil/users.hh"
#include "lix/libexpr/value-to-json.hh"
#include "lix/libutil/xml-writer.hh"
#include "lix/libcmd/legacy.hh"
#include "lix/libexpr/eval-settings.hh" // for defexpr
#include "nix-env.hh"
#include <ctime>
#include <algorithm>
@@ -28,9 +30,10 @@
#include <unistd.h>
#include <nlohmann/json.hpp>
using namespace nix;
using std::cout;
namespace nix {
typedef enum {
srcNixExprDrvs,
@@ -56,7 +59,7 @@ struct Globals
{
InstallSourceInfo instSource;
Path profile;
std::shared_ptr<EvalState> state;
std::shared_ptr<Evaluator> state;
bool dryRun;
bool preserveInstalled;
bool removeAll;
@@ -93,19 +96,24 @@ static bool parseInstallSourceOptions(Globals & globals,
}
static bool isNixExpr(const SourcePath & path, struct InputAccessor::Stat & st)
static bool isNixExpr(EvalPaths & paths, const CheckedSourcePath & path, struct InputAccessor::Stat & st)
{
return
st.type == InputAccessor::tRegular
|| (st.type == InputAccessor::tDirectory && (path + "default.nix").pathExists());
if (st.type == InputAccessor::tRegular) {
return true;
} else if (st.type != InputAccessor::tDirectory) {
return false;
} else {
auto defaultNix = paths.checkSourcePath(path + "default.nix");
return defaultNix.pathExists();
}
}
static constexpr size_t maxAttrs = 1024;
static void getAllExprs(EvalState & state,
const SourcePath & path, StringSet & seen, BindingsBuilder & attrs)
static void getAllExprs(Evaluator & state,
const CheckedSourcePath & path, StringSet & seen, BindingsBuilder & attrs)
{
StringSet namesSorted;
for (auto & [name, _] : path.readDirectory()) namesSorted.insert(name);
@@ -116,16 +124,16 @@ static void getAllExprs(EvalState & state,
are implemented using profiles). */
if (i == "manifest.nix") continue;
SourcePath path2 = path + i;
auto path2 = state.paths.checkSourcePath(path + i);
InputAccessor::Stat st;
try {
st = path2.resolveSymlinks().lstat();
st = path2.stat();
} catch (Error &) {
continue; // ignore dangling symlinks in ~/.nix-defexpr
}
if (isNixExpr(path2, st) && (st.type != InputAccessor::tRegular || path2.baseName().ends_with(".nix"))) {
if (isNixExpr(state.paths, path2, st) && (st.type != InputAccessor::tRegular || path2.baseName().ends_with(".nix"))) {
/* Strip off the `.nix' filename suffix (if applicable),
otherwise the attribute cannot be selected with the
`-A' option. Useful if you want to stick a Nix
@@ -135,18 +143,18 @@ static void getAllExprs(EvalState & state,
attrName = std::string(attrName, 0, attrName.size() - 4);
if (!seen.insert(attrName).second) {
std::string suggestionMessage = "";
if (path2.path.abs().find("channels") != std::string::npos && path.path.abs().find("channels") != std::string::npos)
if (path2.to_string().find("channels") != std::string::npos && path.to_string().find("channels") != std::string::npos)
suggestionMessage = fmt("\nsuggestion: remove '%s' from either the root channels or the user channels", attrName);
printError("warning: name collision in input Nix expressions, skipping '%1%'"
"%2%", path2, suggestionMessage);
continue;
}
/* Load the expression on demand. */
auto vArg = state.allocValue();
vArg->mkString(path2.path.abs());
auto vArg = state.mem.allocValue();
vArg->mkString(path2.canonical().abs());
if (seen.size() == maxAttrs)
throw Error("too many Nix expressions in directory '%1%'", path);
attrs.alloc(attrName).mkApp(&state.getBuiltin("import"), vArg);
attrs.alloc(attrName).mkApp(&state.builtins.get("import"), vArg);
}
else if (st.type == InputAccessor::tDirectory)
/* `path2' is a directory (with no default.nix in it);
@@ -157,11 +165,12 @@ static void getAllExprs(EvalState & state,
static void loadSourceExpr(EvalState & state, const SourcePath & path, Value & v)
static void loadSourceExpr(EvalState & state, const SourcePath & path_, Value & v)
{
auto st = path.resolveSymlinks().lstat();
auto path = state.ctx.paths.checkSourcePath(path_);
auto st = path.stat();
if (isNixExpr(path, st))
if (isNixExpr(state.ctx.paths, path, st))
state.evalFile(path, v);
/* The path is a directory. Put the Nix expressions in the
@@ -171,10 +180,10 @@ static void loadSourceExpr(EvalState & state, const SourcePath & path, Value & v
~/.nix-defexpr directory that includes some system-wide
directory). */
else if (st.type == InputAccessor::tDirectory) {
auto attrs = state.buildBindings(maxAttrs);
auto attrs = state.ctx.buildBindings(maxAttrs);
attrs.alloc("_combineChannels").mkList(0);
StringSet seen;
getAllExprs(state, path, seen, attrs);
getAllExprs(state.ctx, path, seen, attrs);
v.mkAttrs(attrs);
}
@@ -197,7 +206,7 @@ static void loadDerivations(EvalState & state, const SourcePath & nixExprPath,
system. */
for (DrvInfos::iterator i = elems.begin(), j; i != elems.end(); i = j) {
j = i; j++;
if (systemFilter != "*" && i->querySystem() != systemFilter)
if (systemFilter != "*" && i->querySystem(state) != systemFilter)
elems.erase(i);
}
}
@@ -205,7 +214,7 @@ static void loadDerivations(EvalState & state, const SourcePath & nixExprPath,
static NixInt getPriority(EvalState & state, DrvInfo & drv)
{
return drv.queryMetaInt("priority", NixInt(0));
return drv.queryMetaInt(state, "priority", NixInt(0));
}
@@ -219,9 +228,9 @@ static std::strong_ordering comparePriorities(EvalState & state, DrvInfo & drv1,
// at a time.
static bool isPrebuilt(EvalState & state, DrvInfo & elem)
{
auto path = elem.queryOutPath();
if (state.store->isValidPath(path)) return true;
return state.store->querySubstitutablePaths({path}).count(path);
auto path = elem.queryOutPath(state);
if (state.ctx.store->isValidPath(path)) return true;
return state.ctx.store->querySubstitutablePaths({path}).count(path);
}
@@ -236,11 +245,11 @@ static void checkSelectorUse(DrvNames & selectors)
namespace {
std::set<std::string> searchByPrefix(DrvInfos & allElems, std::string_view prefix) {
std::set<std::string> searchByPrefix(EvalState & state, DrvInfos & allElems, std::string_view prefix) {
constexpr std::size_t maxResults = 3;
std::set<std::string> result;
for (auto & drvInfo : allElems) {
const auto drvName = DrvName { drvInfo.queryName() };
const auto drvName = DrvName { drvInfo.queryName(state) };
if (drvName.name.starts_with(prefix)) {
result.emplace(drvName.name);
@@ -278,7 +287,7 @@ std::vector<Match> pickNewestOnly(EvalState & state, std::vector<Match> matches)
for (auto & match : matches) {
auto & oneDrv = match.drvInfo;
const auto drvName = DrvName { oneDrv.queryName() };
const auto drvName = DrvName { oneDrv.queryName(state) };
std::strong_ordering comparison = std::strong_ordering::greater;
const auto itOther = newest.find(drvName.name);
@@ -287,13 +296,13 @@ std::vector<Match> pickNewestOnly(EvalState & state, std::vector<Match> matches)
auto & newestDrv = itOther->second.drvInfo;
comparison =
oneDrv.querySystem() == newestDrv.querySystem() ? std::strong_ordering::equal :
oneDrv.querySystem() == settings.thisSystem ? std::strong_ordering::greater :
newestDrv.querySystem() == settings.thisSystem ? std::strong_ordering::less : std::strong_ordering::equal;
oneDrv.querySystem(state) == newestDrv.querySystem(state) ? std::strong_ordering::equal :
oneDrv.querySystem(state) == settings.thisSystem ? std::strong_ordering::greater :
newestDrv.querySystem(state) == settings.thisSystem ? std::strong_ordering::less : std::strong_ordering::equal;
if (comparison == 0)
comparison = comparePriorities(state, oneDrv, newestDrv);
if (comparison == 0)
comparison = compareVersions(drvName.version, DrvName { newestDrv.queryName() }.version);
comparison = compareVersions(drvName.version, DrvName{newestDrv.queryName(state)}.version);
}
if (comparison > 0) {
@@ -332,7 +341,7 @@ static DrvInfos filterBySelector(EvalState & state, DrvInfos & allElems,
for (auto & selector : selectors) {
std::vector<Match> matches;
for (auto && [index, drvInfo] : enumerate(allElems)) {
const auto drvName = DrvName { drvInfo.queryName() };
const auto drvName = DrvName { drvInfo.queryName(state) };
if (selector.matches(drvName)) {
++selector.hits;
matches.emplace_back(drvInfo, index);
@@ -350,7 +359,7 @@ static DrvInfos filterBySelector(EvalState & state, DrvInfos & allElems,
elems.push_back(match.drvInfo);
if (selector.hits == 0 && selector.fullName != "*") {
const auto prefixHits = searchByPrefix(allElems, selector.name);
const auto prefixHits = searchByPrefix(state, allElems, selector.name);
if (prefixHits.empty()) {
throw Error("selector '%1%' matches no derivations", selector.fullName);
@@ -413,7 +422,7 @@ static void queryInstSources(EvalState & state,
loadSourceExpr(state, *instSource.nixExprPath, vArg);
for (auto & i : args) {
Expr & eFun = state.parseExprFromString(i, state.rootPath(CanonPath::fromCwd()));
Expr & eFun = state.ctx.parseExprFromString(i, CanonPath::fromCwd());
Value vFun, vTmp;
state.eval(eFun, vFun);
vTmp.mkApp(&vFun, &vArg);
@@ -429,16 +438,16 @@ static void queryInstSources(EvalState & state,
case srcStorePaths: {
for (auto & i : args) {
auto path = state.store->followLinksToStorePath(i);
auto path = state.ctx.store->followLinksToStorePath(i);
std::string name(path.name());
DrvInfo elem(state, "", nullptr);
DrvInfo elem("", nullptr);
elem.setName(name);
if (path.isDerivation()) {
elem.setDrvPath(path);
auto outputs = state.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)
@@ -483,23 +492,23 @@ static void printMissing(EvalState & state, DrvInfos & elems)
{
std::vector<DerivedPath> targets;
for (auto & i : elems)
if (auto drvPath = i.queryDrvPath())
if (auto drvPath = i.queryDrvPath(state))
targets.emplace_back(DerivedPath::Built{
.drvPath = makeConstantStorePathRef(*drvPath),
.outputs = OutputsSpec::All { },
});
else
targets.emplace_back(DerivedPath::Opaque{
.path = i.queryOutPath(),
.path = i.queryOutPath(state),
});
printMissing(state.store, targets);
printMissing(state.ctx.store, targets);
}
static bool keep(DrvInfo & drv)
static bool keep(EvalState & state, DrvInfo & drv)
{
return drv.queryMetaBool("keep", false);
return drv.queryMetaBool(state, "keep", false);
}
@@ -508,13 +517,15 @@ static void installDerivations(Globals & globals,
{
debug("installing derivations");
auto state = globals.state->begin();
/* Get the set of user environment elements to be installed. */
DrvInfos newElems, newElemsTmp;
queryInstSources(*globals.state, globals.instSource, args, newElemsTmp, true);
queryInstSources(*state, globals.instSource, args, newElemsTmp, true);
/* If --prebuilt-only is given, filter out source-only packages. */
for (auto & i : newElemsTmp)
if (!globals.prebuiltOnly || isPrebuilt(*globals.state, i))
if (!globals.prebuiltOnly || isPrebuilt(*state, i))
newElems.push_back(i);
StringSet newNames;
@@ -525,7 +536,7 @@ static void installDerivations(Globals & globals,
`java-front-0.9pre15899'). */
if (globals.forceName != "")
i.setName(globals.forceName);
newNames.insert(DrvName(i.queryName()).name);
newNames.insert(DrvName(i.queryName(*state)).name);
}
@@ -537,27 +548,27 @@ static void installDerivations(Globals & globals,
/* Add in the already installed derivations, unless they have
the same name as a to-be-installed element. */
if (!globals.removeAll) {
DrvInfos installedElems = queryInstalled(*globals.state, profile);
DrvInfos installedElems = queryInstalled(*state, profile);
for (auto & i : installedElems) {
DrvName drvName(i.queryName());
DrvName drvName(i.queryName(*state));
if (!globals.preserveInstalled &&
newNames.find(drvName.name) != newNames.end() &&
!keep(i))
printInfo("replacing old '%s'", i.queryName());
!keep(*state, i))
printInfo("replacing old '%s'", i.queryName(*state));
else
allElems.push_back(i);
}
for (auto & i : newElems)
printInfo("installing '%s'", i.queryName());
printInfo("installing '%s'", i.queryName(*state));
}
printMissing(*globals.state, newElems);
printMissing(*state, newElems);
if (globals.dryRun) return;
if (createUserEnv(*globals.state, allElems,
if (createUserEnv(*state, allElems,
profile, settings.envKeepDerivations, lockToken)) break;
}
}
@@ -587,6 +598,8 @@ static void upgradeDerivations(Globals & globals,
{
debug("upgrading derivations");
auto state = globals.state->begin();
/* Upgrade works as follows: we take all currently installed
derivations, and for any derivation matching any selector, look
for a derivation in the input Nix expression that has the same
@@ -595,20 +608,20 @@ static void upgradeDerivations(Globals & globals,
while (true) {
auto lockToken = optimisticLockProfile(globals.profile);
DrvInfos installedElems = queryInstalled(*globals.state, globals.profile);
DrvInfos installedElems = queryInstalled(*state, globals.profile);
/* Fetch all derivations from the input file. */
DrvInfos availElems;
queryInstSources(*globals.state, globals.instSource, args, availElems, false);
queryInstSources(*state, globals.instSource, args, availElems, false);
/* Go through all installed derivations. */
DrvInfos newElems;
for (auto & i : installedElems) {
DrvName drvName(i.queryName());
DrvName drvName(i.queryName(*state));
try {
if (keep(i)) {
if (keep(*state, i)) {
newElems.push_back(i);
continue;
}
@@ -623,9 +636,9 @@ static void upgradeDerivations(Globals & globals,
DrvInfos::iterator bestElem = availElems.end();
std::string bestVersion;
for (auto j = availElems.begin(); j != availElems.end(); ++j) {
if (comparePriorities(*globals.state, i, *j) > 0)
if (comparePriorities(*state, i, *j) > 0)
continue;
DrvName newName(j->queryName());
DrvName newName(j->queryName(*state));
if (newName.name == drvName.name) {
std::strong_ordering d = compareVersions(drvName.version, newName.version);
if ((upgradeType == utLt && d < 0) ||
@@ -635,10 +648,10 @@ static void upgradeDerivations(Globals & globals,
{
std::strong_ordering d2 = std::strong_ordering::less;
if (bestElem != availElems.end()) {
d2 = comparePriorities(*globals.state, *bestElem, *j);
d2 = comparePriorities(*state, *bestElem, *j);
if (d2 == 0) d2 = compareVersions(bestVersion, newName.version);
}
if (d2 < 0 && (!globals.prebuiltOnly || isPrebuilt(*globals.state, *j))) {
if (d2 < 0 && (!globals.prebuiltOnly || isPrebuilt(*state, *j))) {
bestElem = j;
bestVersion = newName.version;
}
@@ -647,27 +660,27 @@ static void upgradeDerivations(Globals & globals,
}
if (bestElem != availElems.end() &&
i.queryOutPath() !=
bestElem->queryOutPath())
i.queryOutPath(*state) !=
bestElem->queryOutPath(*state))
{
const char * action = compareVersions(drvName.version, bestVersion) <= 0
? "upgrading" : "downgrading";
printInfo("%1% '%2%' to '%3%'",
action, i.queryName(), bestElem->queryName());
action, i.queryName(*state), bestElem->queryName(*state));
newElems.push_back(*bestElem);
} else newElems.push_back(i);
} catch (Error & e) {
e.addTrace(nullptr, "while trying to find an upgrade for '%s'", i.queryName());
e.addTrace(nullptr, "while trying to find an upgrade for '%s'", i.queryName(*state));
throw;
}
}
printMissing(*globals.state, newElems);
printMissing(*state, newElems);
if (globals.dryRun) return;
if (createUserEnv(*globals.state, newElems,
if (createUserEnv(*state, newElems,
globals.profile, settings.envKeepDerivations, lockToken)) break;
}
}
@@ -693,9 +706,9 @@ 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.allocValue();
auto v = state.ctx.mem.allocValue();
v->mkString(value);
drv.setMeta(name, v);
drv.setMeta(state, name, v);
}
@@ -711,19 +724,21 @@ 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();
while (true) {
std::string lockToken = optimisticLockProfile(globals.profile);
DrvInfos installedElems = queryInstalled(*globals.state, globals.profile);
DrvInfos installedElems = queryInstalled(*state, globals.profile);
/* Update all matching derivations. */
for (auto & i : installedElems) {
DrvName drvName(i.queryName());
DrvName drvName(i.queryName(*state));
for (auto & j : selectors)
if (j.matches(drvName)) {
printInfo("setting flag on '%1%'", i.queryName());
printInfo("setting flag on '%1%'", i.queryName(*state));
j.hits++;
setMetaFlag(*globals.state, i, flagName, flagValue);
setMetaFlag(*state, i, flagName, flagValue);
break;
}
}
@@ -731,7 +746,7 @@ static void opSetFlag(Globals & globals, Strings opFlags, Strings opArgs)
checkSelectorUse(selectors);
/* Write the new user environment. */
if (createUserEnv(*globals.state, installedElems,
if (createUserEnv(*state, installedElems,
globals.profile, settings.envKeepDerivations, lockToken)) break;
}
}
@@ -739,6 +754,8 @@ static void opSetFlag(Globals & globals, Strings opFlags, Strings opArgs)
static void opSet(Globals & globals, Strings opFlags, Strings opArgs)
{
auto state = globals.state->begin();
auto store2 = globals.state->store.dynamic_pointer_cast<LocalFSStore>();
if (!store2) throw Error("--set is not supported for this Nix store");
@@ -749,7 +766,7 @@ static void opSet(Globals & globals, Strings opFlags, Strings opArgs)
}
DrvInfos elems;
queryInstSources(*globals.state, globals.instSource, opArgs, elems, true);
queryInstSources(*state, globals.instSource, opArgs, elems, true);
if (elems.size() != 1)
throw Error("--set requires exactly one derivation");
@@ -759,7 +776,7 @@ static void opSet(Globals & globals, Strings opFlags, Strings opArgs)
if (globals.forceName != "")
drv.setName(globals.forceName);
auto drvPath = drv.queryDrvPath();
auto drvPath = drv.queryDrvPath(*state);
std::vector<DerivedPath> paths {
drvPath
? (DerivedPath) (DerivedPath::Built {
@@ -767,7 +784,7 @@ static void opSet(Globals & globals, Strings opFlags, Strings opArgs)
.outputs = OutputsSpec::All { },
})
: (DerivedPath) (DerivedPath::Opaque {
.path = drv.queryOutPath(),
.path = drv.queryOutPath(*state),
}),
};
printMissing(globals.state->store, paths);
@@ -778,7 +795,7 @@ static void opSet(Globals & globals, Strings opFlags, Strings opArgs)
Path generation = createGeneration(
*store2,
globals.profile,
drv.queryOutPath());
drv.queryOutPath(*state));
switchLink(globals.profile, generation);
}
@@ -786,10 +803,12 @@ static void opSet(Globals & globals, Strings opFlags, Strings opArgs)
static void uninstallDerivations(Globals & globals, Strings & selectors,
Path & profile)
{
auto state = globals.state->begin();
while (true) {
auto lockToken = optimisticLockProfile(profile);
DrvInfos workingElems = queryInstalled(*globals.state, profile);
DrvInfos workingElems = queryInstalled(*state, profile);
for (auto & selector : selectors) {
DrvInfos::iterator split = workingElems.begin();
@@ -797,16 +816,16 @@ static void uninstallDerivations(Globals & globals, Strings & selectors,
StorePath selectorStorePath = globals.state->store->followLinksToStorePath(selector);
split = std::partition(
workingElems.begin(), workingElems.end(),
[&selectorStorePath, globals](auto &elem) {
return selectorStorePath != elem.queryOutPath();
[&selectorStorePath, &state](auto &elem) {
return selectorStorePath != elem.queryOutPath(*state);
}
);
} else {
DrvName selectorName(selector);
split = std::partition(
workingElems.begin(), workingElems.end(),
[&selectorName](auto &elem){
DrvName elemName(elem.queryName());
[&selectorName, &state](auto &elem){
DrvName elemName(elem.queryName(*state));
return !selectorName.matches(elemName);
}
);
@@ -814,14 +833,14 @@ static void uninstallDerivations(Globals & globals, Strings & selectors,
if (split == workingElems.end())
warn("selector '%s' matched no installed derivations", selector);
for (auto removedElem = split; removedElem != workingElems.end(); removedElem++) {
printInfo("uninstalling '%s'", removedElem->queryName());
printInfo("uninstalling '%s'", removedElem->queryName(*state));
}
workingElems.erase(split, workingElems.end());
}
if (globals.dryRun) return;
if (createUserEnv(*globals.state, workingElems,
if (createUserEnv(*state, workingElems,
profile, settings.envKeepDerivations, lockToken)) break;
}
}
@@ -841,10 +860,10 @@ static bool cmpChars(char a, char b)
}
static bool cmpElemByName(DrvInfo & a, DrvInfo & b)
static bool cmpElemByName(EvalState & state, DrvInfo & a, DrvInfo & b)
{
auto a_name = a.queryName();
auto b_name = b.queryName();
auto a_name = a.queryName(state);
auto b_name = b.queryName(state);
return lexicographical_compare(
a_name.begin(), a_name.end(),
b_name.begin(), b_name.end(), cmpChars);
@@ -894,15 +913,15 @@ void printTable(Table & table)
typedef enum { cvLess, cvEqual, cvGreater, cvUnavail } VersionDiff;
static VersionDiff compareVersionAgainstSet(
DrvInfo & elem, DrvInfos & elems, std::string & version)
EvalState & state, DrvInfo & elem, DrvInfos & elems, std::string & version)
{
DrvName name(elem.queryName());
DrvName name(elem.queryName(state));
VersionDiff diff = cvUnavail;
version = "?";
for (auto & i : elems) {
DrvName name2(i.queryName());
DrvName name2(i.queryName(state));
if (name.name == name2.name) {
std::strong_ordering d = compareVersions(name.version, name2.version);
if (d < 0) {
@@ -925,7 +944,7 @@ static VersionDiff compareVersionAgainstSet(
}
static void queryJSON(Globals & globals, std::vector<DrvInfo> & elems, bool printOutPath, bool printDrvPath, bool printMeta)
static void queryJSON(EvalState & state, Globals & globals, std::vector<DrvInfo> & elems, bool printOutPath, bool printDrvPath, bool printMeta)
{
using nlohmann::json;
json topObj = json::object();
@@ -934,18 +953,18 @@ static void queryJSON(Globals & globals, std::vector<DrvInfo> & elems, bool prin
if (i.hasFailed()) continue;
auto drvName = DrvName(i.queryName());
auto drvName = DrvName(i.queryName(state));
json &pkgObj = topObj[i.attrPath];
pkgObj = {
{"name", drvName.fullName},
{"pname", drvName.name},
{"version", drvName.version},
{"system", i.querySystem()},
{"outputName", i.queryOutputName()},
{"system", i.querySystem(state)},
{"outputName", i.queryOutputName(state)},
};
{
DrvInfo::Outputs outputs = i.queryOutputs(printOutPath);
DrvInfo::Outputs outputs = i.queryOutputs(state, printOutPath);
json &outputObj = pkgObj["outputs"];
outputObj = json::object();
for (auto & j : outputs) {
@@ -957,29 +976,29 @@ static void queryJSON(Globals & globals, std::vector<DrvInfo> & elems, bool prin
}
if (printDrvPath) {
auto drvPath = i.queryDrvPath();
auto drvPath = i.queryDrvPath(state);
if (drvPath) pkgObj["drvPath"] = globals.state->store->printStorePath(*drvPath);
}
if (printMeta) {
json &metaObj = pkgObj["meta"];
metaObj = json::object();
StringSet metaNames = i.queryMetaNames();
StringSet metaNames = i.queryMetaNames(state);
for (auto & j : metaNames) {
Value * v = i.queryMeta(j);
Value * v = i.queryMeta(state, j);
if (!v) {
printError("derivation '%s' has invalid meta attribute '%s'", i.queryName(), j);
printError("derivation '%s' has invalid meta attribute '%s'", i.queryName(state), j);
metaObj[j] = nullptr;
} else {
NixStringContext context;
metaObj[j] = printValueAsJSON(*globals.state, true, *v, noPos, context);
metaObj[j] = printValueAsJSON(state, true, *v, noPos, context);
}
}
}
} catch (AssertionError & e) {
printMsg(lvlTalkative, "skipping derivation named '%1%' which gives an assertion failure", i.queryName());
printMsg(lvlTalkative, "skipping derivation named '%1%' which gives an assertion failure", i.queryName(state));
} catch (Error & e) {
e.addTrace(nullptr, "while querying the derivation named '%1%'", i.queryName());
e.addTrace(nullptr, "while querying the derivation named '%1%'", i.queryName(state));
throw;
}
}
@@ -990,6 +1009,7 @@ static void queryJSON(Globals & globals, std::vector<DrvInfo> & elems, bool prin
static void opQuery(Globals & globals, Strings opFlags, Strings opArgs)
{
auto & store { *globals.state->store };
auto state = globals.state->begin();
Strings remaining;
std::string attrPath;
@@ -1038,14 +1058,14 @@ static void opQuery(Globals & globals, Strings opFlags, Strings opArgs)
DrvInfos availElems, installedElems;
if (source == sInstalled || compareVersions || printStatus)
installedElems = queryInstalled(*globals.state, globals.profile);
installedElems = queryInstalled(*state, globals.profile);
if (source == sAvailable || compareVersions)
loadDerivations(*globals.state, *globals.instSource.nixExprPath,
loadDerivations(*state, *globals.instSource.nixExprPath,
globals.instSource.systemFilter, *globals.instSource.autoArgs,
attrPath, availElems);
DrvInfos elems_ = filterBySelector(*globals.state,
DrvInfos elems_ = filterBySelector(*state,
source == sInstalled ? installedElems : availElems,
opArgs, false);
@@ -1056,8 +1076,7 @@ static void opQuery(Globals & globals, Strings opFlags, Strings opArgs)
/* !!! */
std::vector<DrvInfo> elems;
for (auto & i : elems_) elems.push_back(i);
sort(elems.begin(), elems.end(), cmpElemByName);
sort(elems.begin(), elems.end(), [&] (auto& a, auto& b) { return cmpElemByName(*state, a, b); });
/* We only need to know the installed paths when we are querying
the status of the derivation. */
@@ -1065,7 +1084,7 @@ static void opQuery(Globals & globals, Strings opFlags, Strings opArgs)
if (printStatus)
for (auto & i : installedElems)
installed.insert(i.queryOutPath());
installed.insert(i.queryOutPath(*state));
/* Query which paths have substitutes. */
@@ -1075,9 +1094,9 @@ static void opQuery(Globals & globals, Strings opFlags, Strings opArgs)
StorePathSet paths;
for (auto & i : elems)
try {
paths.insert(i.queryOutPath());
paths.insert(i.queryOutPath(*state));
} catch (AssertionError & e) {
printMsg(lvlTalkative, "skipping derivation named '%s' which gives an assertion failure", i.queryName());
printMsg(lvlTalkative, "skipping derivation named '%s' which gives an assertion failure", i.queryName(*state));
i.setFailed();
}
validPaths = store.queryValidPaths(paths);
@@ -1087,7 +1106,7 @@ static void opQuery(Globals & globals, Strings opFlags, Strings opArgs)
/* Print the desired columns, or XML output. */
if (jsonOutput) {
queryJSON(globals, elems, printOutPath, printDrvPath, printMeta);
queryJSON(*state, globals, elems, printOutPath, printDrvPath, printMeta);
cout << '\n';
return;
}
@@ -1106,8 +1125,8 @@ static void opQuery(Globals & globals, Strings opFlags, Strings opArgs)
//Activity act(*logger, lvlDebug, "outputting query result '%1%'", i.attrPath);
if (globals.prebuiltOnly &&
!validPaths.count(i.queryOutPath()) &&
!substitutablePaths.count(i.queryOutPath()))
!validPaths.count(i.queryOutPath(*state)) &&
!substitutablePaths.count(i.queryOutPath(*state)))
continue;
/* For table output. */
@@ -1117,7 +1136,7 @@ static void opQuery(Globals & globals, Strings opFlags, Strings opArgs)
XMLAttrs attrs;
if (printStatus) {
auto outPath = i.queryOutPath();
auto outPath = i.queryOutPath(*state);
bool hasSubs = substitutablePaths.count(outPath);
bool isInstalled = installed.count(outPath);
bool isValid = validPaths.count(outPath);
@@ -1138,12 +1157,12 @@ static void opQuery(Globals & globals, Strings opFlags, Strings opArgs)
columns.push_back(i.attrPath);
if (xmlOutput) {
auto drvName = DrvName(i.queryName());
auto drvName = DrvName(i.queryName(*state));
attrs["name"] = drvName.fullName;
attrs["pname"] = drvName.name;
attrs["version"] = drvName.version;
} else if (printName) {
columns.push_back(i.queryName());
columns.push_back(i.queryName(*state));
}
if (compareVersions) {
@@ -1152,7 +1171,7 @@ static void opQuery(Globals & globals, Strings opFlags, Strings opArgs)
elements, or the set of installed elements. !!!
This is O(N * M), should be O(N * lg M). */
std::string version;
VersionDiff diff = compareVersionAgainstSet(i, otherElems, version);
VersionDiff diff = compareVersionAgainstSet(*state, i, otherElems, version);
char ch;
switch (diff) {
@@ -1177,13 +1196,13 @@ static void opQuery(Globals & globals, Strings opFlags, Strings opArgs)
}
if (xmlOutput) {
if (i.querySystem() != "") attrs["system"] = i.querySystem();
if (i.querySystem(*state) != "") attrs["system"] = i.querySystem(*state);
}
else if (printSystem)
columns.push_back(i.querySystem());
columns.push_back(i.querySystem(*state));
if (printDrvPath) {
auto drvPath = i.queryDrvPath();
auto drvPath = i.queryDrvPath(*state);
if (xmlOutput) {
if (drvPath) attrs["drvPath"] = store.printStorePath(*drvPath);
} else
@@ -1191,10 +1210,10 @@ static void opQuery(Globals & globals, Strings opFlags, Strings opArgs)
}
if (xmlOutput)
attrs["outputName"] = i.queryOutputName();
attrs["outputName"] = i.queryOutputName(*state);
if (printOutPath && !xmlOutput) {
DrvInfo::Outputs outputs = i.queryOutputs();
DrvInfo::Outputs outputs = i.queryOutputs(*state);
std::string s;
for (auto & j : outputs) {
if (!s.empty()) s += ';';
@@ -1205,7 +1224,7 @@ static void opQuery(Globals & globals, Strings opFlags, Strings opArgs)
}
if (printDescription) {
auto descr = i.queryMetaString("description");
auto descr = i.queryMetaString(*state, "description");
if (xmlOutput) {
if (descr != "") attrs["description"] = descr;
} else
@@ -1214,7 +1233,7 @@ static void opQuery(Globals & globals, Strings opFlags, Strings opArgs)
if (xmlOutput) {
XMLOpenElement item(xml, "item", attrs);
DrvInfo::Outputs outputs = i.queryOutputs(printOutPath);
DrvInfo::Outputs outputs = i.queryOutputs(*state, printOutPath);
for (auto & j : outputs) {
XMLAttrs attrs2;
attrs2["name"] = j.first;
@@ -1223,15 +1242,15 @@ static void opQuery(Globals & globals, Strings opFlags, Strings opArgs)
xml.writeEmptyElement("output", attrs2);
}
if (printMeta) {
StringSet metaNames = i.queryMetaNames();
StringSet metaNames = i.queryMetaNames(*state);
for (auto & j : metaNames) {
XMLAttrs attrs2;
attrs2["name"] = j;
Value * v = i.queryMeta(j);
Value * v = i.queryMeta(*state, j);
if (!v)
printError(
"derivation '%s' has invalid meta attribute '%s'",
i.queryName(), j);
i.queryName(*state), j);
else {
if (v->type() == nString) {
attrs2["type"] = "string";
@@ -1280,9 +1299,9 @@ static void opQuery(Globals & globals, Strings opFlags, Strings opArgs)
cout.flush();
} catch (AssertionError & e) {
printMsg(lvlTalkative, "skipping derivation named '%1%' which gives an assertion failure", i.queryName());
printMsg(lvlTalkative, "skipping derivation named '%1%' which gives an assertion failure", i.queryName(*state));
} catch (Error & e) {
e.addTrace(nullptr, "while querying the derivation named '%1%'", i.queryName());
e.addTrace(nullptr, "while querying the derivation named '%1%'", i.queryName(*state));
throw;
}
}
@@ -1393,7 +1412,7 @@ static void opVersion(Globals & globals, Strings opFlags, Strings opArgs)
}
static int main_nix_env(int argc, char * * argv)
static int main_nix_env(std::string programName, Strings argv)
{
{
Strings opFlags, opArgs;
@@ -1432,7 +1451,7 @@ static int main_nix_env(int argc, char * * argv)
using LegacyArgs::LegacyArgs;
};
MyArgs myArgs(std::string(baseNameOf(argv[0])), [&](Strings::iterator & arg, const Strings::iterator & end) {
MyArgs myArgs(programName, [&](Strings::iterator & arg, const Strings::iterator & end) {
Operation oldOp = op;
if (*arg == "--help")
@@ -1513,20 +1532,20 @@ static int main_nix_env(int argc, char * * argv)
return true;
});
myArgs.parseCmdline(argvToStrings(argc, argv));
myArgs.parseCmdline(argv);
if (showHelp) showManPage("nix-env" + opName);
if (!op) throw UsageError("no operation specified");
auto store = openStore();
globals.state = std::shared_ptr<EvalState>(new EvalState(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 != ""
? lookupFileArg(*globals.state, file)
: globals.state->rootPath(CanonPath(nixExprPath)));
: CanonPath(nixExprPath));
globals.instSource.autoArgs = myArgs.getAutoArgs(*globals.state);
@@ -1544,4 +1563,8 @@ static int main_nix_env(int argc, char * * argv)
}
}
static RegisterLegacyCommand r_nix_env("nix-env", main_nix_env);
void registerNixEnv() {
LegacyCommands::add("nix-env", main_nix_env);
}
}
+8
View File
@@ -0,0 +1,8 @@
#pragma once
/// @file
namespace nix {
void registerNixEnv();
}
@@ -1,22 +1,23 @@
#include "globals.hh"
#include "print-ambiguous.hh"
#include "shared.hh"
#include "eval.hh"
#include "eval-inline.hh"
#include "get-drvs.hh"
#include "attr-path.hh"
#include "value-to-xml.hh"
#include "value-to-json.hh"
#include "store-api.hh"
#include "local-fs-store.hh"
#include "common-eval-args.hh"
#include "legacy.hh"
#include "lix/libstore/globals.hh"
#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"
#include "lix/libexpr/value-to-json.hh"
#include "lix/libstore/store-api.hh"
#include "lix/libstore/local-fs-store.hh"
#include "lix/libcmd/common-eval-args.hh"
#include "lix/libcmd/legacy.hh"
#include "nix-instantiate.hh"
#include <map>
#include <iostream>
using namespace nix;
namespace nix {
static Path gcRoot;
@@ -30,7 +31,7 @@ void processExpr(EvalState & state, const Strings & attrPaths,
bool evalOnly, OutputKind output, bool location, Expr & e)
{
if (parseOnly) {
e.show(state.symbols, std::cout);
e.show(state.ctx.symbols, std::cout);
std::cout << "\n";
return;
}
@@ -57,18 +58,18 @@ void processExpr(EvalState & state, const Strings & attrPaths,
} else {
if (strict) state.forceValueDeep(vRes);
std::set<const void *> seen;
printAmbiguous(vRes, state.symbols, std::cout, &seen, std::numeric_limits<int>::max());
printAmbiguous(vRes, state.ctx.symbols, std::cout, &seen, std::numeric_limits<int>::max());
std::cout << std::endl;
}
} else {
DrvInfos drvs;
getDerivations(state, v, "", autoArgs, drvs, false);
for (auto & i : drvs) {
auto drvPath = i.requireDrvPath();
auto drvPathS = state.store->printStorePath(drvPath);
auto drvPath = i.requireDrvPath(state);
auto drvPathS = state.ctx.store->printStorePath(drvPath);
/* What output do we want? */
std::string outputName = i.queryOutputName();
std::string outputName = i.queryOutputName(state);
if (outputName == "")
throw Error("derivation '%1%' lacks an 'outputName' attribute", drvPathS);
@@ -77,7 +78,7 @@ void processExpr(EvalState & state, const Strings & attrPaths,
else {
Path rootName = absPath(gcRoot);
if (++rootNr > 1) rootName += "-" + std::to_string(rootNr);
auto store2 = state.store.dynamic_pointer_cast<LocalFSStore>();
auto store2 = state.ctx.store.dynamic_pointer_cast<LocalFSStore>();
if (store2)
drvPathS = store2->addPermRoot(drvPath, rootName);
}
@@ -88,7 +89,7 @@ void processExpr(EvalState & state, const Strings & attrPaths,
}
static int main_nix_instantiate(int argc, char * * argv)
static int main_nix_instantiate(std::string programName, Strings argv)
{
{
Strings files;
@@ -108,7 +109,7 @@ static int main_nix_instantiate(int argc, char * * argv)
using LegacyArgs::LegacyArgs;
};
MyArgs myArgs(std::string(baseNameOf(argv[0])), [&](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")
@@ -148,7 +149,7 @@ static int main_nix_instantiate(int argc, char * * argv)
return true;
});
myArgs.parseCmdline(argvToStrings(argc, argv));
myArgs.parseCmdline(argv);
if (evalOnly && !wantsReadWrite)
settings.readOnlyMode = true;
@@ -156,26 +157,24 @@ static int main_nix_instantiate(int argc, char * * argv)
auto store = openStore();
auto evalStore = myArgs.evalStoreUrl ? openStore(*myArgs.evalStoreUrl) : store;
auto state = std::make_unique<EvalState>(myArgs.searchPath, evalStore, store);
state->repair = myArgs.repair;
auto evaluator = std::make_unique<Evaluator>(myArgs.searchPath, evalStore, store);
auto state = evaluator->begin();
evaluator->repair = myArgs.repair;
Bindings & autoArgs = *myArgs.getAutoArgs(*state);
Bindings & autoArgs = *myArgs.getAutoArgs(*evaluator);
if (attrPaths.empty()) attrPaths = {""};
if (findFile) {
for (auto & i : files) {
auto p = state->findFile(i);
if (auto fn = p.getPhysicalPath())
std::cout << fn->abs() << std::endl;
else
throw Error("'%s' has no physical path", p);
auto p = evaluator->paths.findFile(i);
std::cout << p.canonical().abs() << std::endl;
}
return 0;
}
if (readStdin) {
Expr & e = state->parseStdin();
Expr & e = evaluator->parseStdin();
processExpr(*state, attrPaths, parseOnly, strict, autoArgs,
evalOnly, outputKind, xmlOutputSourceLocation, e);
} else if (files.empty() && !fromArgs)
@@ -183,16 +182,20 @@ static int main_nix_instantiate(int argc, char * * argv)
for (auto & i : files) {
Expr & e = fromArgs
? state->parseExprFromString(i, state->rootPath(CanonPath::fromCwd()))
: state->parseExprFromFile(resolveExprPath(state->checkSourcePath(lookupFileArg(*state, i))));
? evaluator->parseExprFromString(i, CanonPath::fromCwd())
: evaluator->parseExprFromFile(evaluator->paths.resolveExprPath(lookupFileArg(*evaluator, i)));
processExpr(*state, attrPaths, parseOnly, strict, autoArgs,
evalOnly, outputKind, xmlOutputSourceLocation, e);
}
state->maybePrintStats();
evaluator->maybePrintStats();
return 0;
}
}
static RegisterLegacyCommand r_nix_instantiate("nix-instantiate", main_nix_instantiate);
void registerNixInstantiate() {
LegacyCommands::add("nix-instantiate", main_nix_instantiate);
}
}
+8
View File
@@ -0,0 +1,8 @@
#pragma once
/// @file
namespace nix {
void registerNixInstantiate();
}
@@ -1,33 +1,33 @@
#include "archive.hh"
#include "derivations.hh"
#include "lix/libutil/archive.hh"
#include "lix/libstore/derivations.hh"
#include "dotgraph.hh"
#include "globals.hh"
#include "build-result.hh"
#include "store-cast.hh"
#include "gc-store.hh"
#include "log-store.hh"
#include "local-store.hh"
#include "monitor-fd.hh"
#include "serve-protocol.hh"
#include "serve-protocol-impl.hh"
#include "shared.hh"
#include "lix/libutil/exit.hh"
#include "lix/libstore/globals.hh"
#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"
#include "lix/libmain/shared.hh"
#include "graphml.hh"
#include "legacy.hh"
#include "path-with-outputs.hh"
#include "lix/libcmd/legacy.hh"
#include "lix/libstore/path-with-outputs.hh"
#include "nix-store.hh"
#include <iostream>
#include <algorithm>
#include <cstdio>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
namespace nix_store {
namespace nix {
using namespace nix;
using std::cin;
using std::cout;
@@ -831,12 +831,12 @@ static void opServe(Strings opFlags, Strings opArgs)
// FIXME: changing options here doesn't work if we're
// building through the daemon.
verbosity = lvlError;
settings.keepLog = false;
settings.useSubstitutes = false;
settings.maxSilentTime = readInt(in);
settings.buildTimeout = readInt(in);
settings.keepLog.override(false);
settings.useSubstitutes.override(false);
settings.maxSilentTime.override(readInt(in));
settings.buildTimeout.override(readInt(in));
if (GET_PROTOCOL_MINOR(clientVersion) >= 2)
settings.maxLogSize = readNum<unsigned long>(in);
settings.maxLogSize.override(readNum<unsigned long>(in));
if (GET_PROTOCOL_MINOR(clientVersion) >= 3) {
auto nrRepeats = readInt(in);
if (nrRepeats != 0) {
@@ -850,10 +850,10 @@ static void opServe(Strings opFlags, Strings opArgs)
// asked for.
readInt(in);
settings.runDiffHook = true;
settings.runDiffHook.override(true);
}
if (GET_PROTOCOL_MINOR(clientVersion) >= 7) {
settings.keepFailed = (bool) readInt(in);
settings.keepFailed.override((bool) readInt(in));
}
};
@@ -1031,7 +1031,7 @@ static void opVersion(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(int argc, char * * argv)
static int main_nix_store(std::string programName, Strings argv)
{
{
Strings opFlags, opArgs;
@@ -1040,7 +1040,7 @@ static int main_nix_store(int argc, char * * argv)
std::string opName;
bool showHelp = false;
parseCmdLine(argc, argv, [&](Strings::iterator & arg, const Strings::iterator & end) {
LegacyArgs(programName, [&](Strings::iterator & arg, const Strings::iterator & end) {
Operation oldOp = op;
if (*arg == "--help")
@@ -1162,7 +1162,7 @@ static int main_nix_store(int argc, char * * argv)
throw UsageError("only one operation may be specified");
return true;
});
}).parseCmdline(argv);
if (showHelp) showManPage("nix-store" + opName);
if (!op) throw UsageError("no operation specified");
@@ -1176,6 +1176,8 @@ static int main_nix_store(int argc, char * * argv)
}
}
static RegisterLegacyCommand r_nix_store("nix-store", main_nix_store);
void registerNixStore() {
LegacyCommands::add("nix-store", main_nix_store);
}
}
+8
View File
@@ -0,0 +1,8 @@
#pragma once
/// @file
namespace nix {
void registerNixStore();
}
@@ -1,14 +1,14 @@
#include "user-env.hh"
#include "derivations.hh"
#include "store-api.hh"
#include "path-with-outputs.hh"
#include "local-fs-store.hh"
#include "globals.hh"
#include "shared.hh"
#include "eval.hh"
#include "eval-inline.hh"
#include "profiles.hh"
#include "print-ambiguous.hh"
#include "lix/libstore/derivations.hh"
#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"
#include <limits>
#include <sstream>
@@ -24,66 +24,65 @@ bool createUserEnv(EvalState & state, DrvInfos & elems,
exist already. */
std::vector<StorePathWithOutputs> drvsToBuild;
for (auto & i : elems)
if (auto drvPath = i.queryDrvPath())
if (auto drvPath = i.queryDrvPath(state))
drvsToBuild.push_back({*drvPath});
debug("building user environment dependencies");
state.store->buildPaths(
state.ctx.store->buildPaths(
toDerivedPaths(drvsToBuild),
state.repair ? bmRepair : bmNormal);
state.ctx.repair ? bmRepair : bmNormal);
/* Construct the whole top level derivation. */
StorePathSet references;
Value manifest;
state.mkList(manifest, elems.size());
Value manifest = state.ctx.mem.newList(elems.size());
size_t n = 0;
for (auto & i : elems) {
/* Create a pseudo-derivation containing the name, system,
output paths, and optionally the derivation path, as well
as the meta attributes. */
std::optional<StorePath> drvPath = keepDerivations ? i.queryDrvPath() : std::nullopt;
DrvInfo::Outputs outputs = i.queryOutputs(true, true);
StringSet metaNames = i.queryMetaNames();
std::optional<StorePath> drvPath = keepDerivations ? i.queryDrvPath(state) : std::nullopt;
DrvInfo::Outputs outputs = i.queryOutputs(state, true, true);
StringSet metaNames = i.queryMetaNames(state);
auto attrs = state.buildBindings(7 + outputs.size());
auto attrs = state.ctx.buildBindings(7 + outputs.size());
attrs.alloc(state.sType).mkString("derivation");
attrs.alloc(state.sName).mkString(i.queryName());
auto system = i.querySystem();
attrs.alloc(state.ctx.s.type).mkString("derivation");
attrs.alloc(state.ctx.s.name).mkString(i.queryName(state));
auto system = i.querySystem(state);
if (!system.empty())
attrs.alloc(state.sSystem).mkString(system);
attrs.alloc(state.sOutPath).mkString(state.store->printStorePath(i.queryOutPath()));
attrs.alloc(state.ctx.s.system).mkString(system);
attrs.alloc(state.ctx.s.outPath).mkString(state.ctx.store->printStorePath(i.queryOutPath(state)));
if (drvPath)
attrs.alloc(state.sDrvPath).mkString(state.store->printStorePath(*drvPath));
attrs.alloc(state.ctx.s.drvPath).mkString(state.ctx.store->printStorePath(*drvPath));
// Copy each output meant for installation.
auto & vOutputs = attrs.alloc(state.sOutputs);
state.mkList(vOutputs, outputs.size());
auto & vOutputs = attrs.alloc(state.ctx.s.outputs);
vOutputs = state.ctx.mem.newList(outputs.size());
for (const auto & [m, j] : enumerate(outputs)) {
(vOutputs.listElems()[m] = state.allocValue())->mkString(j.first);
auto outputAttrs = state.buildBindings(2);
outputAttrs.alloc(state.sOutPath).mkString(state.store->printStorePath(*j.second));
(vOutputs.listElems()[m] = state.ctx.mem.allocValue())->mkString(j.first);
auto outputAttrs = state.ctx.buildBindings(2);
outputAttrs.alloc(state.ctx.s.outPath).mkString(state.ctx.store->printStorePath(*j.second));
attrs.alloc(j.first).mkAttrs(outputAttrs);
/* This is only necessary when installing store paths, e.g.,
`nix-env -i /nix/store/abcd...-foo'. */
state.store->addTempRoot(*j.second);
state.store->ensurePath(*j.second);
state.ctx.store->addTempRoot(*j.second);
state.ctx.store->ensurePath(*j.second);
references.insert(*j.second);
}
// Copy the meta attributes.
auto meta = state.buildBindings(metaNames.size());
auto meta = state.ctx.buildBindings(metaNames.size());
for (auto & j : metaNames) {
Value * v = i.queryMeta(j);
Value * v = i.queryMeta(state, j);
if (!v) continue;
meta.insert(state.symbols.create(j), v);
meta.insert(state.ctx.symbols.create(j), v);
}
attrs.alloc(state.sMeta).mkAttrs(meta);
attrs.alloc(state.ctx.s.meta).mkAttrs(meta);
(manifest.listElems()[n++] = state.allocValue())->mkAttrs(attrs);
(manifest.listElems()[n++] = state.ctx.mem.allocValue())->mkAttrs(attrs);
if (drvPath) references.insert(*drvPath);
}
@@ -92,21 +91,21 @@ bool createUserEnv(EvalState & state, DrvInfos & elems,
the store; we need it for future modifications of the
environment. */
std::ostringstream str;
printAmbiguous(manifest, state.symbols, str, nullptr, std::numeric_limits<int>::max());
auto manifestFile = state.store->addTextToStore("env-manifest.nix",
printAmbiguous(manifest, state.ctx.symbols, str, nullptr, std::numeric_limits<int>::max());
auto manifestFile = state.ctx.store->addTextToStore("env-manifest.nix",
str.str(), references);
/* Get the environment builder expression. */
Value envBuilder;
state.eval(state.parseExprFromString(
state.eval(state.ctx.parseExprFromString(
#include "buildenv.nix.gen.hh"
, state.rootPath(CanonPath::root)), envBuilder);
, CanonPath::root), envBuilder);
/* Construct a Nix expression that calls the user environment
builder with the manifest as argument. */
auto attrs = state.buildBindings(3);
state.mkStorePathString(manifestFile, attrs.alloc("manifest"));
attrs.insert(state.symbols.create("derivations"), &manifest);
auto attrs = state.ctx.buildBindings(3);
state.ctx.paths.mkStorePathString(manifestFile, attrs.alloc("manifest"));
attrs.insert(state.ctx.symbols.create("derivations"), &manifest);
Value args;
args.mkAttrs(attrs);
@@ -117,21 +116,21 @@ bool createUserEnv(EvalState & state, DrvInfos & elems,
debug("evaluating user environment builder");
state.forceValue(topLevel, topLevel.determinePos(noPos));
NixStringContext context;
Attr & aDrvPath(*topLevel.attrs->find(state.sDrvPath));
Attr & aDrvPath(*topLevel.attrs->find(state.ctx.s.drvPath));
auto topLevelDrv = state.coerceToStorePath(aDrvPath.pos, *aDrvPath.value, context, "");
Attr & aOutPath(*topLevel.attrs->find(state.sOutPath));
Attr & aOutPath(*topLevel.attrs->find(state.ctx.s.outPath));
auto topLevelOut = state.coerceToStorePath(aOutPath.pos, *aOutPath.value, context, "");
/* Realise the resulting store expression. */
debug("building user environment");
std::vector<StorePathWithOutputs> topLevelDrvs;
topLevelDrvs.push_back({topLevelDrv});
state.store->buildPaths(
state.ctx.store->buildPaths(
toDerivedPaths(topLevelDrvs),
state.repair ? bmRepair : bmNormal);
state.ctx.repair ? bmRepair : bmNormal);
/* Switch the current user environment to the output path. */
auto store2 = state.store.dynamic_pointer_cast<LocalFSStore>();
auto store2 = state.ctx.store.dynamic_pointer_cast<LocalFSStore>();
if (store2) {
PathLocks lock;
@@ -1,12 +1,10 @@
#pragma once
///@file
#include "get-drvs.hh"
#include "lix/libexpr/get-drvs.hh"
namespace nix {
DrvInfos queryInstalled(EvalState & state, const Path & userEnv);
bool createUserEnv(EvalState & state, DrvInfos & elems,
const Path & profile, bool keepDerivations,
const std::string & lockToken);
@@ -1,6 +1,6 @@
#include "built-path.hh"
#include "derivations.hh"
#include "store-api.hh"
#include "lix/libcmd/built-path.hh"
#include "lix/libstore/derivations.hh"
#include "lix/libstore/store-api.hh"
#include <nlohmann/json.hpp>
@@ -1,7 +1,7 @@
#pragma once
///@file
#include "derived-path.hh"
#include "realisation.hh"
#include "lix/libstore/derived-path.hh"
#include "lix/libstore/realisation.hh"
namespace nix {
@@ -20,13 +20,15 @@ struct SingleBuiltPathBuilt {
DECLARE_CMP(SingleBuiltPathBuilt);
};
using _SingleBuiltPathRaw = std::variant<
namespace built_path::detail {
using SingleBuiltPathRaw = std::variant<
DerivedPathOpaque,
SingleBuiltPathBuilt
>;
}
struct SingleBuiltPath : _SingleBuiltPathRaw {
using Raw = _SingleBuiltPathRaw;
struct SingleBuiltPath : built_path::detail::SingleBuiltPathRaw {
using Raw = built_path::detail::SingleBuiltPathRaw;
using Raw::Raw;
using Opaque = DerivedPathOpaque;
@@ -65,17 +67,19 @@ struct BuiltPathBuilt {
DECLARE_CMP(BuiltPathBuilt);
};
using _BuiltPathRaw = std::variant<
namespace built_path::detail {
using BuiltPathRaw = std::variant<
DerivedPath::Opaque,
BuiltPathBuilt
>;
}
/**
* A built path. Similar to a DerivedPath, but enriched with the corresponding
* output path(s).
*/
struct BuiltPath : _BuiltPathRaw {
using Raw = _BuiltPathRaw;
struct BuiltPath : built_path::detail::BuiltPathRaw {
using Raw = built_path::detail::BuiltPathRaw;
using Raw::Raw;
using Opaque = DerivedPathOpaque;
@@ -1,12 +1,13 @@
#include <set>
#include "cmd-profiles.hh"
#include "built-path.hh"
#include "builtins/buildenv.hh"
#include "logging.hh"
#include "names.hh"
#include "store-api.hh"
#include "url-name.hh"
#include "lix/libcmd/cmd-profiles.hh"
#include "lix/libcmd/built-path.hh"
#include "lix/libstore/builtins/buildenv.hh"
#include "lix/libutil/logging.hh"
#include "lix/libstore/names.hh"
#include "lix/libstore/store-api.hh"
#include "lix/libstore/temporary-dir.hh"
#include "lix/libutil/url-name.hh"
namespace nix
{
@@ -19,8 +20,8 @@ DrvInfos queryInstalled(EvalState & state, const Path & userEnv)
auto manifestFile = userEnv + "/manifest.nix";
if (pathExists(manifestFile)) {
Value v;
state.evalFile(state.rootPath(CanonPath(manifestFile)), v);
Bindings & bindings(*state.allocBindings(0));
state.evalFile(CanonPath(manifestFile), v);
Bindings & bindings(*state.ctx.mem.allocBindings(0));
getDerivations(state, v, "", bindings, elems, false);
}
return elems;
@@ -135,7 +136,7 @@ ProfileManifest::ProfileManifest(EvalState & state, const Path & profile)
auto & e = elem.value();
ProfileElement element;
for (auto & p : e["storePaths"]) {
element.storePaths.insert(state.store->parseStorePath((std::string) p));
element.storePaths.insert(state.ctx.store->parseStorePath((std::string) p));
}
element.active = e["active"];
if (e.contains("priority")) {
@@ -161,14 +162,14 @@ ProfileManifest::ProfileManifest(EvalState & state, const Path & profile)
}
} else if (pathExists(profile + "/manifest.nix")) {
// FIXME: needed because of pure mode; ugly.
state.allowPath(state.store->followLinksToStore(profile));
state.allowPath(state.store->followLinksToStore(profile + "/manifest.nix"));
state.ctx.paths.allowPath(state.ctx.store->followLinksToStore(profile));
state.ctx.paths.allowPath(state.ctx.store->followLinksToStore(profile + "/manifest.nix"));
auto drvInfos = queryInstalled(state, state.store->followLinksToStore(profile));
auto drvInfos = queryInstalled(state, state.ctx.store->followLinksToStore(profile));
for (auto & drvInfo : drvInfos) {
ProfileElement element;
element.storePaths = {drvInfo.queryOutPath()};
element.storePaths = {drvInfo.queryOutPath(state)};
addElement(std::move(element));
}
}
@@ -1,13 +1,13 @@
#pragma once
///@file
#include "built-path.hh"
#include "eval.hh"
#include "flake/flakeref.hh"
#include "get-drvs.hh"
#include "types.hh"
#include "url.hh"
#include "url-name.hh"
#include "lix/libcmd/built-path.hh"
#include "lix/libexpr/eval.hh"
#include "lix/libexpr/flake/flakeref.hh"
#include "lix/libexpr/get-drvs.hh"
#include "lix/libutil/types.hh"
#include "lix/libutil/url.hh"
#include "lix/libutil/url-name.hh"
#include <string>
#include <set>
+14 -29
View File
@@ -1,10 +1,10 @@
#include "command.hh"
#include "store-api.hh"
#include "local-fs-store.hh"
#include "derivations.hh"
#include "nixexpr.hh"
#include "profiles.hh"
#include "repl.hh"
#include "lix/libcmd/command.hh"
#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>
@@ -28,12 +28,6 @@ nix::Commands RegisterCommand::getCommandsFor(const std::vector<std::string> & p
return res;
}
nlohmann::json NixMultiCommand::toJSON()
{
// FIXME: use Command::toJSON() as well.
return MultiCommand::toJSON();
}
StoreCommand::StoreCommand()
{
}
@@ -108,26 +102,17 @@ ref<Store> EvalCommand::getEvalStore()
return ref<Store>(evalStore);
}
ref<EvalState> EvalCommand::getEvalState()
ref<eval_cache::CachingEvaluator> EvalCommand::getEvaluator()
{
if (!evalState) {
evalState =
#if HAVE_BOEHMGC
std::allocate_shared<EvalState>(traceable_allocator<EvalState>(),
searchPath, getEvalStore(), getStore())
#else
std::make_shared<EvalState>(
searchPath, getEvalStore(), getStore())
#endif
;
evalState = std::allocate_shared<eval_cache::CachingEvaluator>(
TraceableAllocator<EvalState>(), searchPath, getEvalStore(), getStore(),
startReplOnEvalErrors ? AbstractNixRepl::runSimple : nullptr
);
evalState->repair = repair;
if (startReplOnEvalErrors) {
evalState->debugRepl = &AbstractNixRepl::runSimple;
};
}
return ref<EvalState>(evalState);
return ref<eval_cache::CachingEvaluator>(evalState);
}
MixOperateOnOptions::MixOperateOnOptions()
@@ -177,7 +162,7 @@ void BuiltPathsCommand::run(ref<Store> store, Installables && installables)
for (auto & p : store->queryAllValidPaths())
paths.emplace_back(BuiltPath::Opaque{p});
} else {
paths = Installable::toBuiltPaths(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
+17 -18
View File
@@ -1,11 +1,12 @@
#pragma once
///@file
#include "installable-value.hh"
#include "args.hh"
#include "common-eval-args.hh"
#include "path.hh"
#include "flake/lockfile.hh"
#include "lix/libcmd/installable-value.hh"
#include "lix/libexpr/eval-cache.hh"
#include "lix/libutil/args.hh"
#include "lix/libcmd/common-eval-args.hh"
#include "lix/libstore/path.hh"
#include "lix/libexpr/flake/lockfile.hh"
#include <optional>
@@ -26,11 +27,6 @@ static constexpr Command::Category catNixInstallation = 102;
static constexpr auto installablesCategory = "Options that change the interpretation of [installables](@docroot@/command-ref/new-cli/nix.md#installables)";
struct NixMultiCommand : virtual MultiCommand, virtual Command
{
nlohmann::json toJSON() override;
};
// For the overloaded run methods
#pragma GCC diagnostic ignored "-Woverloaded-virtual"
@@ -81,12 +77,12 @@ struct EvalCommand : virtual StoreCommand, MixEvalArgs
ref<Store> getEvalStore();
ref<EvalState> getEvalState();
virtual ref<eval_cache::CachingEvaluator> getEvaluator();
private:
std::shared_ptr<Store> evalStore;
std::shared_ptr<EvalState> evalState;
std::shared_ptr<eval_cache::CachingEvaluator> evalState;
};
/**
@@ -118,11 +114,13 @@ struct SourceExprCommand : virtual Args, MixFlakeOptions
SourceExprCommand();
ref<eval_cache::CachingEvaluator> getEvaluator() override;
Installables parseInstallables(
ref<Store> store, std::vector<std::string> ss);
EvalState & state, ref<Store> store, std::vector<std::string> ss);
ref<Installable> parseInstallable(
ref<Store> store, const std::string & installable);
EvalState & state, ref<Store> store, const std::string & installable);
virtual Strings getDefaultFlakeAttrPaths();
@@ -131,7 +129,7 @@ struct SourceExprCommand : virtual Args, MixFlakeOptions
/**
* Complete an installable from the given prefix.
*/
void completeInstallable(AddCompletions & completions, std::string_view prefix);
void completeInstallable(EvalState & state, AddCompletions & completions, std::string_view prefix);
/**
* Convenience wrapper around the underlying function to make setting the
@@ -232,7 +230,7 @@ protected:
public:
BuiltPathsCommand(bool recursive = false);
BuiltPathsCommand(bool recursive);
virtual void run(ref<Store> store, BuiltPaths && paths) = 0;
@@ -328,7 +326,7 @@ struct MixEnvironment : virtual Args {
void completeFlakeInputPath(
AddCompletions & completions,
ref<EvalState> evalState,
EvalState & evalState,
const std::vector<FlakeRef> & flakeRefs,
std::string_view prefix);
@@ -336,7 +334,8 @@ void completeFlakeRef(AddCompletions & completions, ref<Store> store, std::strin
void completeFlakeRefWithFragment(
AddCompletions & completions,
ref<EvalState> evalState,
EvalState & evalState,
ref<eval_cache::CachingEvaluator> evaluator,
flake::LockFlags lockFlags,
Strings attrPathPrefixes,
const Strings & defaultFlakeAttrPaths,
@@ -1,16 +1,32 @@
#include "eval-settings.hh"
#include "common-eval-args.hh"
#include "shared.hh"
#include "filetransfer.hh"
#include "eval.hh"
#include "fetchers.hh"
#include "registry.hh"
#include "flake/flakeref.hh"
#include "store-api.hh"
#include "command.hh"
#include "lix/libexpr/eval-settings.hh"
#include "lix/libcmd/common-eval-args.hh"
#include "lix/libmain/shared.hh"
#include "lix/libstore/filetransfer.hh"
#include "lix/libexpr/eval.hh"
#include "lix/libfetchers/fetchers.hh"
#include "lix/libfetchers/registry.hh"
#include "lix/libexpr/flake/flakeref.hh"
#include "lix/libstore/store-api.hh"
#include "lix/libcmd/command.hh"
#include <regex>
namespace nix {
static std::regex const identifierRegex("^[A-Za-z_][A-Za-z0-9_'-]*$");
static void warnInvalidNixIdentifier(const std::string & name)
{
std::smatch match;
if (!std::regex_match(name, match, identifierRegex)) {
warn("This Nix invocation specifies a value for argument '%s' which isn't a valid \
Nix identifier. The project is considering to drop support for this \
or to require quotes around args that aren't valid Nix identifiers. \
If you depend on this behvior, please reach out in \
https://git.lix.systems/lix-project/lix/issues/496 so we can discuss \
your use-case.", name);
}
}
MixEvalArgs::MixEvalArgs()
{
addFlag({
@@ -18,7 +34,10 @@ MixEvalArgs::MixEvalArgs()
.description = "Pass the value *expr* as the argument *name* to Nix functions.",
.category = category,
.labels = {"name", "expr"},
.handler = {[&](std::string name, std::string expr) { autoArgs[name] = 'E' + expr; }}
.handler = {[&](std::string name, std::string expr) {
warnInvalidNixIdentifier(name);
autoArgs[name] = 'E' + expr;
}}
});
addFlag({
@@ -26,7 +45,10 @@ MixEvalArgs::MixEvalArgs()
.description = "Pass the string *string* as the argument *name* to Nix functions.",
.category = category,
.labels = {"name", "string"},
.handler = {[&](std::string name, std::string s) { autoArgs[name] = 'S' + s; }},
.handler = {[&](std::string name, std::string s) {
warnInvalidNixIdentifier(name);
autoArgs[name] = 'S' + s;
}},
});
addFlag({
@@ -115,7 +137,7 @@ MixEvalArgs::MixEvalArgs()
.description = "Allow access to mutable paths and repositories.",
.category = category,
.handler = {[&]() {
evalSettings.pureEval = false;
evalSettings.pureEval.override(false);
}},
});
@@ -149,13 +171,15 @@ MixEvalArgs::MixEvalArgs()
});
}
Bindings * MixEvalArgs::getAutoArgs(EvalState & state)
Bindings * MixEvalArgs::getAutoArgs(Evaluator & state)
{
auto res = state.buildBindings(autoArgs.size());
for (auto & i : autoArgs) {
auto v = state.allocValue();
auto v = state.mem.allocValue();
if (i.second[0] == 'E')
state.mkThunk_(*v, state.parseExprFromString(i.second.substr(1), state.rootPath(CanonPath::fromCwd())));
state.evalLazily(
state.parseExprFromString(i.second.substr(1), CanonPath::fromCwd()), *v
);
else
v->mkString(((std::string_view) i.second).substr(1));
res.insert(state.symbols.create(i.first), v);
@@ -163,7 +187,7 @@ Bindings * MixEvalArgs::getAutoArgs(EvalState & state)
return res.finish();
}
SourcePath lookupFileArg(EvalState & state, std::string_view fileArg)
SourcePath lookupFileArg(Evaluator & state, std::string_view fileArg)
{
if (EvalSettings::isPseudoUrl(fileArg)) {
auto const url = EvalSettings::resolvePseudoUrl(fileArg);
@@ -174,18 +198,18 @@ SourcePath lookupFileArg(EvalState & state, std::string_view fileArg)
/* locked */ false
);
StorePath const storePath = downloaded.tree.storePath;
return state.rootPath(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 = flakeRef.resolve(state.store).fetchTree(state.store).first.storePath;
return state.rootPath(CanonPath(state.store->toRealPath(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));
return state.findFile(p);
return state.paths.findFile(p);
} else {
return state.rootPath(CanonPath::fromCwd(fileArg));
return CanonPath::fromCwd(fileArg);
}
}
@@ -1,9 +1,10 @@
#pragma once
///@file
#include "args.hh"
#include "common-args.hh"
#include "search-path.hh"
#include "lix/libexpr/eval.hh"
#include "lix/libutil/args.hh"
#include "lix/libmain/common-args.hh"
#include "lix/libexpr/search-path.hh"
namespace nix {
@@ -18,7 +19,7 @@ struct MixEvalArgs : virtual Args, virtual MixRepair
MixEvalArgs();
Bindings * getAutoArgs(EvalState & state);
Bindings * getAutoArgs(Evaluator & state);
SearchPath searchPath;
@@ -48,6 +49,6 @@ private:
*
* @exception nix::ThrownError for failed search path lookup. Probably others.
*/
SourcePath lookupFileArg(EvalState & state, std::string_view fileArg);
SourcePath lookupFileArg(Evaluator & state, std::string_view fileArg);
}
@@ -1,14 +1,12 @@
#include "editor-for.hh"
#include "environment-variables.hh"
#include "source-path.hh"
#include "lix/libcmd/editor-for.hh"
#include "lix/libutil/environment-variables.hh"
#include "lix/libutil/source-path.hh"
#include "lix/libutil/strings.hh"
namespace nix {
Strings editorFor(const SourcePath & file, uint32_t line)
{
auto path = file.getPhysicalPath();
if (!path)
throw Error("cannot open '%s' in an editor because it has no physical path", file);
auto editor = getEnv("EDITOR").value_or("cat");
auto args = tokenizeString<Strings>(editor);
if (line > 0 && (
@@ -17,7 +15,7 @@ Strings editorFor(const SourcePath & file, uint32_t line)
editor.find("vim") != std::string::npos ||
editor.find("kak") != std::string::npos))
args.push_back(fmt("+%d", line));
args.push_back(path->abs());
args.push_back(file.canonical().abs());
return args;
}
@@ -1,8 +1,8 @@
#pragma once
///@file
#include "types.hh"
#include "source-path.hh"
#include "lix/libutil/types.hh"
#include "lix/libutil/source-path.hh"
namespace nix {
@@ -1,18 +1,18 @@
#include "installable-attr-path.hh"
#include "outputs-spec.hh"
#include "command.hh"
#include "attr-path.hh"
#include "common-eval-args.hh"
#include "eval.hh"
#include "get-drvs.hh"
#include "flake/flake.hh"
#include "lix/libcmd/installable-attr-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/libexpr/eval.hh"
#include "lix/libexpr/get-drvs.hh"
#include "lix/libexpr/flake/flake.hh"
#include <nlohmann/json.hpp>
namespace nix {
InstallableAttrPath::InstallableAttrPath(
ref<EvalState> state,
ref<eval_cache::CachingEvaluator> state,
SourceExprCommand & cmd,
Value * v,
const std::string & attrPath,
@@ -26,41 +26,40 @@ InstallableAttrPath::InstallableAttrPath(
std::pair<Value *, PosIdx> InstallableAttrPath::toValue(EvalState & state)
{
auto [vRes, pos] = findAlongAttrPath(state, attrPath, *cmd.getAutoArgs(state), **v);
auto [vRes, pos] = findAlongAttrPath(state, attrPath, *cmd.getAutoArgs(*evaluator), **v);
state.forceValue(*vRes, pos);
return {vRes, pos};
}
DerivedPathsWithInfo InstallableAttrPath::toDerivedPaths()
DerivedPathsWithInfo InstallableAttrPath::toDerivedPaths(EvalState & state)
{
auto [v, pos] = toValue(*state);
auto [v, pos] = toValue(state);
if (std::optional derivedPathWithInfo = trySinglePathToDerivedPaths(
*v,
pos,
fmt("while evaluating the attribute '%s'", attrPath)))
state, *v, pos, fmt("while evaluating the attribute '%s'", attrPath)
))
{
return { *derivedPathWithInfo };
}
Bindings & autoArgs = *cmd.getAutoArgs(*state);
Bindings & autoArgs = *cmd.getAutoArgs(*evaluator);
DrvInfos drvInfos;
getDerivations(*state, *v, "", autoArgs, drvInfos, false);
getDerivations(state, *v, "", autoArgs, drvInfos, false);
// Backward compatibility hack: group results by drvPath. This
// helps keep .all output together.
std::map<StorePath, OutputsSpec> byDrvPath;
for (auto & drvInfo : drvInfos) {
auto drvPath = drvInfo.queryDrvPath();
auto drvPath = drvInfo.queryDrvPath(state);
if (!drvPath)
throw Error("'%s' is not a derivation", what());
auto newOutputs = std::visit(overloaded {
[&](const ExtendedOutputsSpec::Default & d) -> OutputsSpec {
std::set<std::string> outputsToInstall;
for (auto & output : drvInfo.queryOutputs(false, true))
for (auto & output : drvInfo.queryOutputs(state, false, true))
outputsToInstall.insert(output.first);
return OutputsSpec::Names { std::move(outputsToInstall) };
},
@@ -93,7 +92,7 @@ DerivedPathsWithInfo InstallableAttrPath::toDerivedPaths()
}
InstallableAttrPath InstallableAttrPath::parse(
ref<EvalState> state,
ref<eval_cache::CachingEvaluator> state,
SourceExprCommand & cmd,
Value * v,
std::string_view prefix,
@@ -1,11 +1,12 @@
#pragma once
///@file
#include "installable-value.hh"
#include "outputs-spec.hh"
#include "command.hh"
#include "common-eval-args.hh"
#include "eval.hh"
#include "lix/libcmd/installable-value.hh"
#include "lix/libexpr/eval-cache.hh"
#include "lix/libstore/outputs-spec.hh"
#include "lix/libcmd/command.hh"
#include "lix/libcmd/common-eval-args.hh"
#include "lix/libexpr/eval.hh"
#include <nlohmann/json.hpp>
@@ -19,7 +20,7 @@ class InstallableAttrPath : public InstallableValue
ExtendedOutputsSpec extendedOutputsSpec;
InstallableAttrPath(
ref<EvalState> state,
ref<eval_cache::CachingEvaluator> state,
SourceExprCommand & cmd,
Value * v,
const std::string & attrPath,
@@ -29,12 +30,12 @@ class InstallableAttrPath : public InstallableValue
std::pair<Value *, PosIdx> toValue(EvalState & state) override;
DerivedPathsWithInfo toDerivedPaths() override;
DerivedPathsWithInfo toDerivedPaths(EvalState & state) override;
public:
static InstallableAttrPath parse(
ref<EvalState> state,
ref<eval_cache::CachingEvaluator> state,
SourceExprCommand & cmd,
Value * v,
std::string_view prefix,
@@ -1,5 +1,5 @@
#include "installable-derived-path.hh"
#include "derivations.hh"
#include "lix/libcmd/installable-derived-path.hh"
#include "lix/libstore/derivations.hh"
namespace nix {
@@ -8,7 +8,7 @@ std::string InstallableDerivedPath::what() const
return derivedPath.to_string(*store);
}
DerivedPathsWithInfo InstallableDerivedPath::toDerivedPaths()
DerivedPathsWithInfo InstallableDerivedPath::toDerivedPaths(EvalState & state)
{
return {{
.path = derivedPath,
@@ -1,7 +1,7 @@
#pragma once
///@file
#include "installables.hh"
#include "lix/libcmd/installables.hh"
namespace nix {
@@ -16,7 +16,7 @@ struct InstallableDerivedPath : Installable
std::string what() const override;
DerivedPathsWithInfo toDerivedPaths() override;
DerivedPathsWithInfo toDerivedPaths(EvalState & state) override;
std::optional<StorePath> getStorePath() override;
@@ -1,21 +1,21 @@
#include "globals.hh"
#include "installable-flake.hh"
#include "installable-derived-path.hh"
#include "outputs-spec.hh"
#include "command.hh"
#include "attr-path.hh"
#include "common-eval-args.hh"
#include "derivations.hh"
#include "eval-inline.hh"
#include "eval.hh"
#include "get-drvs.hh"
#include "store-api.hh"
#include "shared.hh"
#include "flake/flake.hh"
#include "eval-cache.hh"
#include "url.hh"
#include "registry.hh"
#include "build-result.hh"
#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>
@@ -41,20 +41,6 @@ std::vector<std::string> InstallableFlake::getActualAttrPaths()
return res;
}
Value * InstallableFlake::getFlakeOutputs(EvalState & state, const flake::LockedFlake & lockedFlake)
{
auto vFlake = state.allocValue();
callFlake(state, lockedFlake, *vFlake);
auto aOutputs = vFlake->attrs->get(state.symbols.create("outputs"));
assert(aOutputs);
state.forceValue(*aOutputs->value, aOutputs->value->determinePos(noPos));
return aOutputs->value;
}
static std::string showAttrPaths(const std::vector<std::string> & paths)
{
std::string s;
@@ -67,7 +53,7 @@ static std::string showAttrPaths(const std::vector<std::string> & paths)
InstallableFlake::InstallableFlake(
SourceExprCommand * cmd,
ref<EvalState> state,
ref<eval_cache::CachingEvaluator> state,
FlakeRef && flakeRef,
std::string_view fragment,
ExtendedOutputsSpec extendedOutputsSpec,
@@ -85,23 +71,22 @@ InstallableFlake::InstallableFlake(
throw UsageError("'--arg' and '--argstr' are incompatible with flakes");
}
DerivedPathsWithInfo InstallableFlake::toDerivedPaths()
DerivedPathsWithInfo InstallableFlake::toDerivedPaths(EvalState & state)
{
Activity act(*logger, lvlTalkative, actUnknown, fmt("evaluating derivation '%s'", what()));
auto attr = getCursor(*state);
auto attr = getCursor(state);
auto attrPath = attr->getAttrPathStr();
auto attrPath = attr->getAttrPathStr(state);
if (!attr->isDerivation()) {
if (!attr->isDerivation(state)) {
// FIXME: use eval cache?
auto v = attr->forceValue();
auto v = attr->forceValue(state);
if (std::optional derivedPathWithInfo = trySinglePathToDerivedPaths(
v,
noPos,
fmt("while evaluating the flake output attribute '%s'", attrPath)))
state, v, noPos, fmt("while evaluating the flake output attribute '%s'", attrPath)
))
{
return { *derivedPathWithInfo };
} else {
@@ -109,19 +94,19 @@ DerivedPathsWithInfo InstallableFlake::toDerivedPaths()
"expected flake output attribute '%s' to be a derivation or path but found %s: %s",
attrPath,
showType(v),
ValuePrinter(*this->state, v, errorPrintOptions)
ValuePrinter(state, v, errorPrintOptions)
);
}
}
auto drvPath = attr->forceDerivation();
auto drvPath = attr->forceDerivation(state);
std::optional<NixInt::Inner> priority;
if (attr->maybeGetAttr(state->sOutputSpecified)) {
} else if (auto aMeta = attr->maybeGetAttr(state->sMeta)) {
if (auto aPriority = aMeta->maybeGetAttr("priority"))
priority = aPriority->getInt().value;
if (attr->maybeGetAttr(state, "outputSpecified")) {
} else if (auto aMeta = attr->maybeGetAttr(state, "meta")) {
if (auto aPriority = aMeta->maybeGetAttr(state, "priority"))
priority = aPriority->getInt(state).value;
}
return {{
@@ -130,14 +115,14 @@ DerivedPathsWithInfo InstallableFlake::toDerivedPaths()
.outputs = std::visit(overloaded {
[&](const ExtendedOutputsSpec::Default & d) -> OutputsSpec {
std::set<std::string> outputsToInstall;
if (auto aOutputSpecified = attr->maybeGetAttr(state->sOutputSpecified)) {
if (aOutputSpecified->getBool()) {
if (auto aOutputName = attr->maybeGetAttr("outputName"))
outputsToInstall = { aOutputName->getString() };
if (auto aOutputSpecified = attr->maybeGetAttr(state, "outputSpecified")) {
if (aOutputSpecified->getBool(state)) {
if (auto aOutputName = attr->maybeGetAttr(state, "outputName"))
outputsToInstall = { aOutputName->getString(state) };
}
} else if (auto aMeta = attr->maybeGetAttr(state->sMeta)) {
if (auto aOutputsToInstall = aMeta->maybeGetAttr("outputsToInstall"))
for (auto & s : aOutputsToInstall->getListOfStrings())
} else if (auto aMeta = attr->maybeGetAttr(state, "meta")) {
if (auto aOutputsToInstall = aMeta->maybeGetAttr(state, "outputsToInstall"))
for (auto & s : aOutputsToInstall->getListOfStrings(state))
outputsToInstall.insert(s);
}
@@ -159,20 +144,20 @@ DerivedPathsWithInfo InstallableFlake::toDerivedPaths()
},
ExtraPathInfoFlake::Flake {
.originalRef = flakeRef,
.lockedRef = getLockedFlake()->flake.lockedRef,
.lockedRef = getLockedFlake(state)->flake.lockedRef,
}),
}};
}
std::pair<Value *, PosIdx> InstallableFlake::toValue(EvalState & state)
{
return {&getCursor(state)->forceValue(), noPos};
return {&getCursor(state)->forceValue(state), noPos};
}
std::vector<ref<eval_cache::AttrCursor>>
InstallableFlake::getCursors(EvalState & state)
{
auto evalCache = openEvalCache(state, getLockedFlake());
auto evalCache = openEvalCache(*evaluator, getLockedFlake(state));
auto root = evalCache->getRoot();
@@ -184,7 +169,7 @@ InstallableFlake::getCursors(EvalState & state)
for (auto & attrPath : attrPaths) {
debug("trying flake output attribute '%s'", attrPath);
auto attr = root->findAlongAttrPath(parseAttrPath(state, attrPath));
auto attr = root->findAlongAttrPath(state, parseAttrPath(attrPath));
if (attr) {
res.push_back(ref(*attr));
} else {
@@ -202,20 +187,20 @@ InstallableFlake::getCursors(EvalState & state)
return res;
}
std::shared_ptr<flake::LockedFlake> InstallableFlake::getLockedFlake() const
std::shared_ptr<flake::LockedFlake> InstallableFlake::getLockedFlake(EvalState & state) const
{
if (!_lockedFlake) {
flake::LockFlags lockFlagsApplyConfig = lockFlags;
// FIXME why this side effect?
lockFlagsApplyConfig.applyNixConfig = true;
_lockedFlake = std::make_shared<flake::LockedFlake>(lockFlake(*state, flakeRef, lockFlagsApplyConfig));
_lockedFlake = std::make_shared<flake::LockedFlake>(lockFlake(state, flakeRef, lockFlagsApplyConfig));
}
return _lockedFlake;
}
FlakeRef InstallableFlake::nixpkgsFlakeRef() const
FlakeRef InstallableFlake::nixpkgsFlakeRef(EvalState & state) const
{
auto lockedFlake = getLockedFlake();
auto lockedFlake = getLockedFlake(state);
if (auto nixpkgsInput = lockedFlake->lockFile.findInput({"nixpkgs"})) {
if (auto lockedNode = std::dynamic_pointer_cast<const flake::LockedNode>(nixpkgsInput)) {
@@ -1,7 +1,8 @@
#pragma once
///@file
#include "installable-value.hh"
#include "lix/libcmd/installable-value.hh"
#include "lix/libexpr/eval.hh"
namespace nix {
@@ -40,7 +41,7 @@ struct InstallableFlake : InstallableValue
InstallableFlake(
SourceExprCommand * cmd,
ref<EvalState> state,
ref<eval_cache::CachingEvaluator> state,
FlakeRef && flakeRef,
std::string_view fragment,
ExtendedOutputsSpec extendedOutputsSpec,
@@ -52,9 +53,7 @@ struct InstallableFlake : InstallableValue
std::vector<std::string> getActualAttrPaths();
Value * getFlakeOutputs(EvalState & state, const flake::LockedFlake & lockedFlake);
DerivedPathsWithInfo toDerivedPaths() override;
DerivedPathsWithInfo toDerivedPaths(EvalState & state) override;
std::pair<Value *, PosIdx> toValue(EvalState & state) override;
@@ -62,12 +61,11 @@ struct InstallableFlake : InstallableValue
* Get a cursor to every attrpath in getActualAttrPaths() that
* exists. However if none exists, throw an exception.
*/
std::vector<ref<eval_cache::AttrCursor>>
getCursors(EvalState & state) override;
std::vector<ref<eval_cache::AttrCursor>> getCursors(EvalState & state) override;
std::shared_ptr<flake::LockedFlake> getLockedFlake() const;
std::shared_ptr<flake::LockedFlake> getLockedFlake(EvalState & state) const;
FlakeRef nixpkgsFlakeRef() const;
FlakeRef nixpkgsFlakeRef(EvalState & state) const;
};
/**
@@ -84,7 +82,7 @@ static inline FlakeRef defaultNixpkgsFlakeRef()
}
ref<eval_cache::EvalCache> openEvalCache(
EvalState & state,
eval_cache::CachingEvaluator & state,
std::shared_ptr<flake::LockedFlake> lockedFlake);
}
@@ -1,6 +1,6 @@
#include "installable-value.hh"
#include "eval-cache.hh"
#include "fetch-to-store.hh"
#include "lix/libcmd/installable-value.hh"
#include "lix/libexpr/eval-cache.hh"
#include "lix/libfetchers/fetch-to-store.hh"
namespace nix {
@@ -8,8 +8,8 @@ std::vector<ref<eval_cache::AttrCursor>>
InstallableValue::getCursors(EvalState & state)
{
auto evalCache =
std::make_shared<nix::eval_cache::EvalCache>(std::nullopt, state,
[&]() { return toValue(state).first; });
std::make_shared<nix::eval_cache::EvalCache>(std::nullopt,
[&](EvalState & state) { return toValue(state).first; });
return {evalCache->getRoot()};
}
@@ -42,10 +42,12 @@ ref<InstallableValue> InstallableValue::require(ref<Installable> installable)
return ref { castedInstallable };
}
std::optional<DerivedPathWithInfo> InstallableValue::trySinglePathToDerivedPaths(Value & v, const PosIdx pos, std::string_view errorCtx)
std::optional<DerivedPathWithInfo> InstallableValue::trySinglePathToDerivedPaths(
EvalState & state, Value & v, const PosIdx pos, std::string_view errorCtx
)
{
if (v.type() == nPath) {
auto storePath = fetchToStore(*state->store, v.path());
auto storePath = fetchToStore(*evaluator->store, state.ctx.paths.checkSourcePath(v.path()));
return {{
.path = DerivedPath::Opaque {
.path = std::move(storePath),
@@ -57,7 +59,7 @@ std::optional<DerivedPathWithInfo> InstallableValue::trySinglePathToDerivedPaths
else if (v.type() == nString) {
return {{
.path = DerivedPath::fromSingle(
state->coerceToSingleDerivedPath(pos, v, errorCtx)),
state.coerceToSingleDerivedPath(pos, v, errorCtx)),
.info = make_ref<ExtraPathInfo>(),
}};
}
@@ -1,16 +1,16 @@
#pragma once
///@file
#include "installables.hh"
#include "flake/flake.hh"
#include "lix/libcmd/installables.hh"
#include "lix/libexpr/eval-cache.hh"
#include "lix/libexpr/eval.hh"
#include "lix/libexpr/flake/flake.hh"
namespace nix {
struct DrvInfo;
struct SourceExprCommand;
namespace eval_cache { class EvalCache; class AttrCursor; }
struct App
{
std::vector<DerivedPath> context;
@@ -21,7 +21,7 @@ struct App
struct UnresolvedApp
{
App unresolved;
App resolve(ref<Store> evalStore, ref<Store> store);
App resolve(EvalState & state, ref<Store> evalStore, ref<Store> store);
};
/**
@@ -71,9 +71,9 @@ struct ExtraPathInfoValue : ExtraPathInfo
*/
struct InstallableValue : Installable
{
ref<EvalState> state;
ref<eval_cache::CachingEvaluator> evaluator;
InstallableValue(ref<EvalState> state) : state(state) {}
InstallableValue(ref<eval_cache::CachingEvaluator> evaluator) : evaluator(evaluator) {}
virtual ~InstallableValue() { }
@@ -84,15 +84,13 @@ struct InstallableValue : Installable
* However if none exists, throw exception instead of returning
* empty vector.
*/
virtual std::vector<ref<eval_cache::AttrCursor>>
getCursors(EvalState & state);
virtual std::vector<ref<eval_cache::AttrCursor>> getCursors(EvalState & state);
/**
* Get the first and most preferred cursor this Installable could
* refer to, or throw an exception if none exists.
*/
virtual ref<eval_cache::AttrCursor>
getCursor(EvalState & state);
virtual ref<eval_cache::AttrCursor> getCursor(EvalState & state);
UnresolvedApp toApp(EvalState & state);
@@ -115,7 +113,9 @@ protected:
* @result A derived path (with empty info, for now) if the value
* matched the above criteria.
*/
std::optional<DerivedPathWithInfo> trySinglePathToDerivedPaths(Value & v, const PosIdx pos, std::string_view errorCtx);
std::optional<DerivedPathWithInfo> trySinglePathToDerivedPaths(
EvalState & state, Value & v, const PosIdx pos, std::string_view errorCtx
);
};
}
@@ -1,22 +1,22 @@
#include "globals.hh"
#include "installables.hh"
#include "installable-derived-path.hh"
#include "installable-attr-path.hh"
#include "installable-flake.hh"
#include "logging.hh"
#include "outputs-spec.hh"
#include "command.hh"
#include "attr-path.hh"
#include "common-eval-args.hh"
#include "derivations.hh"
#include "eval.hh"
#include "eval-settings.hh"
#include "store-api.hh"
#include "shared.hh"
#include "flake/flake.hh"
#include "eval-cache.hh"
#include "registry.hh"
#include "build-result.hh"
#include "lix/libstore/globals.hh"
#include "lix/libcmd/installables.hh"
#include "lix/libcmd/installable-derived-path.hh"
#include "lix/libcmd/installable-attr-path.hh"
#include "lix/libcmd/installable-flake.hh"
#include "lix/libutil/logging.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.hh"
#include "lix/libexpr/eval-settings.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/libfetchers/registry.hh"
#include "lix/libstore/build-result.hh"
#include <nlohmann/json.hpp>
@@ -25,12 +25,12 @@ namespace nix {
void completeFlakeInputPath(
AddCompletions & completions,
ref<EvalState> evalState,
EvalState & evalState,
const std::vector<FlakeRef> & flakeRefs,
std::string_view prefix)
{
for (auto & flakeRef : flakeRefs) {
auto flake = flake::getFlake(*evalState, flakeRef, true);
auto flake = flake::getFlake(evalState, flakeRef, true);
for (auto & input : flake.inputs)
if (input.first.starts_with(prefix))
completions.add(input.first);
@@ -86,9 +86,9 @@ MixFlakeOptions::MixFlakeOptions()
}},
.completer = {[&](AddCompletions & completions, size_t n, std::string_view prefix) {
if (n == 0) {
completeFlakeInputPath(completions, getEvalState(), getFlakeRefsForCompletion(), prefix);
completeFlakeInputPath(completions, *getEvaluator()->begin(), getFlakeRefsForCompletion(), prefix);
} else if (n == 1) {
completeFlakeRef(completions, getEvalState()->store, prefix);
completeFlakeRef(completions, getEvaluator()->store, prefix);
}
}}
});
@@ -121,7 +121,7 @@ MixFlakeOptions::MixFlakeOptions()
.category = category,
.labels = {"flake-url"},
.handler = {[&](std::string flakeRef) {
auto evalState = getEvalState();
auto evalState = getEvaluator()->begin();
auto flake = flake::lockFlake(
*evalState,
parseFlakeRef(flakeRef, absPath(".")),
@@ -137,7 +137,7 @@ MixFlakeOptions::MixFlakeOptions()
}
}},
.completer = {[&](AddCompletions & completions, size_t, std::string_view prefix) {
completeFlakeRef(completions, getEvalState()->store, prefix);
completeFlakeRef(completions, getEvaluator()->store, prefix);
}}
});
}
@@ -202,26 +202,26 @@ Strings SourceExprCommand::getDefaultFlakeAttrPathPrefixes()
Args::CompleterClosure SourceExprCommand::getCompleteInstallable()
{
return [this](AddCompletions & completions, size_t, std::string_view prefix) {
completeInstallable(completions, prefix);
completeInstallable(*getEvaluator()->begin(), completions, prefix);
};
}
void SourceExprCommand::completeInstallable(AddCompletions & completions, std::string_view prefix)
void SourceExprCommand::completeInstallable(EvalState & state, AddCompletions & completions, std::string_view prefix)
{
try {
if (file) {
completions.setType(AddCompletions::Type::Attrs);
evalSettings.pureEval = false;
auto state = getEvalState();
Expr & e = state->parseExprFromFile(
resolveExprPath(state->checkSourcePath(lookupFileArg(*state, *file)))
);
auto evaluator = getEvaluator();
Expr & e = evaluator->parseExprFromFile(
state.ctx.paths.resolveExprPath(lookupFileArg(*evaluator, *file))
);
Value root;
state->eval(e, root);
state.eval(e, root);
auto autoArgs = getAutoArgs(*state);
auto autoArgs = getAutoArgs(*evaluator);
std::string prefix_ = std::string(prefix);
auto sep = prefix_.rfind('.');
@@ -234,15 +234,15 @@ void SourceExprCommand::completeInstallable(AddCompletions & completions, std::s
prefix_ = "";
}
auto [v, pos] = findAlongAttrPath(*state, prefix_, *autoArgs, root);
auto [v, pos] = findAlongAttrPath(state, prefix_, *autoArgs, root);
Value &v1(*v);
state->forceValue(v1, pos);
state.forceValue(v1, pos);
Value v2;
state->autoCallFunction(*autoArgs, v1, v2);
state.autoCallFunction(*autoArgs, v1, v2);
if (v2.type() == nAttrs) {
for (auto & i : *v2.attrs) {
std::string name = state->symbols[i.name];
std::string name = evaluator->symbols[i.name];
if (name.find(searchWord) == 0) {
if (prefix_ == "")
completions.add(name);
@@ -254,7 +254,8 @@ void SourceExprCommand::completeInstallable(AddCompletions & completions, std::s
} else {
completeFlakeRefWithFragment(
completions,
getEvalState(),
state,
getEvaluator(),
lockFlags,
getDefaultFlakeAttrPathPrefixes(),
getDefaultFlakeAttrPaths(),
@@ -267,7 +268,8 @@ void SourceExprCommand::completeInstallable(AddCompletions & completions, std::s
void completeFlakeRefWithFragment(
AddCompletions & completions,
ref<EvalState> evalState,
EvalState & evalState,
ref<eval_cache::CachingEvaluator> evaluator,
flake::LockFlags lockFlags,
Strings attrPathPrefixes,
const Strings & defaultFlakeAttrPaths,
@@ -278,7 +280,7 @@ void completeFlakeRefWithFragment(
try {
auto hash = prefix.find('#');
if (hash == std::string::npos) {
completeFlakeRef(completions, evalState->store, prefix);
completeFlakeRef(completions, evaluator->store, prefix);
} else {
completions.setType(AddCompletions::Type::Attrs);
@@ -291,8 +293,10 @@ void completeFlakeRefWithFragment(
auto flakeRefS = std::string(prefix.substr(0, hash));
auto flakeRef = parseFlakeRef(expandTilde(flakeRefS), absPath("."));
auto evalCache = openEvalCache(*evalState,
std::make_shared<flake::LockedFlake>(lockFlake(*evalState, flakeRef, lockFlags)));
auto evalCache = openEvalCache(
*evaluator,
std::make_shared<flake::LockedFlake>(lockFlake(evalState, flakeRef, lockFlags))
);
auto root = evalCache->getRoot();
@@ -305,25 +309,25 @@ void completeFlakeRefWithFragment(
attrPathPrefixes.push_back("");
for (auto & attrPathPrefixS : attrPathPrefixes) {
auto attrPathPrefix = parseAttrPath(*evalState, attrPathPrefixS);
auto attrPathPrefix = parseAttrPath(attrPathPrefixS);
auto attrPathS = attrPathPrefixS + std::string(fragment);
auto attrPath = parseAttrPath(*evalState, attrPathS);
auto attrPath = parseAttrPath(attrPathS);
std::string lastAttr;
if (!attrPath.empty() && !attrPathS.ends_with(".")) {
lastAttr = evalState->symbols[attrPath.back()];
lastAttr = attrPath.back();
attrPath.pop_back();
}
auto attr = root->findAlongAttrPath(attrPath);
auto attr = root->findAlongAttrPath(evalState, attrPath);
if (!attr) continue;
for (auto & attr2 : (*attr)->getAttrs()) {
if (std::string_view(evalState->symbols[attr2]).starts_with(lastAttr)) {
auto attrPath2 = (*attr)->getAttrPath(attr2);
for (auto & attr2 : (*attr)->getAttrs(evalState)) {
if (std::string_view attr2s = attr2; attr2s.starts_with(lastAttr)) {
auto attrPath2 = (*attr)->getAttrPath(evalState, attr2s);
/* Strip the attrpath prefix. */
attrPath2.erase(attrPath2.begin(), attrPath2.begin() + attrPathPrefix.size());
completions.add(flakeRefS + "#" + prefixRoot + concatStringsSep(".", evalState->symbols.resolve(attrPath2)));
completions.add(flakeRefS + "#" + prefixRoot + concatStringsSep(".", attrPath2));
}
}
}
@@ -332,7 +336,7 @@ void completeFlakeRefWithFragment(
attrpaths. */
if (fragment.empty()) {
for (auto & attrPath : defaultFlakeAttrPaths) {
auto attr = root->findAlongAttrPath(parseAttrPath(*evalState, attrPath));
auto attr = root->findAlongAttrPath(evalState, parseAttrPath(attrPath));
if (!attr) continue;
completions.add(flakeRefS + "#" + prefixRoot);
}
@@ -369,9 +373,9 @@ void completeFlakeRef(AddCompletions & completions, ref<Store> store, std::strin
}
}
DerivedPathWithInfo Installable::toDerivedPath()
DerivedPathWithInfo Installable::toDerivedPath(EvalState & state)
{
auto buildables = toDerivedPaths();
auto buildables = toDerivedPaths(state);
if (buildables.size() != 1)
throw Error("installable '%s' evaluates to %d derivations, where only one is expected", what(), buildables.size());
return std::move(buildables[0]);
@@ -390,43 +394,49 @@ static StorePath getDeriver(
}
ref<eval_cache::EvalCache> openEvalCache(
EvalState & state,
eval_cache::CachingEvaluator & state,
std::shared_ptr<flake::LockedFlake> lockedFlake)
{
auto fingerprint = evalSettings.useEvalCache && evalSettings.pureEval
? std::make_optional(lockedFlake->getFingerprint())
: std::nullopt;
auto rootLoader = [&state, lockedFlake]()
auto rootLoader = [lockedFlake](EvalState & state)
{
/* For testing whether the evaluation cache is
complete. */
if (getEnv("NIX_ALLOW_EVAL").value_or("1") == "0")
throw Error("not everything is cached, but evaluation is not allowed");
auto vFlake = state.allocValue();
auto vFlake = state.ctx.mem.allocValue();
flake::callFlake(state, *lockedFlake, *vFlake);
state.forceAttrs(*vFlake, noPos, "while parsing cached flake data");
auto aOutputs = vFlake->attrs->get(state.symbols.create("outputs"));
auto aOutputs = vFlake->attrs->get(state.ctx.symbols.create("outputs"));
assert(aOutputs);
return aOutputs->value;
};
if (fingerprint) {
auto search = state.evalCaches.find(fingerprint.value());
if (search == state.evalCaches.end()) {
search = state.evalCaches.emplace(fingerprint.value(), make_ref<nix::eval_cache::EvalCache>(fingerprint, state, rootLoader)).first;
}
return search->second;
return state.getCacheFor(fingerprint.value(), rootLoader);
} else {
return make_ref<nix::eval_cache::EvalCache>(std::nullopt, state, rootLoader);
return make_ref<nix::eval_cache::EvalCache>(std::nullopt, rootLoader);
}
}
ref<eval_cache::CachingEvaluator> SourceExprCommand::getEvaluator()
{
// FIXME: backward compatibility hack
if (file) {
evalSettings.pureEval.override(false);
}
return EvalCommand::getEvaluator();
}
Installables SourceExprCommand::parseInstallables(
ref<Store> store, std::vector<std::string> ss)
EvalState & state, ref<Store> store, std::vector<std::string> ss)
{
Installables result;
@@ -434,21 +444,18 @@ Installables SourceExprCommand::parseInstallables(
if (file && expr)
throw UsageError("'--file' and '--expr' are exclusive");
// FIXME: backward compatibility hack
if (file) evalSettings.pureEval = false;
auto state = getEvalState();
auto vFile = state->allocValue();
auto evaluator = getEvaluator();
auto vFile = evaluator->mem.allocValue();
if (file == "-") {
auto & e = state->parseStdin();
state->eval(e, *vFile);
auto & e = evaluator->parseStdin();
state.eval(e, *vFile);
}
else if (file)
state->evalFile(lookupFileArg(*state, *file), *vFile);
state.evalFile(lookupFileArg(*evaluator, *file), *vFile);
else {
auto & e = state->parseExprFromString(*expr, state->rootPath(CanonPath::fromCwd()));
state->eval(e, *vFile);
auto & e = evaluator->parseExprFromString(*expr, CanonPath::fromCwd());
state.eval(e, *vFile);
}
for (auto & s : ss) {
@@ -456,7 +463,7 @@ Installables SourceExprCommand::parseInstallables(
result.push_back(
make_ref<InstallableAttrPath>(
InstallableAttrPath::parse(
state, *this, vFile, std::move(prefix), std::move(extendedOutputsSpec))));
evaluator, *this, vFile, std::move(prefix), std::move(extendedOutputsSpec))));
}
} else {
@@ -485,7 +492,7 @@ Installables SourceExprCommand::parseInstallables(
auto [flakeRef, fragment] = parseFlakeRefWithFragment(std::string { prefix }, absPath("."));
result.push_back(make_ref<InstallableFlake>(
this,
getEvalState(),
getEvaluator(),
std::move(flakeRef),
fragment,
std::move(extendedOutputsSpec),
@@ -505,9 +512,9 @@ Installables SourceExprCommand::parseInstallables(
}
ref<Installable> SourceExprCommand::parseInstallable(
ref<Store> store, const std::string & installable)
EvalState & state, ref<Store> store, const std::string & installable)
{
auto installables = parseInstallables(store, {installable});
auto installables = parseInstallables(state, store, {installable});
assert(installables.size() == 1);
return installables.front();
}
@@ -537,6 +544,7 @@ static SingleBuiltPath getBuiltPath(ref<Store> evalStore, ref<Store> store, cons
}
std::vector<BuiltPathWithResult> Installable::build(
EvalState & state,
ref<Store> evalStore,
ref<Store> store,
Realise mode,
@@ -544,7 +552,7 @@ std::vector<BuiltPathWithResult> Installable::build(
BuildMode bMode)
{
std::vector<BuiltPathWithResult> res;
for (auto & [_, builtPathWithResult] : build2(evalStore, store, mode, installables, bMode))
for (auto & [_, builtPathWithResult] : build2(state, evalStore, store, mode, installables, bMode))
res.push_back(builtPathWithResult);
return res;
}
@@ -581,6 +589,7 @@ static void throwBuildErrors(
}
std::vector<std::pair<ref<Installable>, BuiltPathWithResult>> Installable::build2(
EvalState & state,
ref<Store> evalStore,
ref<Store> store,
Realise mode,
@@ -600,7 +609,7 @@ std::vector<std::pair<ref<Installable>, BuiltPathWithResult>> Installable::build
std::map<DerivedPath, std::vector<Aux>> backmap;
for (auto & i : installables) {
for (auto b : i->toDerivedPaths()) {
for (auto b : i->toDerivedPaths(state)) {
pathsToBuild.push_back(b.path);
backmap[b.path].push_back({.info = b.info, .installable = i});
}
@@ -679,6 +688,7 @@ std::vector<std::pair<ref<Installable>, BuiltPathWithResult>> Installable::build
}
BuiltPaths Installable::toBuiltPaths(
EvalState & state,
ref<Store> evalStore,
ref<Store> store,
Realise mode,
@@ -687,7 +697,7 @@ BuiltPaths Installable::toBuiltPaths(
{
if (operateOn == OperateOn::Output) {
BuiltPaths res;
for (auto & p : Installable::build(evalStore, store, mode, installables))
for (auto & p : Installable::build(state, evalStore, store, mode, installables))
res.push_back(p.path);
return res;
} else {
@@ -695,20 +705,21 @@ BuiltPaths Installable::toBuiltPaths(
settings.readOnlyMode = true;
BuiltPaths res;
for (auto & drvPath : Installable::toDerivations(store, installables, true))
for (auto & drvPath : Installable::toDerivations(state, store, installables, true))
res.emplace_back(BuiltPath::Opaque{drvPath});
return res;
}
}
StorePathSet Installable::toStorePathSet(
EvalState & state,
ref<Store> evalStore,
ref<Store> store,
Realise mode, OperateOn operateOn,
const Installables & installables)
{
StorePathSet outPaths;
for (auto & path : toBuiltPaths(evalStore, store, mode, operateOn, installables)) {
for (auto & path : toBuiltPaths(state, evalStore, store, mode, operateOn, installables)) {
auto thisOutPaths = path.outPaths();
outPaths.insert(thisOutPaths.begin(), thisOutPaths.end());
}
@@ -716,13 +727,14 @@ StorePathSet Installable::toStorePathSet(
}
StorePaths Installable::toStorePaths(
EvalState & state,
ref<Store> evalStore,
ref<Store> store,
Realise mode, OperateOn operateOn,
const Installables & installables)
{
StorePaths outPaths;
for (auto & path : toBuiltPaths(evalStore, store, mode, operateOn, installables)) {
for (auto & path : toBuiltPaths(state, evalStore, store, mode, operateOn, installables)) {
auto thisOutPaths = path.outPaths();
outPaths.insert(outPaths.end(), thisOutPaths.begin(), thisOutPaths.end());
}
@@ -730,12 +742,13 @@ StorePaths Installable::toStorePaths(
}
StorePath Installable::toStorePath(
EvalState & state,
ref<Store> evalStore,
ref<Store> store,
Realise mode, OperateOn operateOn,
ref<Installable> installable)
{
auto paths = toStorePathSet(evalStore, store, mode, operateOn, {installable});
auto paths = toStorePathSet(state, evalStore, store, mode, operateOn, {installable});
if (paths.size() != 1)
throw Error("argument '%s' should evaluate to one store path", installable->what());
@@ -744,6 +757,7 @@ StorePath Installable::toStorePath(
}
StorePathSet Installable::toDerivations(
EvalState & state,
ref<Store> store,
const Installables & installables,
bool useDeriver)
@@ -751,7 +765,7 @@ StorePathSet Installable::toDerivations(
StorePathSet drvPaths;
for (const auto & i : installables)
for (const auto & b : i->toDerivedPaths())
for (const auto & b : i->toDerivedPaths(state))
std::visit(overloaded {
[&](const DerivedPath::Opaque & bo) {
drvPaths.insert(
@@ -828,7 +842,7 @@ std::vector<FlakeRef> InstallableCommand::getFlakeRefsForCompletion()
void InstallablesCommand::run(ref<Store> store, std::vector<std::string> && rawInstallables)
{
auto installables = parseInstallables(store, rawInstallables);
auto installables = parseInstallables(*getEvaluator()->begin(), store, rawInstallables);
run(store, std::move(installables));
}
@@ -845,7 +859,7 @@ InstallableCommand::InstallableCommand()
void InstallableCommand::run(ref<Store> store)
{
auto installable = parseInstallable(store, _installable);
auto installable = parseInstallable(*getEvaluator()->begin(), store, _installable);
run(store, std::move(installable));
}
@@ -1,12 +1,13 @@
#pragma once
///@file
#include "path.hh"
#include "outputs-spec.hh"
#include "derived-path.hh"
#include "built-path.hh"
#include "store-api.hh"
#include "build-result.hh"
#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"
#include "lix/libstore/build-result.hh"
#include <optional>
@@ -127,7 +128,7 @@ struct Installable
*
* This is the main method of this class
*/
virtual DerivedPathsWithInfo toDerivedPaths() = 0;
virtual DerivedPathsWithInfo toDerivedPaths(EvalState & state) = 0;
/**
* A convenience wrapper of the above for when we expect an
@@ -137,7 +138,7 @@ struct Installable
* If no or multiple \ref DerivedPath "derived paths" are produced,
* and error is raised.
*/
DerivedPathWithInfo toDerivedPath();
DerivedPathWithInfo toDerivedPath(EvalState & state);
/**
* Return a value only if this installable is a store path or a
@@ -152,6 +153,7 @@ struct Installable
}
static std::vector<BuiltPathWithResult> build(
EvalState & state,
ref<Store> evalStore,
ref<Store> store,
Realise mode,
@@ -159,6 +161,7 @@ struct Installable
BuildMode bMode = bmNormal);
static std::vector<std::pair<ref<Installable>, BuiltPathWithResult>> build2(
EvalState & state,
ref<Store> evalStore,
ref<Store> store,
Realise mode,
@@ -166,6 +169,7 @@ struct Installable
BuildMode bMode = bmNormal);
static std::set<StorePath> toStorePathSet(
EvalState & state,
ref<Store> evalStore,
ref<Store> store,
Realise mode,
@@ -173,6 +177,7 @@ struct Installable
const Installables & installables);
static std::vector<StorePath> toStorePaths(
EvalState & state,
ref<Store> evalStore,
ref<Store> store,
Realise mode,
@@ -180,6 +185,7 @@ struct Installable
const Installables & installables);
static StorePath toStorePath(
EvalState & state,
ref<Store> evalStore,
ref<Store> store,
Realise mode,
@@ -187,11 +193,13 @@ struct Installable
ref<Installable> installable);
static std::set<StorePath> toDerivations(
EvalState & state,
ref<Store> store,
const Installables & installables,
bool useDeriver = false);
static BuiltPaths toBuiltPaths(
EvalState & state,
ref<Store> evalStore,
ref<Store> store,
Realise mode,
+7
View File
@@ -0,0 +1,7 @@
#include "lix/libcmd/legacy.hh"
namespace nix {
LegacyCommands::Commands * LegacyCommands::commands = 0;
}
@@ -2,19 +2,20 @@
///@file
#include <functional>
#include <list>
#include <map>
#include <string>
namespace nix {
typedef std::function<void(int, char * *)> MainFunction;
typedef std::function<void(std::string, std::list<std::string>)> MainFunction;
struct RegisterLegacyCommand
struct LegacyCommands
{
typedef std::map<std::string, MainFunction> Commands;
static Commands * commands;
RegisterLegacyCommand(const std::string & name, MainFunction fun)
static void add(const std::string & name, MainFunction fun)
{
if (!commands) commands = new Commands;
(*commands)[name] = fun;
@@ -7,4 +7,3 @@ Description: Lix Package Manager (libcmd)
Version: @PACKAGE_VERSION@
Requires: lix-base lix-util
Libs: -L${libdir} -llixcmd
Cflags: -I${includedir}/lix/libcmd
@@ -1,7 +1,7 @@
#include "markdown.hh"
#include "error.hh"
#include "finally.hh"
#include "terminal.hh"
#include "lix/libcmd/markdown.hh"
#include "lix/libutil/error.hh"
#include "lix/libutil/finally.hh"
#include "lix/libutil/terminal.hh"
#include <sys/queue.h>
#include <lowdown.h>
@@ -11,28 +11,13 @@ namespace nix {
std::string renderMarkdownToTerminal(std::string_view markdown)
{
int windowWidth = getWindowSize().second;
size_t lowdown_cols = std::max(windowWidth - 5, 60);
struct lowdown_opts opts{
struct lowdown_opts opts {
.type = LOWDOWN_TERM,
#ifdef LOWDOWN_SEPARATE_TERM_OPTS
.term =
{
.cols = lowdown_cols,
.width = 0,
.hmargin = 0,
.hpadding = 4,
.vmargin = 0,
.centre = 0,
},
// maxdepth needs to be part of the ifdefs to match declaration order
.maxdepth = 20,
#else
.maxdepth = 20,
.cols = lowdown_cols,
.cols = (size_t) std::max(windowWidth - 5, 60),
.hmargin = 0,
.vmargin = 0,
#endif /* LOWDOWN_SEPARATE_TERM_OPTS */
.feat = LOWDOWN_COMMONMARK | LOWDOWN_FENCED | LOWDOWN_DEFLIST | LOWDOWN_TABLES,
.oflags = LOWDOWN_TERM_NOLINK,
};
@@ -1,7 +1,7 @@
#pragma once
///@file
#include "types.hh"
#include "lix/libutil/types.hh"
namespace nix {
@@ -47,11 +47,14 @@ libcmd = library(
liblixfetchers,
liblixmain,
boehm,
ncurses,
editline,
lowdown,
nlohmann_json,
lix_doc
liblix_doc,
],
# '../..' for self references like "lix/libcmd/*.hh"
include_directories : [ '../..' ],
cpp_pch : cpp_pch,
install : true,
# FIXME(Qyriad): is this right?
@@ -68,7 +71,7 @@ custom_target(
)
liblixcmd = declare_dependency(
include_directories : '.',
include_directories : include_directories('../..'),
link_with : libcmd,
)
@@ -1,6 +1,6 @@
#include "error.hh"
#include "file-system.hh"
#include "logging.hh"
#include "lix/libutil/error.hh"
#include "lix/libutil/file-system.hh"
#include "lix/libutil/logging.hh"
#include <csignal>
#include <cstdio>
#include <iostream>
@@ -8,10 +8,6 @@
#include <string_view>
#include <cerrno>
#ifdef READLINE
#include <readline/history.h>
#include <readline/readline.h>
#else
// editline < 1.15.2 don't wrap their API for C++ usage
// (added in https://github.com/troglobit/editline/commit/91398ceb3427b730995357e9d120539fb9bb7461).
// This results in linker errors due to to name-mangling of editline C symbols.
@@ -20,10 +16,10 @@
extern "C" {
#include <editline.h>
}
#endif
#include "finally.hh"
#include "repl-interacter.hh"
#include "lix/libutil/finally.hh"
#include "lix/libutil/strings.hh"
#include "lix/libcmd/repl-interacter.hh"
namespace nix {
@@ -35,52 +31,14 @@ void sigintHandler(int signo)
{
g_signal_received = signo;
}
};
static detail::ReplCompleterMixin * curRepl; // ugly
static char * completionCallback(char * s, int * match)
/**
* @return a null-terminated list of completions as expected by `el_print_columns`
*/
char ** copyCompletions(const StringSet& possible)
{
auto possible = curRepl->completePrefix(s);
if (possible.size() == 1) {
*match = 1;
auto * res = strdup(possible.begin()->c_str() + strlen(s));
if (!res)
throw Error("allocation failure");
return res;
} else if (possible.size() > 1) {
auto checkAllHaveSameAt = [&](size_t pos) {
auto & first = *possible.begin();
for (auto & p : possible) {
if (p.size() <= pos || p[pos] != first[pos])
return false;
}
return true;
};
size_t start = strlen(s);
size_t len = 0;
while (checkAllHaveSameAt(start + len))
++len;
if (len > 0) {
*match = 1;
auto * res = strdup(std::string(*possible.begin(), start, len).c_str());
if (!res)
throw Error("allocation failure");
return res;
}
}
*match = 0;
return nullptr;
}
static int listPossibleCallback(char * s, char *** avp)
{
auto possible = curRepl->completePrefix(s);
if (possible.size() > (INT_MAX / sizeof(char *)))
throw Error("too many completions");
int ac = 0;
char ** vp = nullptr;
@@ -95,15 +53,95 @@ static int listPossibleCallback(char * s, char *** avp)
}
return p;
};
vp = check(static_cast<char **>(malloc(possible.size() * sizeof(char *))));
for (auto & p : possible)
vp[ac++] = check(strdup(p.c_str()));
*avp = vp;
return vp;
}
return ac;
// Instead of using the readline-provided prefix, do our own tokenization
// to avoid the default behavior of treating dots/quotes as word boundaries.
// See the definition of SEPS for what it treats as a boundary:
// https://github.com/troglobit/editline/blob/caf4b3c0ce3b0785791198b11de6f3134e9f05d8/src/editline.c
std::string getLastTokenBeforeCursor()
{
std::string_view line{rl_line_buffer, static_cast<size_t>(rl_point)};
auto tokens = tokenizeString<std::vector<std::string>>(
line,
// Same as editline's SEPS, except for double and single quotes:
"#$&()*:;<=>?[\\]^`{|}~\n\t "
);
if (tokens.empty()) {
return "";
}
return tokens.back();
}
// Sometimes inserting text or listing possible completions has a side effect
// of hiding the text after the cursor (even though it remains in the buffer).
// This helper just refreshes the display while keeping the cursor in place.
//
// Inserting text also sometimes moves the whole buffer down one line, usually
// if the cursor is inside a quoted attr name. I'm not sure why (vs unquoted)
// but it still seems to work pretty well and is just a visual artifact.
el_status_t redisplay()
{
int cursorPos = rl_point;
rl_refresh_line(0, 0);
rl_point = cursorPos;
return (rl_point == rl_end) ? CSstay : CSmove;
}
};
static el_status_t doCompletion() {
auto s = getLastTokenBeforeCursor();
auto possible = curRepl->completePrefix(s);
if (possible.empty()) {
return el_ring_bell();
}
if (possible.size() == 1) {
const auto completion = *possible.cbegin();
if (completion.size() > s.size()) {
rl_insert_text(completion.c_str() + s.size());
return redisplay();
}
return el_ring_bell();
}
auto checkAllHaveSameAt = [&](size_t pos) {
auto & first = *possible.begin();
for (auto & p : possible) {
if (p.size() <= pos || p[pos] != first[pos]) {
return false;
}
}
return true;
};
size_t start = s.size();
size_t len = 0;
while (checkAllHaveSameAt(start + len)) {
++len;
}
if (len > 0) {
auto commonPrefix = possible.begin()->substr(start, len);
rl_insert_text(commonPrefix.c_str());
el_ring_bell();
return redisplay();
}
char** columns = copyCompletions(possible);
el_print_columns(possible.size(), columns);
return redisplay();
}
ReadlineLikeInteracter::Guard ReadlineLikeInteracter::init(detail::ReplCompleterMixin * repl)
@@ -115,17 +153,15 @@ ReadlineLikeInteracter::Guard ReadlineLikeInteracter::init(detail::ReplCompleter
} catch (SysError & e) {
logWarning(e.info());
}
#ifndef READLINE
el_hist_size = 1000;
#endif
read_history(historyFile.c_str());
auto oldRepl = curRepl;
curRepl = repl;
Guard restoreRepl([oldRepl] { curRepl = oldRepl; });
#ifndef READLINE
rl_set_complete_func(completionCallback);
rl_set_list_possib_func(listPossibleCallback);
#endif
// editline does its own escaping of completions, so we rebind tab
// to our own completion function to skip that and do nix escaping
// instead of shell escaping.
el_bind_key(CTL('I'), doCompletion);
return restoreRepl;
}
@@ -1,8 +1,8 @@
#pragma once
/// @file
#include "finally.hh"
#include "types.hh"
#include "lix/libutil/finally.hh"
#include "lix/libutil/types.hh"
#include <functional>
#include <string>
@@ -32,7 +32,7 @@ public:
virtual ~ReplInteracter(){};
};
class ReadlineLikeInteracter : public virtual ReplInteracter
class ReadlineLikeInteracter final : public ReplInteracter
{
std::string historyFile;
public:
@@ -50,7 +50,7 @@ public:
virtual ~ReadlineLikeInteracter() override;
};
class AutomationInteracter : public virtual ReplInteracter
class AutomationInteracter final : public ReplInteracter
{
public:
AutomationInteracter() = default;
+181 -170
View File
@@ -6,34 +6,35 @@
#include <climits>
#include <string_view>
#include "box_ptr.hh"
#include "repl-interacter.hh"
#include "repl.hh"
#include "lix/libutil/box_ptr.hh"
#include "lix/libcmd/repl-interacter.hh"
#include "lix/libcmd/repl.hh"
#include "ansicolor.hh"
#include "shared.hh"
#include "eval.hh"
#include "eval-cache.hh"
#include "eval-inline.hh"
#include "eval-settings.hh"
#include "attr-path.hh"
#include "signals.hh"
#include "store-api.hh"
#include "log-store.hh"
#include "common-eval-args.hh"
#include "get-drvs.hh"
#include "derivations.hh"
#include "globals.hh"
#include "flake/flake.hh"
#include "flake/lockfile.hh"
#include "editor-for.hh"
#include "finally.hh"
#include "markdown.hh"
#include "local-fs-store.hh"
#include "signals.hh"
#include "print.hh"
#include "gc-small-vector.hh"
#include "users.hh"
#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"
#include "lix/libstore/store-api.hh"
#include "lix/libstore/log-store.hh"
#include "lix/libcmd/common-eval-args.hh"
#include "lix/libexpr/get-drvs.hh"
#include "lix/libstore/derivations.hh"
#include "lix/libstore/globals.hh"
#include "lix/libexpr/flake/flake.hh"
#include "lix/libexpr/flake/lockfile.hh"
#include "lix/libcmd/editor-for.hh"
#include "lix/libutil/finally.hh"
#include "lix/libcmd/markdown.hh"
#include "lix/libstore/local-fs-store.hh"
#include "lix/libutil/signals.hh"
#include "lix/libexpr/print.hh"
#include "lix/libexpr/gc-small-vector.hh"
#include "lix/libutil/users.hh"
#if HAVE_BOEHMGC
#define GC_INCLUDE_NEW
@@ -119,6 +120,7 @@ struct NixRepl
};
/* clang-format: on */
Evaluator & evaluator;
size_t debugTraceIndex;
Strings loadedFiles;
@@ -133,7 +135,7 @@ struct NixRepl
box_ptr<ReplInteracter> interacter;
NixRepl(const SearchPath & searchPath, nix::ref<Store> store,ref<EvalState> state,
NixRepl(const SearchPath & searchPath, nix::ref<Store> store, EvalState & state,
std::function<AnnotatedValues()> getValues);
virtual ~NixRepl() = default;
@@ -157,7 +159,7 @@ struct NixRepl
void addVarToScope(const Symbol name, Value & v);
Expr & parseString(std::string s);
void evalString(std::string s, Value & v);
void loadDebugTraceEnv(DebugTrace & dt);
void loadDebugTraceEnv(const DebugTrace & dt);
/**
* Load the `repl-overlays` and add the resulting AttrSet to the top-level
@@ -181,11 +183,7 @@ struct NixRepl
* Note: This is `shared_ptr` to avoid garbage collection.
*/
std::shared_ptr<Value *> replOverlaysEvalFunction =
#if HAVE_BOEHMGC
std::allocate_shared<Value *>(traceable_allocator<Value *>(), nullptr);
#else
std::make_shared<Value *>(nullptr);
#endif
std::allocate_shared<Value *>(TraceableAllocator<Value *>(), nullptr);
/**
* Get the `info` AttrSet that's passed as the first argument to each
@@ -206,7 +204,7 @@ struct NixRepl
Value & v,
unsigned int maxDepth = std::numeric_limits<unsigned int>::max())
{
::nix::printValue(*state, str, v, PrintOptions {
::nix::printValue(state, str, v, PrintOptions {
.ansiColors = true,
.force = true,
.derivationPaths = true,
@@ -232,12 +230,13 @@ static box_ptr<ReplInteracter> makeInteracter() {
return make_box_ptr<ReadlineLikeInteracter>(getDataDir() + "/nix/repl-history");
}
NixRepl::NixRepl(const SearchPath & searchPath, nix::ref<Store> store, ref<EvalState> state,
NixRepl::NixRepl(const SearchPath & searchPath, nix::ref<Store> store, EvalState & state,
std::function<NixRepl::AnnotatedValues()> getValues)
: AbstractNixRepl(state)
, evaluator(state.ctx)
, debugTraceIndex(0)
, getValues(getValues)
, staticEnv(new StaticEnv(nullptr, state->staticBaseEnv.get()))
, staticEnv(new StaticEnv(nullptr, evaluator.builtins.staticEnv.get()))
, interacter(makeInteracter())
{
}
@@ -285,7 +284,7 @@ ReplExitStatus NixRepl::mainLoop()
{
if (isFirstRepl) {
std::string_view debuggerNotice = "";
if (state->debugRepl) {
if (evaluator.debug && evaluator.debug->inDebugger) {
debuggerNotice = " debugger";
}
notice("Lix %1%%2%\nType :? for help.", nixVersion, debuggerNotice);
@@ -310,7 +309,9 @@ ReplExitStatus NixRepl::mainLoop()
// number of chars as the prompt.
if (!interacter->getLine(input, input.empty() ? ReplPromptType::ReplPrompt : ReplPromptType::ContinuationPrompt)) {
// Ctrl-D should exit the debugger.
state->debugStop = false;
if (evaluator.debug) {
evaluator.debug->stop = false;
}
logger->cout("");
// TODO: Should Ctrl-D exit just the current debugger session or
// the entire program?
@@ -350,7 +351,7 @@ ReplExitStatus NixRepl::mainLoop()
}
}
StringSet NixRepl::completePrefix(const std::string & prefix)
StringSet NixRepl::completePrefix(const std::string &prefix)
{
StringSet completions;
@@ -366,7 +367,7 @@ StringSet NixRepl::completePrefix(const std::string & prefix)
}
}
if (state->debugRepl) {
if (evaluator.debug && evaluator.debug->inDebugger) {
for (auto const & colonCmd : this->DEBUG_COMMANDS) {
if (colonCmd.starts_with(prefix)) {
completions.insert(std::string(colonCmd));
@@ -415,10 +416,25 @@ StringSet NixRepl::completePrefix(const std::string & prefix)
i++;
}
} else {
// To handle cases like `foo."bar.`, walk back the cursor
// to the previous dot if there are an odd number of quotes.
auto quoteCount =
std::count_if(cur.begin(), cur.begin() + dot, [](char c) { return c == '"'; });
if (quoteCount % 2 != 0) {
// Find the last quote before the dot
auto prevQuote = cur.rfind('"', dot - 1);
if (prevQuote != std::string::npos) {
// And the previous dot prior to that quote
auto prevDot = cur.rfind('.', prevQuote);
if (prevDot != std::string::npos) {
dot = prevDot;
}
}
}
/* Temporarily disable the debugger, to avoid re-entering readline. */
auto debug_repl = state->debugRepl;
state->debugRepl = nullptr;
Finally restoreDebug([&]() { state->debugRepl = debug_repl; });
auto debug = std::move(evaluator.debug);
Finally restoreDebug([&]() { evaluator.debug = std::move(debug); });
try {
/* This is an expression that should evaluate to an
attribute set. Evaluate it to get the names of the
@@ -428,11 +444,14 @@ StringSet NixRepl::completePrefix(const std::string & prefix)
Expr & e = parseString(expr);
Value v;
e.eval(*state, *env, v);
state->forceAttrs(v, noPos, "while evaluating an attrset for the purpose of completion (this error should not be displayed; file an issue?)");
e.eval(state, *env, v);
state.forceAttrs(v, noPos, "while evaluating an attrset for the purpose of completion (this error should not be displayed; file an issue?)");
for (auto & i : *v.attrs) {
std::string_view name = state->symbols[i.name];
std::ostringstream output;
printAttributeName(output, evaluator.symbols[i.name]);
std::string name = output.str();
if (name.substr(0, cur2.size()) != cur2) continue;
completions.insert(concatStrings(prev, expr, ".", name));
}
@@ -470,28 +489,28 @@ static bool isVarName(std::string_view s)
StorePath NixRepl::getDerivationPath(Value & v) {
auto drvInfo = getDerivation(*state, v, false);
auto drvInfo = getDerivation(state, v, false);
if (!drvInfo)
throw Error("expression does not evaluate to a derivation, so I can't build it");
auto drvPath = drvInfo->queryDrvPath();
auto drvPath = drvInfo->queryDrvPath(state);
if (!drvPath)
throw Error("expression did not evaluate to a valid derivation (no 'drvPath' attribute)");
if (!state->store->isValidPath(*drvPath))
throw Error("expression evaluated to invalid derivation '%s'", state->store->printStorePath(*drvPath));
if (!evaluator.store->isValidPath(*drvPath))
throw Error("expression evaluated to invalid derivation '%s'", evaluator.store->printStorePath(*drvPath));
return *drvPath;
}
void NixRepl::loadDebugTraceEnv(DebugTrace & dt)
void NixRepl::loadDebugTraceEnv(const DebugTrace & dt)
{
initEnv();
auto se = state->getStaticEnv(dt.expr);
auto se = evaluator.debug->staticEnvFor(dt.expr);
if (se) {
auto vm = mapStaticEnvBindings(state->symbols, *se.get(), dt.env);
auto vm = mapStaticEnvBindings(evaluator.symbols, *se.get(), dt.env);
// add staticenv vars.
for (auto & [name, value] : *(vm.get()))
addVarToScope(state->symbols.create(name), *value);
addVarToScope(evaluator.symbols.create(name), *value);
}
}
@@ -541,7 +560,7 @@ ProcessLineResult NixRepl::processLine(std::string line)
<< " errors\n"
<< " :?, :help Brings up this help menu\n"
;
if (state->debugRepl) {
if (evaluator.debug && evaluator.debug->inDebugger) {
std::cout
<< "\n"
<< " Debug mode commands\n"
@@ -556,49 +575,52 @@ ProcessLineResult NixRepl::processLine(std::string line)
}
else if (state->debugRepl && (command == ":bt" || command == ":backtrace")) {
for (const auto & [idx, i] : enumerate(state->debugTraces)) {
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 << ": ";
showDebugTrace(std::cout, state->positions, i);
showDebugTrace(std::cout, evaluator.positions, *i);
}
}
else if (state->debugRepl && (command == ":env")) {
for (const auto & [idx, i] : enumerate(state->debugTraces)) {
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);
printEnvBindings(state, i->expr, i->env);
break;
}
}
}
else if (state->debugRepl && (command == ":st")) {
else if (evaluator.debug && evaluator.debug->inDebugger && (command == ":st")) {
try {
// change the DebugTrace index.
debugTraceIndex = stoi(arg);
} catch (...) { }
for (const auto & [idx, i] : enumerate(state->debugTraces)) {
auto traces = evaluator.debug->traces();
for (const auto & [idx, i] : enumerate(traces)) {
if (idx == debugTraceIndex) {
std::cout << "\n" << ANSI_BLUE << idx << ANSI_NORMAL << ": ";
showDebugTrace(std::cout, state->positions, i);
showDebugTrace(std::cout, evaluator.positions, *i);
std::cout << std::endl;
printEnvBindings(*state, i.expr, i.env);
loadDebugTraceEnv(i);
printEnvBindings(state, i->expr, i->env);
loadDebugTraceEnv(*i);
break;
}
}
}
else if (state->debugRepl && (command == ":s" || command == ":step")) {
else if (evaluator.debug && evaluator.debug->inDebugger && (command == ":s" || command == ":step")) {
// set flag to stop at next DebugTrace; exit repl.
state->debugStop = true;
evaluator.debug->stop = true;
return ProcessLineResult::Continue;
}
else if (state->debugRepl && (command == ":c" || command == ":continue")) {
else if (evaluator.debug && evaluator.debug->inDebugger && (command == ":c" || command == ":continue")) {
// set flag to run to next breakpoint or end of program; exit repl.
state->debugStop = false;
evaluator.debug->stop = false;
return ProcessLineResult::Continue;
}
@@ -609,7 +631,7 @@ ProcessLineResult NixRepl::processLine(std::string line)
}
else if (command == ":l" || command == ":load") {
state->resetFileCache();
state.resetFileCache();
loadFile(arg);
}
@@ -618,7 +640,7 @@ ProcessLineResult NixRepl::processLine(std::string line)
}
else if (command == ":r" || command == ":reload") {
state->resetFileCache();
state.resetFileCache();
reloadFiles();
}
@@ -629,17 +651,17 @@ ProcessLineResult NixRepl::processLine(std::string line)
const auto [path, line] = [&] () -> std::pair<SourcePath, uint32_t> {
if (v.type() == nPath || v.type() == nString) {
NixStringContext context;
auto path = state->coerceToPath(noPos, v, context, "while evaluating the filename to edit");
auto path = state.coerceToPath(noPos, v, context, "while evaluating the filename to edit");
return {path, 0};
} else if (v.isLambda()) {
auto pos = state->positions[v.lambda.fun->pos];
if (auto path = std::get_if<SourcePath>(&pos.origin))
auto pos = evaluator.positions[v.lambda.fun->pos];
if (auto path = std::get_if<CheckedSourcePath>(&pos.origin))
return {*path, pos.line};
else
throw Error("'%s' cannot be shown in an editor", pos);
} else {
// assume it's a derivation
return findPackageFilename(*state, v, arg);
return findPackageFilename(state, v, arg);
}
}();
@@ -654,8 +676,8 @@ ProcessLineResult NixRepl::processLine(std::string line)
// Reload right after exiting the editor if path is not in store
// Store is immutable, so there could be no changes, so there's no need to reload
if (!state->store->isInStore(path.resolveSymlinks().path.abs())) {
state->resetFileCache();
if (!evaluator.store->isInStore(canonPath(path.canonical().abs(), true))) {
state.resetFileCache();
reloadFiles();
}
}
@@ -670,15 +692,15 @@ ProcessLineResult NixRepl::processLine(std::string line)
Value v, f, result;
evalString(arg, v);
evalString("drv: (import <nixpkgs> {}).runCommand \"shell\" { buildInputs = [ drv ]; } \"\"", f);
state->callFunction(f, v, result, PosIdx());
state.callFunction(f, v, result, PosIdx());
StorePath drvPath = getDerivationPath(result);
runNix("nix-shell", {state->store->printStorePath(drvPath)});
runNix("nix-shell", {evaluator.store->printStorePath(drvPath)});
}
else if (command == ":log") {
StorePath drvPath = ([&] {
auto maybeDrvPath = state->store->maybeParseStorePath(arg);
auto maybeDrvPath = evaluator.store->maybeParseStorePath(arg);
if (maybeDrvPath && maybeDrvPath->isDerivation()) {
return std::move(*maybeDrvPath);
} else {
@@ -687,7 +709,7 @@ ProcessLineResult NixRepl::processLine(std::string line)
return getDerivationPath(v);
}
})();
Path drvPathRaw = state->store->printStorePath(drvPath);
Path drvPathRaw = evaluator.store->printStorePath(drvPath);
settings.readOnlyMode = true;
Finally roModeReset([&]() {
@@ -695,7 +717,7 @@ ProcessLineResult NixRepl::processLine(std::string line)
});
auto subs = getDefaultSubstituters();
subs.push_front(state->store);
subs.push_front(evaluator.store);
bool foundLog = false;
RunPager pager;
@@ -722,7 +744,7 @@ ProcessLineResult NixRepl::processLine(std::string line)
Value v;
evalString(arg, v);
StorePath drvPath = getDerivationPath(v);
Path drvPathRaw = state->store->printStorePath(drvPath);
Path drvPathRaw = evaluator.store->printStorePath(drvPath);
if (command == ":b" || command == ":bl") {
// TODO: this only shows a progress bar for explicitly initiated builds,
@@ -734,22 +756,22 @@ ProcessLineResult NixRepl::processLine(std::string line)
logger->pause();
});
state->store->buildPaths({
evaluator.store->buildPaths({
DerivedPath::Built {
.drvPath = makeConstantStorePathRef(drvPath),
.outputs = OutputsSpec::All { },
},
});
auto drv = state->store->readDerivation(drvPath);
auto drv = evaluator.store->readDerivation(drvPath);
logger->cout("\nThis derivation produced the following outputs:");
for (auto & [outputName, outputPath] : state->store->queryDerivationOutputMap(drvPath)) {
auto localStore = state->store.dynamic_pointer_cast<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;
localStore->addPermRoot(outputPath, absPath(symlink));
logger->cout(" ./%s -> %s", symlink, state->store->printStorePath(outputPath));
logger->cout(" ./%s -> %s", symlink, evaluator.store->printStorePath(outputPath));
} else {
logger->cout(" %s -> %s", outputName, state->store->printStorePath(outputPath));
logger->cout(" %s -> %s", outputName, evaluator.store->printStorePath(outputPath));
}
}
} else if (command == ":i") {
@@ -771,14 +793,16 @@ ProcessLineResult NixRepl::processLine(std::string line)
}
else if (command == ":q" || command == ":quit") {
state->debugStop = false;
if (evaluator.debug) {
evaluator.debug->stop = false;
}
return ProcessLineResult::Quit;
}
else if (command == ":doc") {
Value v;
evalString(arg, v);
if (auto doc = state->getDoc(v)) {
if (auto doc = evaluator.builtins.getDoc(v)) {
std::string markdown;
if (!doc->args.empty() && doc->name) {
@@ -795,8 +819,8 @@ ProcessLineResult NixRepl::processLine(std::string line)
logger->cout(trim(renderMarkdownToTerminal(markdown)));
} else if (v.isLambda()) {
auto pos = state->positions[v.lambda.fun->pos];
if (auto path = std::get_if<SourcePath>(&pos.origin)) {
auto pos = evaluator.positions[v.lambda.fun->pos];
if (auto path = std::get_if<CheckedSourcePath>(&pos.origin)) {
// Path and position have now been obtained, feed to nix-doc library to get data.
auto docComment = lambdaDocsForPos(*path, pos);
if (!docComment) {
@@ -817,10 +841,10 @@ ProcessLineResult NixRepl::processLine(std::string line)
else if (command == ":te" || command == ":trace-enable") {
if (arg == "false" || (arg == "" && loggerSettings.showTrace)) {
std::cout << "not showing error traces\n";
loggerSettings.showTrace = false;
loggerSettings.showTrace.override(false);
} else if (arg == "true" || (arg == "" && !loggerSettings.showTrace)) {
std::cout << "showing error traces\n";
loggerSettings.showTrace = true;
loggerSettings.showTrace.override(true);
} else {
throw Error("unexpected argument '%s' to %s", arg, command);
};
@@ -838,9 +862,9 @@ ProcessLineResult NixRepl::processLine(std::string line)
isVarName(name = removeWhitespace(line.substr(0, p))))
{
Expr & e = parseString(line.substr(p + 1));
Value & v(*state->allocValue());
Value & v(*evaluator.mem.allocValue());
v.mkThunk(env, e);
addVarToScope(state->symbols.create(name), v);
addVarToScope(evaluator.symbols.create(name), v);
} else {
Value v;
evalString(line, v);
@@ -857,8 +881,8 @@ void NixRepl::loadFile(const Path & path)
loadedFiles.remove(path);
loadedFiles.push_back(path);
Value v, v2;
state->evalFile(lookupFileArg(*state, path), v);
state->autoCallFunction(*autoArgs, v, v2);
state.evalFile(lookupFileArg(evaluator, path), v);
state.autoCallFunction(*autoArgs, v, v2);
addAttrsToScope(v2);
}
@@ -873,8 +897,8 @@ void NixRepl::loadFlake(const std::string & flakeRefS)
Value v;
flake::callFlake(*state,
flake::lockFlake(*state, flakeRef,
flake::callFlake(state,
flake::lockFlake(state, flakeRef,
flake::LockFlags {
.updateLockFile = false,
.useRegistries = !evalSettings.pureEval,
@@ -887,14 +911,14 @@ void NixRepl::loadFlake(const std::string & flakeRefS)
void NixRepl::initEnv()
{
env = &state->allocEnv(envSize);
env->up = &state->baseEnv;
env = &evaluator.mem.allocEnv(envSize);
env->up = &evaluator.builtins.env;
displ = 0;
staticEnv->vars.clear();
varNames.clear();
for (auto & i : state->staticBaseEnv->vars)
varNames.emplace(state->symbols[i.first]);
for (auto & i : evaluator.builtins.staticEnv->vars)
varNames.emplace(evaluator.symbols[i.first]);
}
@@ -926,16 +950,16 @@ void NixRepl::loadFiles()
void NixRepl::loadReplOverlays()
{
if (!evalSettings.replOverlays) {
if (evalSettings.replOverlays.get().empty()) {
return;
}
notice("Loading '%1%'...", Magenta("repl-overlays"));
auto replInitFilesFunction = getReplOverlaysEvalFunction();
Value &newAttrs(*state->allocValue());
Value &newAttrs(*evaluator.mem.allocValue());
SmallValueVector<3> args = {replInitInfo(), bindingsToAttrs(), replOverlays()};
state->callFunction(
state.callFunction(
*replInitFilesFunction,
args.size(),
args.data(),
@@ -958,25 +982,25 @@ Value * NixRepl::getReplOverlaysEvalFunction()
}
auto evalReplInitFilesPath = CanonPath::root + "repl-overlays.nix";
*replOverlaysEvalFunction = state->allocValue();
*replOverlaysEvalFunction = evaluator.mem.allocValue();
auto code =
#include "repl-overlays.nix.gen.hh"
;
auto & expr = state->parseExprFromString(
auto & expr = evaluator.parseExprFromString(
code,
SourcePath(evalReplInitFilesPath),
state->staticBaseEnv
evaluator.builtins.staticEnv
);
state->eval(expr, **replOverlaysEvalFunction);
state.eval(expr, **replOverlaysEvalFunction);
return *replOverlaysEvalFunction;
}
Value * NixRepl::replOverlays()
{
Value * replInits(state->allocValue());
state->mkList(*replInits, evalSettings.replOverlays.get().size());
Value * replInits(evaluator.mem.allocValue());
*replInits = evaluator.mem.newList(evalSettings.replOverlays.get().size());
Value ** replInitElems = replInits->listElems();
size_t i = 0;
@@ -986,10 +1010,10 @@ Value * NixRepl::replOverlays()
auto replInit = evalFile(sourcePath);
if (!replInit->isLambda()) {
state->error<TypeError>(
evaluator.errors.make<TypeError>(
"Expected `repl-overlays` to be a lambda but found %1%: %2%",
showType(*replInit),
ValuePrinter(*state, *replInit, errorPrintOptions)
ValuePrinter(state, *replInit, errorPrintOptions)
)
.atPos(replInit->determinePos(noPos))
.debugThrow();
@@ -997,7 +1021,7 @@ Value * NixRepl::replOverlays()
if (replInit->lambda.fun->hasFormals()
&& !replInit->lambda.fun->formals->ellipsis) {
state->error<TypeError>(
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",
"..."
@@ -1016,13 +1040,13 @@ Value * NixRepl::replOverlays()
Value * NixRepl::replInitInfo()
{
auto builder = state->buildBindings(2);
auto builder = evaluator.buildBindings(2);
Value * currentSystem(state->allocValue());
Value * currentSystem(evaluator.mem.allocValue());
currentSystem->mkString(evalSettings.getCurrentSystem());
builder.insert(state->symbols.create("currentSystem"), currentSystem);
builder.insert(evaluator.symbols.create("currentSystem"), currentSystem);
Value * info(state->allocValue());
Value * info(evaluator.mem.allocValue());
info->mkAttrs(builder.finish());
return info;
}
@@ -1030,14 +1054,14 @@ Value * NixRepl::replInitInfo()
void NixRepl::addAttrsToScope(Value & attrs)
{
state->forceAttrs(attrs, [&]() { return attrs.determinePos(noPos); }, "while evaluating an attribute set to be merged in the global scope");
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(state->symbols[i.name]);
varNames.emplace(evaluator.symbols[i.name]);
}
staticEnv->sort();
staticEnv->deduplicate();
@@ -1054,17 +1078,17 @@ void NixRepl::addVarToScope(const Symbol name, Value & v)
staticEnv->vars.emplace_back(name, displ);
staticEnv->sort();
env->values[displ++] = &v;
varNames.emplace(state->symbols[name]);
varNames.emplace(evaluator.symbols[name]);
}
Value * NixRepl::bindingsToAttrs()
{
auto builder = state->buildBindings(staticEnv->vars.size());
auto builder = evaluator.buildBindings(staticEnv->vars.size());
for (auto & [symbol, displacement] : staticEnv->vars) {
builder.insert(symbol, env->values[displacement]);
}
Value * attrs(state->allocValue());
Value * attrs(evaluator.mem.allocValue());
attrs->mkAttrs(builder.finish());
return attrs;
}
@@ -1072,63 +1096,50 @@ Value * NixRepl::bindingsToAttrs()
Expr & NixRepl::parseString(std::string s)
{
return state->parseExprFromString(std::move(s), state->rootPath(CanonPath::fromCwd()), staticEnv);
return evaluator.parseExprFromString(std::move(s), CanonPath::fromCwd(), staticEnv);
}
void NixRepl::evalString(std::string s, Value & v)
{
Expr & e = parseString(s);
e.eval(*state, *env, v);
state->forceValue(v, v.determinePos(noPos));
e.eval(state, *env, v);
state.forceValue(v, v.determinePos(noPos));
}
Value * NixRepl::evalFile(SourcePath & path)
{
auto & expr = state->parseExprFromFile(path, staticEnv);
Value * result(state->allocValue());
expr.eval(*state, *env, *result);
state->forceValue(*result, result->determinePos(noPos));
auto & expr = evaluator.parseExprFromFile(evaluator.paths.checkSourcePath(path), staticEnv);
Value * result(evaluator.mem.allocValue());
expr.eval(state, *env, *result);
state.forceValue(*result, result->determinePos(noPos));
return result;
}
std::unique_ptr<AbstractNixRepl> AbstractNixRepl::create(
const SearchPath & searchPath, nix::ref<Store> store, ref<EvalState> state,
std::function<AnnotatedValues()> getValues)
ReplExitStatus AbstractNixRepl::run(
const SearchPath & searchPath,
nix::ref<Store> store,
EvalState & state,
std::function<AnnotatedValues()> getValues,
const ValMap & extraEnv,
Bindings * autoArgs
)
{
return std::make_unique<NixRepl>(
searchPath,
openStore(),
state,
getValues
NixRepl repl(searchPath, store, state, getValues);
repl.autoArgs = autoArgs;
repl.initEnv();
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(
{}, openStore(), evalState, [] { return AnnotatedValues{}; }, extraEnv, nullptr
);
}
ReplExitStatus AbstractNixRepl::runSimple(
ref<EvalState> evalState,
const ValMap & extraEnv)
{
auto getValues = [&]()->NixRepl::AnnotatedValues{
NixRepl::AnnotatedValues values;
return values;
};
SearchPath searchPath = {};
auto repl = std::make_unique<NixRepl>(
searchPath,
openStore(),
evalState,
getValues
);
repl->initEnv();
// add 'extra' vars.
for (auto & [name, value] : extraEnv)
repl->addVarToScope(repl->state->symbols.create(name), *value);
return repl->mainLoop();
}
}
+18 -13
View File
@@ -1,32 +1,37 @@
#pragma once
///@file
#include "eval.hh"
#include "lix/libexpr/eval.hh"
namespace nix {
struct AbstractNixRepl
{
ref<EvalState> state;
typedef std::vector<std::pair<Value*,std::string>> AnnotatedValues;
static ReplExitStatus
run(const SearchPath & searchPath,
nix::ref<Store> store,
EvalState & state,
std::function<AnnotatedValues()> getValues,
const ValMap & extraEnv,
Bindings * autoArgs);
static ReplExitStatus runSimple(
EvalState & evalState,
const ValMap & extraEnv);
protected:
EvalState & state;
Bindings * autoArgs;
AbstractNixRepl(ref<EvalState> state)
AbstractNixRepl(EvalState & state)
: state(state)
{ }
virtual ~AbstractNixRepl()
{ }
typedef std::vector<std::pair<Value*,std::string>> AnnotatedValues;
static std::unique_ptr<AbstractNixRepl> create(
const SearchPath & searchPath, nix::ref<Store> store, ref<EvalState> state,
std::function<AnnotatedValues()> getValues);
static ReplExitStatus runSimple(
ref<EvalState> evalState,
const ValMap & extraEnv);
virtual void initEnv() = 0;
virtual ReplExitStatus mainLoop() = 0;

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