Compare commits
121
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
21317ce965 | ||
|
|
b43a289c02 | ||
|
|
2138b0f7e9 | ||
|
|
2f32368136 | ||
|
|
a713c064af | ||
|
|
cba05329ee | ||
|
|
f5f2e1537d | ||
|
|
0ea8649445 | ||
|
|
d0678a57f9 | ||
|
|
1d4ddb7e3b | ||
|
|
c7867e89f9 | ||
|
|
deb5150c82 | ||
|
|
92eb418a59 | ||
|
|
7fceee3ce3 | ||
|
|
31f976dc19 | ||
|
|
d8db15010d | ||
|
|
253be7d2ba | ||
|
|
a34e583305 | ||
|
|
a8fb008106 | ||
|
|
32cbb66a69 | ||
|
|
9631e9a30f | ||
|
|
98772c4a3b | ||
|
|
a7fd5c3867 | ||
|
|
6f3a7bbeb0 | ||
|
|
0d1f794178 | ||
|
|
ef5689dc1b | ||
|
|
db55ca9a2e | ||
|
|
4cd618272a | ||
|
|
73f0213500 | ||
|
|
9c1db3cd8b | ||
|
|
5fa27057b2 | ||
|
|
d2b1af70ee | ||
|
|
50def3fa73 | ||
|
|
8e2ab5532c | ||
|
|
079528098f | ||
|
|
9446732466 | ||
|
|
b5c3c21792 | ||
|
|
fc8bd88cf4 | ||
|
|
9b2761d66d | ||
|
|
fe79b90055 | ||
|
|
0d14c2b67a | ||
|
|
9760c00591 | ||
|
|
bf3ebde25f | ||
|
|
423d8b03c8 | ||
|
|
acbb3cff2d | ||
|
|
38dd196b03 | ||
|
|
d46adb45eb | ||
|
|
0dbfa7b26e | ||
|
|
4737d8b65e | ||
|
|
c948b350fb | ||
|
|
8db8ac9a67 | ||
|
|
3f6a1e45c9 | ||
|
|
ede0851fb4 | ||
|
|
5af069b248 | ||
|
|
f93af1db1f | ||
|
|
3acba7951a | ||
|
|
44255c316d | ||
|
|
d953e138a2 | ||
|
|
fafe647bc2 | ||
|
|
37e8904ba7 | ||
|
|
3413ab5629 | ||
|
|
a1c09bed7b | ||
|
|
8c1ece93cc | ||
|
|
c314c5e581 | ||
|
|
6e8bc92ca5 | ||
|
|
539c340c66 | ||
|
|
5c7ea4f446 | ||
|
|
92ed9fee7d | ||
|
|
4f5e5e9bdf | ||
|
|
06996718c3 | ||
|
|
cb44fe7c15 | ||
|
|
67f07e05df | ||
|
|
bc63b007e5 | ||
|
|
f51943f171 | ||
|
|
5530de4673 | ||
|
|
faf00ad022 | ||
|
|
c0808bd855 | ||
|
|
93d5221b9b | ||
|
|
21ad02c1d0 | ||
|
|
8ddcdfb59c | ||
|
|
445b7c7d8a | ||
|
|
9a96086381 | ||
|
|
7c76053d93 | ||
|
|
95e088de72 | ||
|
|
369e3f82f0 | ||
|
|
a2ae14bfd8 | ||
|
|
172515bd8b | ||
|
|
cdda2454f6 | ||
|
|
f7687fa414 | ||
|
|
9611018c27 | ||
|
|
2a9e560570 | ||
|
|
53e0b6ecfb | ||
|
|
fb76a4d335 | ||
|
|
9dd58224ea | ||
|
|
61eed2c97c | ||
|
|
07a26fc776 | ||
|
|
71c392ae0f | ||
|
|
7fa6e785ff | ||
|
|
1f37fc85ac | ||
|
|
5db5eed43d | ||
|
|
d30027a37a | ||
|
|
2f2bc90d92 | ||
|
|
014845ddf1 | ||
|
|
8e74d1ceeb | ||
|
|
ce698198f0 | ||
|
|
81559ea8ad | ||
|
|
473c1bdcab | ||
|
|
9ff702554d | ||
|
|
8468dc65c7 | ||
|
|
f47cd0fc88 | ||
|
|
d6a7fb5f6a | ||
|
|
a5d02bcdf9 | ||
|
|
9a3e3a5560 | ||
|
|
dd7d7450a5 | ||
|
|
276a771210 | ||
|
|
ea931d4d03 | ||
|
|
32f7a93f71 | ||
|
|
a65e9e5828 | ||
|
|
a4fa93d469 | ||
|
|
ef1d62ec6c | ||
|
|
0bfa58ff53 |
@@ -16,6 +16,8 @@ Checks:
|
||||
- -bugprone-unchecked-optional-access
|
||||
# many warnings, seems like a questionable lint
|
||||
- -bugprone-branch-clone
|
||||
# extremely noisy before clang 19: https://github.com/llvm/llvm-project/issues/93959
|
||||
- -bugprone-multi-level-implicit-pointer-conversion
|
||||
# all thrown exceptions must derive from std::exception
|
||||
- hicpp-exception-baseclass
|
||||
# capturing async lambdas are dangerous
|
||||
@@ -33,3 +35,4 @@ Checks:
|
||||
|
||||
CheckOptions:
|
||||
bugprone-reserved-identifier.AllowedIdentifiers: '__asan_default_options'
|
||||
bugprone-unused-return-value.AllowCastToVoid: true
|
||||
|
||||
+10
-21
@@ -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
|
||||
|
||||
@@ -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,6 +90,9 @@ jade:
|
||||
forgejo: jade
|
||||
github: lf-
|
||||
|
||||
just1602:
|
||||
forgejo: just1602
|
||||
|
||||
kiara:
|
||||
github: KiaraGrouwstra
|
||||
|
||||
|
||||
@@ -1,21 +0,0 @@
|
||||
---
|
||||
synopsis: "Build failures caused by `allowSubstitutes = false` while being the wrong system now produce a decent error"
|
||||
issues: [fj#484]
|
||||
cls: [1841]
|
||||
category: Fixes
|
||||
credits: jade
|
||||
---
|
||||
|
||||
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.
|
||||
```
|
||||
@@ -1,10 +0,0 @@
|
||||
---
|
||||
synopsis: "`Alt+Left` and `Alt+Right` go back/forwards by words in `nix repl`"
|
||||
issues: [fj#501]
|
||||
cls: [1883]
|
||||
category: Fixes
|
||||
credits: 9999years
|
||||
---
|
||||
|
||||
`nix repl` now recognizes `Alt+Left` and `Alt+Right` for navigating by words
|
||||
when entering input in `nix repl` on more terminals/platforms.
|
||||
@@ -1,15 +0,0 @@
|
||||
---
|
||||
synopsis: "Drop support for `xz` and `bzip2` Content-Encoding"
|
||||
category: Miscellany
|
||||
cls: [2134]
|
||||
credits: horrors
|
||||
---
|
||||
|
||||
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.
|
||||
@@ -1,13 +0,0 @@
|
||||
---
|
||||
synopsis: Ctrl-C stops Nix commands much more reliably and responsively
|
||||
issues: [7245, fj#393]
|
||||
cls: [2016]
|
||||
prs: [11618]
|
||||
category: Fixes
|
||||
credits: [roberth, 9999years]
|
||||
---
|
||||
|
||||
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.
|
||||
@@ -1,18 +0,0 @@
|
||||
---
|
||||
synopsis: Deprecated language features
|
||||
issues: [fj#437, 861]
|
||||
cls: [1785, 1736, 1735, 1744, 2206]
|
||||
category: Breaking Changes
|
||||
credits: [piegames, horrors]
|
||||
---
|
||||
|
||||
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`, `__lessThan`, `__findFile` and `__nixPath`. 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.
|
||||
@@ -1,10 +0,0 @@
|
||||
---
|
||||
synopsis: "transfers no longer allow arbitrary url schemas"
|
||||
category: Breaking Changes
|
||||
cls: [2106]
|
||||
credits: horrors
|
||||
---
|
||||
|
||||
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.
|
||||
@@ -1,23 +0,0 @@
|
||||
---
|
||||
synopsis: restore backwards-compatibility of `builtins.fetchGit` with Nix 2.3
|
||||
issues: [5291, 5128]
|
||||
credits: [ma27]
|
||||
category: Fixes
|
||||
---
|
||||
|
||||
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.
|
||||
@@ -1,10 +0,0 @@
|
||||
---
|
||||
synopsis: HTTP proxy environment variables are now respected for S3 binary cache stores
|
||||
issues: [fj#433]
|
||||
cls: [1788]
|
||||
category: Fixes
|
||||
credits: jade
|
||||
---
|
||||
|
||||
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.
|
||||
@@ -1,20 +0,0 @@
|
||||
---
|
||||
synopsis: "Includes are now qualified with library name"
|
||||
category: Development
|
||||
cls: [2178]
|
||||
credits: jade
|
||||
---
|
||||
|
||||
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.
|
||||
|
||||
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):
|
||||
|
||||
```
|
||||
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
|
||||
```
|
||||
@@ -1,13 +0,0 @@
|
||||
---
|
||||
synopsis: "Improvements to interactive flake config"
|
||||
cls: [2066]
|
||||
category: Improvements
|
||||
credits: ma27
|
||||
---
|
||||
|
||||
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.
|
||||
@@ -1,13 +0,0 @@
|
||||
---
|
||||
synopsis: Fix potential store corruption with auto-optimise-store
|
||||
issues: [7273]
|
||||
cls: [2100]
|
||||
category: Fixes
|
||||
credits: lilyball
|
||||
---
|
||||
|
||||
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.
|
||||
@@ -1,13 +0,0 @@
|
||||
---
|
||||
synopsis: Change `nix-build -o ""` to behave like `--no-out-link`
|
||||
cls: [2103]
|
||||
category: Fixes
|
||||
credits: lilyball
|
||||
---
|
||||
|
||||
[`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.
|
||||
@@ -1,38 +0,0 @@
|
||||
---
|
||||
synopsis: Removing the `.` default argument passed to the `nix fmt` formatter
|
||||
issues: []
|
||||
prs: [11438]
|
||||
cls: [1902]
|
||||
category: Breaking Changes
|
||||
credits: zimbatm
|
||||
---
|
||||
|
||||
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 "$@"
|
||||
'');
|
||||
};
|
||||
}
|
||||
```
|
||||
@@ -1,10 +0,0 @@
|
||||
---
|
||||
synopsis: "The beginnings of a new pytest-based functional test suite"
|
||||
category: Development
|
||||
cls: [2036, 2037]
|
||||
credits: jade
|
||||
---
|
||||
|
||||
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.
|
||||
@@ -1,17 +0,0 @@
|
||||
---
|
||||
synopsis: readline support removed
|
||||
cls: [1885]
|
||||
category: Packaging
|
||||
credits: [9999years]
|
||||
---
|
||||
|
||||
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
|
||||
@@ -1,30 +0,0 @@
|
||||
---
|
||||
synopsis: Relative and tilde paths in configuration
|
||||
issues: [fj#482]
|
||||
cls: [1851, 1863, 1864]
|
||||
category: Features
|
||||
credits: [9999years]
|
||||
---
|
||||
|
||||
[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.
|
||||
@@ -1,8 +0,0 @@
|
||||
---
|
||||
synopsis: "Dependency on monolithic coreutils removed"
|
||||
category: Development
|
||||
cls: [2108]
|
||||
credits: vigress8
|
||||
---
|
||||
|
||||
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.
|
||||
@@ -1,22 +0,0 @@
|
||||
---
|
||||
synopsis: "Reproducibility check builds now report all differing outputs"
|
||||
cls: [2069]
|
||||
category: Improvements
|
||||
credits: [lheckemann]
|
||||
---
|
||||
|
||||
`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'
|
||||
```
|
||||
@@ -1,12 +0,0 @@
|
||||
---
|
||||
synopsis: Ignore broken `/etc/ssl/certs/ca-certificates.crt` symlink
|
||||
issues: [fj#560]
|
||||
cls: [2144]
|
||||
category: Fixes
|
||||
credits: lilyball
|
||||
---
|
||||
|
||||
[`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`.
|
||||
@@ -1,26 +0,0 @@
|
||||
---
|
||||
synopsis: "Some Lix crashes now produce reporting instructions and a stack trace, then abort"
|
||||
cls: [1854]
|
||||
category: Improvements
|
||||
credits: jade
|
||||
---
|
||||
|
||||
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
|
||||
...
|
||||
```
|
||||
@@ -1,15 +0,0 @@
|
||||
---
|
||||
synopsis: Add a `temp-dir` setting to set the temporary directory location
|
||||
issues: [7731, 8995, fj#112, fj#253]
|
||||
cls: [2103]
|
||||
category: Improvements
|
||||
credits: lilyball
|
||||
---
|
||||
|
||||
[`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.
|
||||
@@ -1,17 +0,0 @@
|
||||
---
|
||||
synopsis: All Lix threads are named
|
||||
cls: [2210]
|
||||
category: Development
|
||||
credits: [jade]
|
||||
---
|
||||
|
||||
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
|
||||
```
|
||||
@@ -1,10 +0,0 @@
|
||||
---
|
||||
synopsis: "`<nix/fetchurl.nix>` now uses TLS verification"
|
||||
category: Fixes
|
||||
prs: [11585]
|
||||
credits: edolstra
|
||||
---
|
||||
|
||||
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.
|
||||
@@ -1,23 +0,0 @@
|
||||
---
|
||||
synopsis: "Set `X-GitHub-Api-Version` header"
|
||||
issues: [fj#255]
|
||||
cls: [1925]
|
||||
category: Development
|
||||
credits: kiara
|
||||
---
|
||||
|
||||
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.
|
||||
@@ -198,6 +198,7 @@
|
||||
- [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)
|
||||
|
||||
@@ -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.
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -0,0 +1,504 @@
|
||||
# Lix 2.92 "Bombe glacée" (2025-01-18)
|
||||
# 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.
|
||||
Generated
+7
-7
@@ -34,16 +34,16 @@
|
||||
},
|
||||
"nixpkgs": {
|
||||
"locked": {
|
||||
"lastModified": 1727184566,
|
||||
"narHash": "sha256-mgdK8BcFsLSNhe780+cHbEUbZ3OruLa1T/xgQlL4Aj4=",
|
||||
"lastModified": 1733348545,
|
||||
"narHash": "sha256-b4JrUmqT0vFNx42aEN9LTWOHomkTKL/ayLopflVf81U=",
|
||||
"owner": "NixOS",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "48c3030083c46042584531bc9d931020f1975677",
|
||||
"rev": "9ecb50d2fae8680be74c08bb0a995c5383747f89",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "NixOS",
|
||||
"ref": "nixos-24.05-small",
|
||||
"ref": "nixos-24.11-small",
|
||||
"repo": "nixpkgs",
|
||||
"type": "github"
|
||||
}
|
||||
@@ -67,11 +67,11 @@
|
||||
"pre-commit-hooks": {
|
||||
"flake": false,
|
||||
"locked": {
|
||||
"lastModified": 1726745158,
|
||||
"narHash": "sha256-D5AegvGoEjt4rkKedmxlSEmC+nNLMBPWFxvmYnVLhjk=",
|
||||
"lastModified": 1733318908,
|
||||
"narHash": "sha256-SVQVsbafSM1dJ4fpgyBqLZ+Lft+jcQuMtEL3lQWx2Sk=",
|
||||
"owner": "cachix",
|
||||
"repo": "git-hooks.nix",
|
||||
"rev": "4e743a6920eab45e8ba0fbe49dc459f1423a4b74",
|
||||
"rev": "6f4e2a2112050951a314d2733a994fbab94864c6",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
description = "Lix: A modern, delicious implementation of the Nix package manager";
|
||||
|
||||
inputs = {
|
||||
nixpkgs.url = "github:NixOS/nixpkgs/nixos-24.05-small";
|
||||
nixpkgs.url = "github:NixOS/nixpkgs/nixos-24.11-small";
|
||||
nixpkgs-regression.url = "github:NixOS/nixpkgs/215d4d0fd80ca5163643b03a33fde804a29cc1e2";
|
||||
pre-commit-hooks = {
|
||||
url = "github:cachix/git-hooks.nix";
|
||||
@@ -350,10 +350,10 @@
|
||||
name = "nixpkgs-lib-tests";
|
||||
paths =
|
||||
[ testWithNix ]
|
||||
# FIXME: This is disabled on darwin due to a nixpkgs bug https://github.com/NixOS/nixpkgs/issues/319147
|
||||
# After that is fixed, it should be restored to use lib/tests/release.nix as before, rather than this reimplementation.
|
||||
# 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 [
|
||||
(import (nixpkgs + "/pkgs/test/release") { inherit pkgs lib nix; })
|
||||
(pkgs.callPackage "${nixpkgs}/ci/eval" { nixVersions.nix_2_24 = nix; }).attrpathsSuperset
|
||||
];
|
||||
}
|
||||
);
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
#include <algorithm>
|
||||
#include <chrono>
|
||||
#include <set>
|
||||
#include <memory>
|
||||
#include <tuple>
|
||||
@@ -23,9 +24,6 @@
|
||||
|
||||
namespace nix {
|
||||
|
||||
static void handleAlarm(int sig) {
|
||||
}
|
||||
|
||||
std::string escapeUri(std::string uri)
|
||||
{
|
||||
std::replace(uri.begin(), uri.end(), '/', '_');
|
||||
@@ -137,7 +135,7 @@ static int main_build_remote(std::string programName, Strings argv)
|
||||
while (true) {
|
||||
bestSlotLock.reset();
|
||||
AutoCloseFD lock = openLockFile(currentLoad + "/main-lock", true);
|
||||
lockFile(lock.get(), ltWrite, true);
|
||||
lockFile(lock.get(), ltWrite);
|
||||
|
||||
bool rightType = false;
|
||||
|
||||
@@ -156,7 +154,7 @@ static int main_build_remote(std::string programName, Strings 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);
|
||||
}
|
||||
@@ -279,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;
|
||||
|
||||
+22
-17
@@ -198,15 +198,16 @@ static void main_nix_build(std::string programName, Strings 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);
|
||||
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();
|
||||
@@ -236,11 +237,11 @@ static void main_nix_build(std::string programName, Strings 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), CanonPath::fromCwd()));
|
||||
exprs.push_back(evaluator->parseExprFromString(std::move(i), CanonPath::fromCwd()));
|
||||
else {
|
||||
auto absolute = i;
|
||||
try {
|
||||
@@ -248,12 +249,16 @@ static void main_nix_build(std::string programName, Strings 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
|
||||
))
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -272,7 +277,7 @@ static void main_nix_build(std::string programName, Strings 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;
|
||||
}
|
||||
@@ -300,7 +305,7 @@ static void main_nix_build(std::string programName, Strings argv)
|
||||
}
|
||||
}
|
||||
|
||||
state->maybePrintStats();
|
||||
evaluator->maybePrintStats();
|
||||
|
||||
auto buildPaths = [&](const std::vector<DerivedPath> & paths) {
|
||||
/* Note: we do this even when !printMissing to efficiently
|
||||
@@ -322,7 +327,7 @@ static void main_nix_build(std::string programName, Strings 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;
|
||||
@@ -336,7 +341,7 @@ static void main_nix_build(std::string programName, Strings argv)
|
||||
if (!shell) {
|
||||
|
||||
try {
|
||||
auto & expr = state->parseExprFromString(
|
||||
auto & expr = evaluator->parseExprFromString(
|
||||
"(import <nixpkgs> {}).bashInteractive",
|
||||
CanonPath::fromCwd());
|
||||
|
||||
@@ -347,7 +352,7 @@ static void main_nix_build(std::string programName, Strings 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"},
|
||||
@@ -463,7 +468,7 @@ static void main_nix_build(std::string programName, Strings 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();
|
||||
@@ -566,9 +571,9 @@ static void main_nix_build(std::string programName, Strings 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));
|
||||
|
||||
|
||||
+146
-129
@@ -1,3 +1,4 @@
|
||||
#include "lix/libcmd/cmd-profiles.hh"
|
||||
#include "lix/libexpr/attr-path.hh"
|
||||
#include "lix/libcmd/common-eval-args.hh"
|
||||
#include "lix/libstore/derivations.hh"
|
||||
@@ -58,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;
|
||||
@@ -95,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);
|
||||
@@ -118,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
|
||||
@@ -137,7 +143,7 @@ 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);
|
||||
@@ -145,7 +151,7 @@ static void getAllExprs(EvalState & state,
|
||||
}
|
||||
/* Load the expression on demand. */
|
||||
auto vArg = state.mem.allocValue();
|
||||
vArg->mkString(path2.path.abs());
|
||||
vArg->mkString(path2.canonical().abs());
|
||||
if (seen.size() == maxAttrs)
|
||||
throw Error("too many Nix expressions in directory '%1%'", path);
|
||||
attrs.alloc(attrName).mkApp(&state.builtins.get("import"), vArg);
|
||||
@@ -159,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
|
||||
@@ -173,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);
|
||||
}
|
||||
|
||||
@@ -199,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);
|
||||
}
|
||||
}
|
||||
@@ -207,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));
|
||||
}
|
||||
|
||||
|
||||
@@ -221,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);
|
||||
}
|
||||
|
||||
|
||||
@@ -238,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);
|
||||
|
||||
@@ -280,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);
|
||||
@@ -289,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) {
|
||||
@@ -334,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);
|
||||
@@ -352,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);
|
||||
@@ -415,7 +422,7 @@ static void queryInstSources(EvalState & state,
|
||||
loadSourceExpr(state, *instSource.nixExprPath, vArg);
|
||||
|
||||
for (auto & i : args) {
|
||||
Expr & eFun = state.parseExprFromString(i, CanonPath::fromCwd());
|
||||
Expr & eFun = state.ctx.parseExprFromString(i, CanonPath::fromCwd());
|
||||
Value vFun, vTmp;
|
||||
state.eval(eFun, vFun);
|
||||
vTmp.mkApp(&vFun, &vArg);
|
||||
@@ -431,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)
|
||||
@@ -485,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);
|
||||
}
|
||||
|
||||
|
||||
@@ -510,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;
|
||||
@@ -527,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);
|
||||
}
|
||||
|
||||
|
||||
@@ -539,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;
|
||||
}
|
||||
}
|
||||
@@ -589,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
|
||||
@@ -597,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;
|
||||
}
|
||||
@@ -625,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) ||
|
||||
@@ -637,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;
|
||||
}
|
||||
@@ -649,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;
|
||||
}
|
||||
}
|
||||
@@ -695,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.mem.allocValue();
|
||||
auto v = state.ctx.mem.allocValue();
|
||||
v->mkString(value);
|
||||
drv.setMeta(name, v);
|
||||
drv.setMeta(state, name, v);
|
||||
}
|
||||
|
||||
|
||||
@@ -713,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;
|
||||
}
|
||||
}
|
||||
@@ -733,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;
|
||||
}
|
||||
}
|
||||
@@ -741,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");
|
||||
|
||||
@@ -751,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");
|
||||
@@ -761,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 {
|
||||
@@ -769,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);
|
||||
@@ -780,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);
|
||||
}
|
||||
|
||||
@@ -788,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();
|
||||
@@ -799,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);
|
||||
}
|
||||
);
|
||||
@@ -816,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;
|
||||
}
|
||||
}
|
||||
@@ -843,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);
|
||||
@@ -896,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) {
|
||||
@@ -927,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();
|
||||
@@ -936,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) {
|
||||
@@ -959,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;
|
||||
}
|
||||
}
|
||||
@@ -992,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;
|
||||
@@ -1040,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);
|
||||
|
||||
@@ -1058,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. */
|
||||
@@ -1067,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. */
|
||||
@@ -1077,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);
|
||||
@@ -1089,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;
|
||||
}
|
||||
@@ -1108,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. */
|
||||
@@ -1119,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);
|
||||
@@ -1140,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) {
|
||||
@@ -1154,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) {
|
||||
@@ -1179,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
|
||||
@@ -1193,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 += ';';
|
||||
@@ -1207,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
|
||||
@@ -1216,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;
|
||||
@@ -1225,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";
|
||||
@@ -1282,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;
|
||||
}
|
||||
}
|
||||
@@ -1315,7 +1332,7 @@ static void opSwitchGeneration(Globals & globals, Strings opFlags, Strings opArg
|
||||
throw UsageError("exactly one argument expected");
|
||||
|
||||
if (auto dstGen = string2Int<GenerationNumber>(opArgs.front()))
|
||||
switchGeneration(globals.profile, *dstGen, globals.dryRun);
|
||||
switchGeneration(globals.profile, dstGen, globals.dryRun);
|
||||
else
|
||||
throw UsageError("expected a generation number");
|
||||
}
|
||||
@@ -1522,7 +1539,7 @@ static int main_nix_env(std::string programName, Strings argv)
|
||||
|
||||
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>(
|
||||
|
||||
@@ -31,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;
|
||||
}
|
||||
@@ -58,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);
|
||||
|
||||
@@ -78,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);
|
||||
}
|
||||
@@ -157,26 +157,24 @@ static int main_nix_instantiate(std::string programName, Strings 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)
|
||||
@@ -184,13 +182,13 @@ static int main_nix_instantiate(std::string programName, Strings argv)
|
||||
|
||||
for (auto & i : files) {
|
||||
Expr & e = fromArgs
|
||||
? state->parseExprFromString(i, 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;
|
||||
}
|
||||
|
||||
+37
-37
@@ -24,65 +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.mem.newList(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.s.type).mkString("derivation");
|
||||
attrs.alloc(state.s.name).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.s.system).mkString(system);
|
||||
attrs.alloc(state.s.outPath).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.s.drvPath).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.s.outputs);
|
||||
vOutputs = state.mem.newList(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.mem.allocValue())->mkString(j.first);
|
||||
auto outputAttrs = state.buildBindings(2);
|
||||
outputAttrs.alloc(state.s.outPath).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.s.meta).mkAttrs(meta);
|
||||
attrs.alloc(state.ctx.s.meta).mkAttrs(meta);
|
||||
|
||||
(manifest.listElems()[n++] = state.mem.allocValue())->mkAttrs(attrs);
|
||||
(manifest.listElems()[n++] = state.ctx.mem.allocValue())->mkAttrs(attrs);
|
||||
|
||||
if (drvPath) references.insert(*drvPath);
|
||||
}
|
||||
@@ -91,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"
|
||||
, 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);
|
||||
|
||||
@@ -116,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.s.drvPath));
|
||||
Attr & aDrvPath(*topLevel.attrs->find(state.ctx.s.drvPath));
|
||||
auto topLevelDrv = state.coerceToStorePath(aDrvPath.pos, *aDrvPath.value, context, "");
|
||||
Attr & aOutPath(*topLevel.attrs->find(state.s.outPath));
|
||||
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;
|
||||
|
||||
@@ -5,8 +5,6 @@
|
||||
|
||||
namespace nix {
|
||||
|
||||
DrvInfos queryInstalled(EvalState & state, const Path & userEnv);
|
||||
|
||||
bool createUserEnv(EvalState & state, DrvInfos & elems,
|
||||
const Path & profile, bool keepDerivations,
|
||||
const std::string & lockToken);
|
||||
|
||||
@@ -21,7 +21,7 @@ DrvInfos queryInstalled(EvalState & state, const Path & userEnv)
|
||||
if (pathExists(manifestFile)) {
|
||||
Value v;
|
||||
state.evalFile(CanonPath(manifestFile), v);
|
||||
Bindings & bindings(*state.mem.allocBindings(0));
|
||||
Bindings & bindings(*state.ctx.mem.allocBindings(0));
|
||||
getDerivations(state, v, "", bindings, elems, false);
|
||||
}
|
||||
return elems;
|
||||
@@ -136,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")) {
|
||||
@@ -162,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));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -102,20 +102,17 @@ ref<Store> EvalCommand::getEvalStore()
|
||||
return ref<Store>(evalStore);
|
||||
}
|
||||
|
||||
ref<eval_cache::CachingEvalState> EvalCommand::getEvalState()
|
||||
ref<eval_cache::CachingEvaluator> EvalCommand::getEvaluator()
|
||||
{
|
||||
if (!evalState) {
|
||||
evalState = std::allocate_shared<eval_cache::CachingEvalState>(
|
||||
TraceableAllocator<EvalState>(), searchPath, getEvalStore(), getStore()
|
||||
evalState = std::allocate_shared<eval_cache::CachingEvaluator>(
|
||||
TraceableAllocator<EvalState>(), searchPath, getEvalStore(), getStore(),
|
||||
startReplOnEvalErrors ? AbstractNixRepl::runSimple : nullptr
|
||||
);
|
||||
|
||||
evalState->repair = repair;
|
||||
|
||||
if (startReplOnEvalErrors) {
|
||||
evalState->debug = std::make_unique<DebugState>(&AbstractNixRepl::runSimple);
|
||||
};
|
||||
}
|
||||
return ref<eval_cache::CachingEvalState>(evalState);
|
||||
return ref<eval_cache::CachingEvaluator>(evalState);
|
||||
}
|
||||
|
||||
MixOperateOnOptions::MixOperateOnOptions()
|
||||
@@ -165,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
|
||||
|
||||
+10
-7
@@ -77,12 +77,12 @@ struct EvalCommand : virtual StoreCommand, MixEvalArgs
|
||||
|
||||
ref<Store> getEvalStore();
|
||||
|
||||
ref<eval_cache::CachingEvalState> getEvalState();
|
||||
virtual ref<eval_cache::CachingEvaluator> getEvaluator();
|
||||
|
||||
private:
|
||||
std::shared_ptr<Store> evalStore;
|
||||
|
||||
std::shared_ptr<eval_cache::CachingEvalState> evalState;
|
||||
std::shared_ptr<eval_cache::CachingEvaluator> evalState;
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -114,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();
|
||||
|
||||
@@ -127,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
|
||||
@@ -324,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);
|
||||
|
||||
@@ -332,7 +334,8 @@ void completeFlakeRef(AddCompletions & completions, ref<Store> store, std::strin
|
||||
|
||||
void completeFlakeRefWithFragment(
|
||||
AddCompletions & completions,
|
||||
ref<eval_cache::CachingEvalState> evalState,
|
||||
EvalState & evalState,
|
||||
ref<eval_cache::CachingEvaluator> evaluator,
|
||||
flake::LockFlags lockFlags,
|
||||
Strings attrPathPrefixes,
|
||||
const Strings & defaultFlakeAttrPaths,
|
||||
|
||||
@@ -171,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.mem.allocValue();
|
||||
if (i.second[0] == 'E')
|
||||
state.mkThunk_(*v, state.parseExprFromString(i.second.substr(1), 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);
|
||||
@@ -185,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);
|
||||
@@ -205,7 +207,7 @@ SourcePath lookupFileArg(EvalState & state, std::string_view fileArg)
|
||||
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 CanonPath::fromCwd(fileArg);
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#pragma once
|
||||
///@file
|
||||
|
||||
#include "lix/libexpr/eval.hh"
|
||||
#include "lix/libutil/args.hh"
|
||||
#include "lix/libmain/common-args.hh"
|
||||
#include "lix/libexpr/search-path.hh"
|
||||
@@ -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);
|
||||
|
||||
}
|
||||
|
||||
@@ -7,9 +7,6 @@ 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 && (
|
||||
@@ -18,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;
|
||||
}
|
||||
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
namespace nix {
|
||||
|
||||
InstallableAttrPath::InstallableAttrPath(
|
||||
ref<eval_cache::CachingEvalState> state,
|
||||
ref<eval_cache::CachingEvaluator> state,
|
||||
SourceExprCommand & cmd,
|
||||
Value * v,
|
||||
const std::string & attrPath,
|
||||
@@ -24,43 +24,42 @@ InstallableAttrPath::InstallableAttrPath(
|
||||
, extendedOutputsSpec(std::move(extendedOutputsSpec))
|
||||
{ }
|
||||
|
||||
std::pair<Value *, PosIdx> InstallableAttrPath::toValue()
|
||||
std::pair<Value *, PosIdx> InstallableAttrPath::toValue(EvalState & state)
|
||||
{
|
||||
auto [vRes, pos] = findAlongAttrPath(*state, attrPath, *cmd.getAutoArgs(*state), **v);
|
||||
state->forceValue(*vRes, pos);
|
||||
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();
|
||||
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<eval_cache::CachingEvalState> state,
|
||||
ref<eval_cache::CachingEvaluator> state,
|
||||
SourceExprCommand & cmd,
|
||||
Value * v,
|
||||
std::string_view prefix,
|
||||
|
||||
@@ -20,7 +20,7 @@ class InstallableAttrPath : public InstallableValue
|
||||
ExtendedOutputsSpec extendedOutputsSpec;
|
||||
|
||||
InstallableAttrPath(
|
||||
ref<eval_cache::CachingEvalState> state,
|
||||
ref<eval_cache::CachingEvaluator> state,
|
||||
SourceExprCommand & cmd,
|
||||
Value * v,
|
||||
const std::string & attrPath,
|
||||
@@ -28,14 +28,14 @@ class InstallableAttrPath : public InstallableValue
|
||||
|
||||
std::string what() const override { return attrPath; };
|
||||
|
||||
std::pair<Value *, PosIdx> toValue() override;
|
||||
std::pair<Value *, PosIdx> toValue(EvalState & state) override;
|
||||
|
||||
DerivedPathsWithInfo toDerivedPaths() override;
|
||||
DerivedPathsWithInfo toDerivedPaths(EvalState & state) override;
|
||||
|
||||
public:
|
||||
|
||||
static InstallableAttrPath parse(
|
||||
ref<eval_cache::CachingEvalState> state,
|
||||
ref<eval_cache::CachingEvaluator> state,
|
||||
SourceExprCommand & cmd,
|
||||
Value * v,
|
||||
std::string_view prefix,
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -41,20 +41,6 @@ std::vector<std::string> InstallableFlake::getActualAttrPaths()
|
||||
return res;
|
||||
}
|
||||
|
||||
Value * InstallableFlake::getFlakeOutputs(const flake::LockedFlake & lockedFlake)
|
||||
{
|
||||
auto vFlake = state->mem.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<eval_cache::CachingEvalState> 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();
|
||||
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->s.outputSpecified)) {
|
||||
} else if (auto aMeta = attr->maybeGetAttr(state->s.meta)) {
|
||||
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->s.outputSpecified)) {
|
||||
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->s.meta)) {
|
||||
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()
|
||||
std::pair<Value *, PosIdx> InstallableFlake::toValue(EvalState & state)
|
||||
{
|
||||
return {&getCursor()->forceValue(), noPos};
|
||||
return {&getCursor(state)->forceValue(state), noPos};
|
||||
}
|
||||
|
||||
std::vector<ref<eval_cache::AttrCursor>>
|
||||
InstallableFlake::getCursors()
|
||||
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()
|
||||
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()
|
||||
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)) {
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
///@file
|
||||
|
||||
#include "lix/libcmd/installable-value.hh"
|
||||
#include "lix/libexpr/eval.hh"
|
||||
|
||||
namespace nix {
|
||||
|
||||
@@ -40,7 +41,7 @@ struct InstallableFlake : InstallableValue
|
||||
|
||||
InstallableFlake(
|
||||
SourceExprCommand * cmd,
|
||||
ref<eval_cache::CachingEvalState> state,
|
||||
ref<eval_cache::CachingEvaluator> state,
|
||||
FlakeRef && flakeRef,
|
||||
std::string_view fragment,
|
||||
ExtendedOutputsSpec extendedOutputsSpec,
|
||||
@@ -52,21 +53,19 @@ struct InstallableFlake : InstallableValue
|
||||
|
||||
std::vector<std::string> getActualAttrPaths();
|
||||
|
||||
Value * getFlakeOutputs(const flake::LockedFlake & lockedFlake);
|
||||
DerivedPathsWithInfo toDerivedPaths(EvalState & state) override;
|
||||
|
||||
DerivedPathsWithInfo toDerivedPaths() override;
|
||||
|
||||
std::pair<Value *, PosIdx> toValue() override;
|
||||
std::pair<Value *, PosIdx> toValue(EvalState & state) override;
|
||||
|
||||
/**
|
||||
* Get a cursor to every attrpath in getActualAttrPaths() that
|
||||
* exists. However if none exists, throw an exception.
|
||||
*/
|
||||
std::vector<ref<eval_cache::AttrCursor>> getCursors() 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;
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -83,7 +82,7 @@ static inline FlakeRef defaultNixpkgsFlakeRef()
|
||||
}
|
||||
|
||||
ref<eval_cache::EvalCache> openEvalCache(
|
||||
eval_cache::CachingEvalState & state,
|
||||
eval_cache::CachingEvaluator & state,
|
||||
std::shared_ptr<flake::LockedFlake> lockedFlake);
|
||||
|
||||
}
|
||||
|
||||
@@ -5,20 +5,20 @@
|
||||
namespace nix {
|
||||
|
||||
std::vector<ref<eval_cache::AttrCursor>>
|
||||
InstallableValue::getCursors()
|
||||
InstallableValue::getCursors(EvalState & state)
|
||||
{
|
||||
auto evalCache =
|
||||
std::make_shared<nix::eval_cache::EvalCache>(std::nullopt, *state,
|
||||
[&]() { return toValue().first; });
|
||||
std::make_shared<nix::eval_cache::EvalCache>(std::nullopt,
|
||||
[&](EvalState & state) { return toValue(state).first; });
|
||||
return {evalCache->getRoot()};
|
||||
}
|
||||
|
||||
ref<eval_cache::AttrCursor>
|
||||
InstallableValue::getCursor()
|
||||
InstallableValue::getCursor(EvalState & state)
|
||||
{
|
||||
/* Although getCursors should return at least one element, in case it doesn't,
|
||||
bound check to avoid an undefined behavior for vector[0] */
|
||||
return getCursors().at(0);
|
||||
return getCursors(state).at(0);
|
||||
}
|
||||
|
||||
static UsageError nonValueInstallable(Installable & installable)
|
||||
@@ -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>(),
|
||||
}};
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
|
||||
#include "lix/libcmd/installables.hh"
|
||||
#include "lix/libexpr/eval-cache.hh"
|
||||
#include "lix/libexpr/eval.hh"
|
||||
#include "lix/libexpr/flake/flake.hh"
|
||||
|
||||
namespace nix {
|
||||
@@ -20,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);
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -70,28 +71,28 @@ struct ExtraPathInfoValue : ExtraPathInfo
|
||||
*/
|
||||
struct InstallableValue : Installable
|
||||
{
|
||||
ref<eval_cache::CachingEvalState> state;
|
||||
ref<eval_cache::CachingEvaluator> evaluator;
|
||||
|
||||
InstallableValue(ref<eval_cache::CachingEvalState> state) : state(state) {}
|
||||
InstallableValue(ref<eval_cache::CachingEvaluator> evaluator) : evaluator(evaluator) {}
|
||||
|
||||
virtual ~InstallableValue() { }
|
||||
|
||||
virtual std::pair<Value *, PosIdx> toValue() = 0;
|
||||
virtual std::pair<Value *, PosIdx> toValue(EvalState & state) = 0;
|
||||
|
||||
/**
|
||||
* Get a cursor to each value this Installable could refer to.
|
||||
* However if none exists, throw exception instead of returning
|
||||
* empty vector.
|
||||
*/
|
||||
virtual std::vector<ref<eval_cache::AttrCursor>> getCursors();
|
||||
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();
|
||||
virtual ref<eval_cache::AttrCursor> getCursor(EvalState & state);
|
||||
|
||||
UnresolvedApp toApp();
|
||||
UnresolvedApp toApp(EvalState & state);
|
||||
|
||||
static InstallableValue & require(Installable & installable);
|
||||
static ref<InstallableValue> require(ref<Installable> installable);
|
||||
@@ -112,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
|
||||
);
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
+83
-65
@@ -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.override(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<eval_cache::CachingEvalState> 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,25 +394,25 @@ static StorePath getDeriver(
|
||||
}
|
||||
|
||||
ref<eval_cache::EvalCache> openEvalCache(
|
||||
eval_cache::CachingEvalState & 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.mem.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;
|
||||
@@ -417,12 +421,22 @@ ref<eval_cache::EvalCache> openEvalCache(
|
||||
if (fingerprint) {
|
||||
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;
|
||||
|
||||
@@ -430,21 +444,18 @@ Installables SourceExprCommand::parseInstallables(
|
||||
if (file && expr)
|
||||
throw UsageError("'--file' and '--expr' are exclusive");
|
||||
|
||||
// FIXME: backward compatibility hack
|
||||
if (file) evalSettings.pureEval.override(false);
|
||||
|
||||
auto state = getEvalState();
|
||||
auto vFile = state->mem.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, CanonPath::fromCwd());
|
||||
state->eval(e, *vFile);
|
||||
auto & e = evaluator->parseExprFromString(*expr, CanonPath::fromCwd());
|
||||
state.eval(e, *vFile);
|
||||
}
|
||||
|
||||
for (auto & s : ss) {
|
||||
@@ -452,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 {
|
||||
@@ -481,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),
|
||||
@@ -501,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();
|
||||
}
|
||||
@@ -533,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,
|
||||
@@ -540,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;
|
||||
}
|
||||
@@ -577,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,
|
||||
@@ -596,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});
|
||||
}
|
||||
@@ -675,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,
|
||||
@@ -683,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 {
|
||||
@@ -691,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());
|
||||
}
|
||||
@@ -712,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());
|
||||
}
|
||||
@@ -726,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());
|
||||
@@ -740,6 +757,7 @@ StorePath Installable::toStorePath(
|
||||
}
|
||||
|
||||
StorePathSet Installable::toDerivations(
|
||||
EvalState & state,
|
||||
ref<Store> store,
|
||||
const Installables & installables,
|
||||
bool useDeriver)
|
||||
@@ -747,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(
|
||||
@@ -824,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));
|
||||
}
|
||||
|
||||
@@ -841,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,6 +1,7 @@
|
||||
#pragma once
|
||||
///@file
|
||||
|
||||
#include "lix/libexpr/eval.hh"
|
||||
#include "lix/libstore/path.hh"
|
||||
#include "lix/libstore/outputs-spec.hh"
|
||||
#include "lix/libstore/derived-path.hh"
|
||||
@@ -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,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
|
||||
|
||||
@@ -18,6 +18,7 @@ extern "C" {
|
||||
}
|
||||
|
||||
#include "lix/libutil/finally.hh"
|
||||
#include "lix/libutil/strings.hh"
|
||||
#include "lix/libcmd/repl-interacter.hh"
|
||||
|
||||
namespace nix {
|
||||
@@ -30,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;
|
||||
|
||||
@@ -90,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,8 +158,10 @@ ReadlineLikeInteracter::Guard ReadlineLikeInteracter::init(detail::ReplCompleter
|
||||
auto oldRepl = curRepl;
|
||||
curRepl = repl;
|
||||
Guard restoreRepl([oldRepl] { curRepl = oldRepl; });
|
||||
rl_set_complete_func(completionCallback);
|
||||
rl_set_list_possib_func(listPossibleCallback);
|
||||
// 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;
|
||||
}
|
||||
|
||||
|
||||
+98
-76
@@ -12,6 +12,7 @@
|
||||
|
||||
#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"
|
||||
@@ -119,6 +120,7 @@ struct NixRepl
|
||||
};
|
||||
/* clang-format: on */
|
||||
|
||||
Evaluator & evaluator;
|
||||
size_t debugTraceIndex;
|
||||
|
||||
Strings loadedFiles;
|
||||
@@ -231,9 +233,10 @@ static box_ptr<ReplInteracter> makeInteracter() {
|
||||
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.builtins.staticEnv.get()))
|
||||
, staticEnv(new StaticEnv(nullptr, evaluator.builtins.staticEnv.get()))
|
||||
, interacter(makeInteracter())
|
||||
{
|
||||
}
|
||||
@@ -247,7 +250,7 @@ void runNix(Path program, const Strings & args)
|
||||
.program = settings.nixBinDir+ "/" + program,
|
||||
.args = args,
|
||||
.environment = subprocessEnv,
|
||||
}).wait();
|
||||
}).waitAndCheck();
|
||||
|
||||
return;
|
||||
}
|
||||
@@ -281,7 +284,7 @@ ReplExitStatus NixRepl::mainLoop()
|
||||
{
|
||||
if (isFirstRepl) {
|
||||
std::string_view debuggerNotice = "";
|
||||
if (state.debug && state.debug->inDebugger) {
|
||||
if (evaluator.debug && evaluator.debug->inDebugger) {
|
||||
debuggerNotice = " debugger";
|
||||
}
|
||||
notice("Lix %1%%2%\nType :? for help.", nixVersion, debuggerNotice);
|
||||
@@ -306,8 +309,8 @@ 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.
|
||||
if (state.debug) {
|
||||
state.debug->stop = false;
|
||||
if (evaluator.debug) {
|
||||
evaluator.debug->stop = false;
|
||||
}
|
||||
logger->cout("");
|
||||
// TODO: Should Ctrl-D exit just the current debugger session or
|
||||
@@ -348,7 +351,7 @@ ReplExitStatus NixRepl::mainLoop()
|
||||
}
|
||||
}
|
||||
|
||||
StringSet NixRepl::completePrefix(const std::string & prefix)
|
||||
StringSet NixRepl::completePrefix(const std::string &prefix)
|
||||
{
|
||||
StringSet completions;
|
||||
|
||||
@@ -364,7 +367,7 @@ StringSet NixRepl::completePrefix(const std::string & prefix)
|
||||
}
|
||||
}
|
||||
|
||||
if (state.debug && state.debug->inDebugger) {
|
||||
if (evaluator.debug && evaluator.debug->inDebugger) {
|
||||
for (auto const & colonCmd : this->DEBUG_COMMANDS) {
|
||||
if (colonCmd.starts_with(prefix)) {
|
||||
completions.insert(std::string(colonCmd));
|
||||
@@ -413,9 +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 = std::move(state.debug);
|
||||
Finally restoreDebug([&]() { state.debug = std::move(debug); });
|
||||
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
|
||||
@@ -429,7 +448,10 @@ StringSet NixRepl::completePrefix(const std::string & prefix)
|
||||
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,11 +492,11 @@ StorePath NixRepl::getDerivationPath(Value & v) {
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -482,13 +504,13 @@ void NixRepl::loadDebugTraceEnv(const DebugTrace & dt)
|
||||
{
|
||||
initEnv();
|
||||
|
||||
auto se = state.debug->staticEnvFor(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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -538,7 +560,7 @@ ProcessLineResult NixRepl::processLine(std::string line)
|
||||
<< " errors\n"
|
||||
<< " :?, :help Brings up this help menu\n"
|
||||
;
|
||||
if (state.debug && state.debug->inDebugger) {
|
||||
if (evaluator.debug && evaluator.debug->inDebugger) {
|
||||
std::cout
|
||||
<< "\n"
|
||||
<< " Debug mode commands\n"
|
||||
@@ -553,16 +575,16 @@ ProcessLineResult NixRepl::processLine(std::string line)
|
||||
|
||||
}
|
||||
|
||||
else if (state.debug && state.debug->inDebugger && (command == ":bt" || command == ":backtrace")) {
|
||||
auto traces = state.debug->traces();
|
||||
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.debug && state.debug->inDebugger && (command == ":env")) {
|
||||
auto traces = state.debug->traces();
|
||||
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);
|
||||
@@ -571,17 +593,17 @@ ProcessLineResult NixRepl::processLine(std::string line)
|
||||
}
|
||||
}
|
||||
|
||||
else if (state.debug && state.debug->inDebugger && (command == ":st")) {
|
||||
else if (evaluator.debug && evaluator.debug->inDebugger && (command == ":st")) {
|
||||
try {
|
||||
// change the DebugTrace index.
|
||||
debugTraceIndex = stoi(arg);
|
||||
} catch (...) { }
|
||||
|
||||
auto traces = state.debug->traces();
|
||||
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);
|
||||
@@ -590,15 +612,15 @@ ProcessLineResult NixRepl::processLine(std::string line)
|
||||
}
|
||||
}
|
||||
|
||||
else if (state.debug && state.debug->inDebugger && (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.debug->stop = true;
|
||||
evaluator.debug->stop = true;
|
||||
return ProcessLineResult::Continue;
|
||||
}
|
||||
|
||||
else if (state.debug && state.debug->inDebugger && (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.debug->stop = false;
|
||||
evaluator.debug->stop = false;
|
||||
return ProcessLineResult::Continue;
|
||||
}
|
||||
|
||||
@@ -632,8 +654,8 @@ ProcessLineResult NixRepl::processLine(std::string line)
|
||||
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);
|
||||
@@ -650,11 +672,11 @@ ProcessLineResult NixRepl::processLine(std::string line)
|
||||
|
||||
// runProgram redirects stdout to a StringSink,
|
||||
// using runProgram2 to allow editors to display their UI
|
||||
runProgram2(RunOptions { .program = editor, .searchPath = true, .args = args }).wait();
|
||||
runProgram2(RunOptions { .program = editor, .searchPath = true, .args = args }).waitAndCheck();
|
||||
|
||||
// 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())) {
|
||||
if (!evaluator.store->isInStore(canonPath(path.canonical().abs(), true))) {
|
||||
state.resetFileCache();
|
||||
reloadFiles();
|
||||
}
|
||||
@@ -673,12 +695,12 @@ ProcessLineResult NixRepl::processLine(std::string line)
|
||||
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,8 +793,8 @@ ProcessLineResult NixRepl::processLine(std::string line)
|
||||
}
|
||||
|
||||
else if (command == ":q" || command == ":quit") {
|
||||
if (state.debug) {
|
||||
state.debug->stop = false;
|
||||
if (evaluator.debug) {
|
||||
evaluator.debug->stop = false;
|
||||
}
|
||||
return ProcessLineResult::Quit;
|
||||
}
|
||||
@@ -780,7 +802,7 @@ ProcessLineResult NixRepl::processLine(std::string line)
|
||||
else if (command == ":doc") {
|
||||
Value v;
|
||||
evalString(arg, v);
|
||||
if (auto doc = state.builtins.getDoc(v)) {
|
||||
if (auto doc = evaluator.builtins.getDoc(v)) {
|
||||
std::string markdown;
|
||||
|
||||
if (!doc->args.empty() && doc->name) {
|
||||
@@ -797,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) {
|
||||
@@ -840,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.mem.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);
|
||||
@@ -859,7 +881,7 @@ void NixRepl::loadFile(const Path & path)
|
||||
loadedFiles.remove(path);
|
||||
loadedFiles.push_back(path);
|
||||
Value v, v2;
|
||||
state.evalFile(lookupFileArg(state, path), v);
|
||||
state.evalFile(lookupFileArg(evaluator, path), v);
|
||||
state.autoCallFunction(*autoArgs, v, v2);
|
||||
addAttrsToScope(v2);
|
||||
}
|
||||
@@ -889,14 +911,14 @@ void NixRepl::loadFlake(const std::string & flakeRefS)
|
||||
|
||||
void NixRepl::initEnv()
|
||||
{
|
||||
env = &state.mem.allocEnv(envSize);
|
||||
env->up = &state.builtins.env;
|
||||
env = &evaluator.mem.allocEnv(envSize);
|
||||
env->up = &evaluator.builtins.env;
|
||||
displ = 0;
|
||||
staticEnv->vars.clear();
|
||||
|
||||
varNames.clear();
|
||||
for (auto & i : state.builtins.staticEnv->vars)
|
||||
varNames.emplace(state.symbols[i.first]);
|
||||
for (auto & i : evaluator.builtins.staticEnv->vars)
|
||||
varNames.emplace(evaluator.symbols[i.first]);
|
||||
}
|
||||
|
||||
|
||||
@@ -935,7 +957,7 @@ void NixRepl::loadReplOverlays()
|
||||
notice("Loading '%1%'...", Magenta("repl-overlays"));
|
||||
auto replInitFilesFunction = getReplOverlaysEvalFunction();
|
||||
|
||||
Value &newAttrs(*state.mem.allocValue());
|
||||
Value &newAttrs(*evaluator.mem.allocValue());
|
||||
SmallValueVector<3> args = {replInitInfo(), bindingsToAttrs(), replOverlays()};
|
||||
state.callFunction(
|
||||
*replInitFilesFunction,
|
||||
@@ -960,14 +982,14 @@ Value * NixRepl::getReplOverlaysEvalFunction()
|
||||
}
|
||||
|
||||
auto evalReplInitFilesPath = CanonPath::root + "repl-overlays.nix";
|
||||
*replOverlaysEvalFunction = state.mem.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.builtins.staticEnv
|
||||
evaluator.builtins.staticEnv
|
||||
);
|
||||
|
||||
state.eval(expr, **replOverlaysEvalFunction);
|
||||
@@ -977,8 +999,8 @@ Value * NixRepl::getReplOverlaysEvalFunction()
|
||||
|
||||
Value * NixRepl::replOverlays()
|
||||
{
|
||||
Value * replInits(state.mem.allocValue());
|
||||
*replInits = state.mem.newList(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;
|
||||
@@ -988,7 +1010,7 @@ 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)
|
||||
@@ -999,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",
|
||||
"..."
|
||||
@@ -1018,13 +1040,13 @@ Value * NixRepl::replOverlays()
|
||||
|
||||
Value * NixRepl::replInitInfo()
|
||||
{
|
||||
auto builder = state.buildBindings(2);
|
||||
auto builder = evaluator.buildBindings(2);
|
||||
|
||||
Value * currentSystem(state.mem.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.mem.allocValue());
|
||||
Value * info(evaluator.mem.allocValue());
|
||||
info->mkAttrs(builder.finish());
|
||||
return info;
|
||||
}
|
||||
@@ -1039,7 +1061,7 @@ void NixRepl::addAttrsToScope(Value & attrs)
|
||||
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();
|
||||
@@ -1056,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.mem.allocValue());
|
||||
Value * attrs(evaluator.mem.allocValue());
|
||||
attrs->mkAttrs(builder.finish());
|
||||
return attrs;
|
||||
}
|
||||
@@ -1074,7 +1096,7 @@ Value * NixRepl::bindingsToAttrs()
|
||||
|
||||
Expr & NixRepl::parseString(std::string s)
|
||||
{
|
||||
return state.parseExprFromString(std::move(s), CanonPath::fromCwd(), staticEnv);
|
||||
return evaluator.parseExprFromString(std::move(s), CanonPath::fromCwd(), staticEnv);
|
||||
}
|
||||
|
||||
|
||||
@@ -1087,8 +1109,8 @@ void NixRepl::evalString(std::string s, Value & v)
|
||||
|
||||
Value * NixRepl::evalFile(SourcePath & path)
|
||||
{
|
||||
auto & expr = state.parseExprFromFile(path, staticEnv);
|
||||
Value * result(state.mem.allocValue());
|
||||
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;
|
||||
@@ -1108,7 +1130,7 @@ ReplExitStatus AbstractNixRepl::run(
|
||||
repl.autoArgs = autoArgs;
|
||||
repl.initEnv();
|
||||
for (auto & [name, value] : extraEnv) {
|
||||
repl.addVarToScope(repl.state.symbols.create(name), *value);
|
||||
repl.addVarToScope(repl.evaluator.symbols.create(name), *value);
|
||||
}
|
||||
return repl.mainLoop();
|
||||
}
|
||||
|
||||
+91
-34
@@ -1,60 +1,90 @@
|
||||
#include "lix/libexpr/attr-path.hh"
|
||||
#include "lix/libexpr/eval-inline.hh"
|
||||
#include "print-options.hh"
|
||||
#include <algorithm>
|
||||
#include <sstream>
|
||||
|
||||
|
||||
namespace nix {
|
||||
|
||||
|
||||
static Strings parseAttrPath(std::string_view s)
|
||||
std::vector<std::string> parseAttrPath(std::string_view const s)
|
||||
{
|
||||
Strings res;
|
||||
std::vector<std::string> res;
|
||||
std::string cur;
|
||||
bool haveData = false;
|
||||
auto i = s.begin();
|
||||
while (i != s.end()) {
|
||||
if (*i == '.') {
|
||||
res.push_back(cur);
|
||||
haveData = false;
|
||||
cur.clear();
|
||||
} else if (*i == '"') {
|
||||
// If there is a quote there *will* be a named term even if it is empty.
|
||||
++i;
|
||||
haveData = true;
|
||||
|
||||
while (1) {
|
||||
if (i == s.end())
|
||||
throw ParseError("missing closing quote in selection path '%1%'", s);
|
||||
if (*i == '"') break;
|
||||
cur.push_back(*i++);
|
||||
}
|
||||
} else
|
||||
} else {
|
||||
cur.push_back(*i);
|
||||
haveData = true;
|
||||
}
|
||||
++i;
|
||||
}
|
||||
if (!cur.empty()) res.push_back(cur);
|
||||
if (haveData) res.push_back(cur);
|
||||
return res;
|
||||
}
|
||||
|
||||
|
||||
std::vector<Symbol> parseAttrPath(EvalState & state, std::string_view s)
|
||||
std::string unparseAttrPath(std::vector<std::string> const & attrPath)
|
||||
{
|
||||
std::vector<Symbol> res;
|
||||
for (auto & a : parseAttrPath(s))
|
||||
res.push_back(state.symbols.create(a));
|
||||
return res;
|
||||
// FIXME(jade): can probably be rewritten with ranges once libc++ has a
|
||||
// fully featured implementation
|
||||
// https://github.com/llvm/llvm-project/pull/65536
|
||||
auto ret = std::ostringstream{};
|
||||
bool first = true;
|
||||
|
||||
for (auto const & part : attrPath) {
|
||||
if (!first) {
|
||||
ret << ".";
|
||||
}
|
||||
first = false;
|
||||
|
||||
bool mustQuote = std::ranges::any_of(part, [](char c) -> bool {
|
||||
return c == '"' || c == '.' || c == ' ';
|
||||
});
|
||||
|
||||
if (mustQuote || part.empty()) {
|
||||
ret << '"' << part << '"';
|
||||
} else {
|
||||
ret << part;
|
||||
}
|
||||
}
|
||||
|
||||
return ret.str();
|
||||
}
|
||||
|
||||
|
||||
std::pair<Value *, PosIdx> findAlongAttrPath(EvalState & state, const std::string & attrPath,
|
||||
Bindings & autoArgs, Value & vIn)
|
||||
{
|
||||
Strings tokens = parseAttrPath(attrPath);
|
||||
auto tokens = parseAttrPath(attrPath);
|
||||
|
||||
Value * v = &vIn;
|
||||
PosIdx pos = noPos;
|
||||
|
||||
for (auto & attr : tokens) {
|
||||
for (auto [attrPathIdx, attr] : enumerate(tokens)) {
|
||||
|
||||
/* Is i an index (integer) or a normal attribute name? */
|
||||
auto attrIndex = string2Int<unsigned int>(attr);
|
||||
|
||||
/* Evaluate the expression. */
|
||||
Value * vNew = state.mem.allocValue();
|
||||
Value * vNew = state.ctx.mem.allocValue();
|
||||
state.autoCallFunction(autoArgs, *v, *vNew);
|
||||
v = vNew;
|
||||
state.forceValue(*v, noPos);
|
||||
@@ -63,37 +93,65 @@ std::pair<Value *, PosIdx> findAlongAttrPath(EvalState & state, const std::strin
|
||||
according to what is specified in the attrPath. */
|
||||
|
||||
if (!attrIndex) {
|
||||
|
||||
if (v->type() != nAttrs)
|
||||
state.error<TypeError>(
|
||||
"the expression selected by the selection path '%1%' should be a set but is %2%",
|
||||
attrPath,
|
||||
showType(*v)).debugThrow();
|
||||
if (attr.empty())
|
||||
throw Error("empty attribute name in selection path '%1%'", attrPath);
|
||||
|
||||
Bindings::iterator a = v->attrs->find(state.symbols.create(attr));
|
||||
if (v->type() != nAttrs) {
|
||||
auto pathPart =
|
||||
std::vector<std::string>(tokens.begin(), tokens.begin() + attrPathIdx);
|
||||
state.ctx.errors
|
||||
.make<TypeError>(
|
||||
"the value being indexed in the selection path '%1%' at '%2%' should be a "
|
||||
"set but is %3%: %4%",
|
||||
attrPath,
|
||||
unparseAttrPath(pathPart),
|
||||
showType(*v),
|
||||
ValuePrinter(state, *v, errorPrintOptions)
|
||||
)
|
||||
.debugThrow();
|
||||
}
|
||||
|
||||
Bindings::iterator a = v->attrs->find(state.ctx.symbols.create(attr));
|
||||
if (a == v->attrs->end()) {
|
||||
std::set<std::string> attrNames;
|
||||
for (auto & attr : *v->attrs)
|
||||
attrNames.insert(state.symbols[attr.name]);
|
||||
attrNames.insert(state.ctx.symbols[attr.name]);
|
||||
|
||||
auto suggestions = Suggestions::bestMatches(attrNames, attr);
|
||||
throw AttrPathNotFound(suggestions, "attribute '%1%' in selection path '%2%' not found", attr, attrPath);
|
||||
auto pathPart =
|
||||
std::vector<std::string>(tokens.begin(), tokens.begin() + attrPathIdx);
|
||||
throw AttrPathNotFound(
|
||||
suggestions,
|
||||
"attribute '%1%' in selection path '%2%' not found inside path '%3%', whose "
|
||||
"contents are: %4%",
|
||||
attr,
|
||||
attrPath,
|
||||
unparseAttrPath(pathPart),
|
||||
ValuePrinter(state, *v, errorPrintOptions)
|
||||
);
|
||||
}
|
||||
v = &*a->value;
|
||||
pos = a->pos;
|
||||
}
|
||||
|
||||
else {
|
||||
|
||||
if (!v->isList())
|
||||
state.error<TypeError>(
|
||||
"the expression selected by the selection path '%1%' should be a list but is %2%",
|
||||
} else {
|
||||
if (!v->isList()) {
|
||||
state.ctx.errors
|
||||
.make<TypeError>(
|
||||
"the expression selected by the selection path '%1%' should be a list but "
|
||||
"is %2%: %3%",
|
||||
attrPath,
|
||||
showType(*v),
|
||||
ValuePrinter(state, *v, errorPrintOptions)
|
||||
)
|
||||
.debugThrow();
|
||||
}
|
||||
if (*attrIndex >= v->listSize()) {
|
||||
throw AttrPathNotFound(
|
||||
"list index %1% in selection path '%2%' is out of range for list %3%",
|
||||
*attrIndex,
|
||||
attrPath,
|
||||
showType(*v)).debugThrow();
|
||||
if (*attrIndex >= v->listSize())
|
||||
throw AttrPathNotFound("list index %1% in selection path '%2%' is out of range", *attrIndex, attrPath);
|
||||
ValuePrinter(state, *v, errorPrintOptions)
|
||||
);
|
||||
}
|
||||
|
||||
v = v->listElems()[*attrIndex];
|
||||
pos = noPos;
|
||||
@@ -109,7 +167,7 @@ std::pair<SourcePath, uint32_t> findPackageFilename(EvalState & state, Value & v
|
||||
{
|
||||
Value * v2;
|
||||
try {
|
||||
auto dummyArgs = state.mem.allocBindings(0);
|
||||
auto dummyArgs = state.ctx.mem.allocBindings(0);
|
||||
v2 = findAlongAttrPath(state, "meta.position", *dummyArgs, v).first;
|
||||
} catch (Error &) {
|
||||
throw NoPositionInfo("package '%s' has no source location information", what);
|
||||
@@ -120,7 +178,7 @@ std::pair<SourcePath, uint32_t> findPackageFilename(EvalState & state, Value & v
|
||||
NixStringContext context;
|
||||
auto path = state.coerceToPath(noPos, *v2, context, "while evaluating the 'meta.position' attribute of a derivation");
|
||||
|
||||
auto fn = path.path.abs();
|
||||
auto fn = path.canonical().abs();
|
||||
|
||||
auto fail = [fn]() {
|
||||
throw ParseError("cannot parse 'meta.position' attribute '%s'", fn);
|
||||
@@ -129,7 +187,6 @@ std::pair<SourcePath, uint32_t> findPackageFilename(EvalState & state, Value & v
|
||||
try {
|
||||
auto colon = fn.rfind(':');
|
||||
if (colon == std::string::npos) fail();
|
||||
std::string filename(fn, 0, colon);
|
||||
auto lineno = std::stoi(std::string(fn, colon + 1, std::string::npos));
|
||||
return {CanonPath(fn.substr(0, colon)), lineno};
|
||||
} catch (std::invalid_argument & e) {
|
||||
|
||||
@@ -22,6 +22,18 @@ std::pair<Value *, PosIdx> findAlongAttrPath(
|
||||
*/
|
||||
std::pair<SourcePath, uint32_t> findPackageFilename(EvalState & state, Value & v, std::string what);
|
||||
|
||||
std::vector<Symbol> parseAttrPath(EvalState & state, std::string_view s);
|
||||
/**
|
||||
* Parses an attr path (as used in nix-build -A foo.bar.baz) into a list of tokens.
|
||||
*
|
||||
* Such an attr path is a dot-separated sequence of attribute names, which are possibly quoted.
|
||||
* No escaping is performed; attribute names containing double quotes are unrepresentable.
|
||||
*/
|
||||
std::vector<std::string> parseAttrPath(std::string_view const s);
|
||||
|
||||
/**
|
||||
* Converts an attr path from a list of strings into a string once more.
|
||||
* The result returned is an attr path and is *not necessarily valid nix syntax*.
|
||||
*/
|
||||
std::string unparseAttrPath(std::vector<std::string> const & attrPath);
|
||||
|
||||
}
|
||||
|
||||
@@ -17,11 +17,11 @@ Bindings * EvalMemory::allocBindings(size_t capacity)
|
||||
{
|
||||
if (capacity == 0)
|
||||
return &Bindings::EMPTY;
|
||||
if (capacity > std::numeric_limits<Bindings::size_t>::max())
|
||||
if (capacity > std::numeric_limits<Bindings::Size>::max())
|
||||
throw Error("attribute set of size %d is too big", capacity);
|
||||
stats.nrAttrsets++;
|
||||
stats.nrAttrsInAttrsets += capacity;
|
||||
return new (gcAllocBytes(sizeof(Bindings) + sizeof(Attr) * capacity)) Bindings((Bindings::size_t) capacity);
|
||||
return new (gcAllocBytes(sizeof(Bindings) + sizeof(Attr) * capacity)) Bindings((Bindings::Size) capacity);
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -48,20 +48,20 @@ static_assert(sizeof(Attr) == 2 * sizeof(uint32_t) + sizeof(Value *),
|
||||
class Bindings
|
||||
{
|
||||
public:
|
||||
typedef uint32_t size_t;
|
||||
using Size = uint32_t;
|
||||
PosIdx pos;
|
||||
|
||||
static Bindings EMPTY;
|
||||
|
||||
private:
|
||||
size_t size_, capacity_;
|
||||
Size size_, capacity_;
|
||||
Attr attrs[0];
|
||||
|
||||
Bindings(size_t capacity) : size_(0), capacity_(capacity) { }
|
||||
Bindings(Size capacity) : size_(0), capacity_(capacity) { }
|
||||
Bindings(const Bindings & bindings) = delete;
|
||||
|
||||
public:
|
||||
size_t size() const { return size_; }
|
||||
Size size() const { return size_; }
|
||||
|
||||
bool empty() const { return !size_; }
|
||||
|
||||
@@ -92,14 +92,14 @@ public:
|
||||
iterator begin() { return &attrs[0]; }
|
||||
iterator end() { return &attrs[size_]; }
|
||||
|
||||
Attr & operator[](size_t pos)
|
||||
Attr & operator[](Size pos)
|
||||
{
|
||||
return attrs[pos];
|
||||
}
|
||||
|
||||
void sort();
|
||||
|
||||
size_t capacity() { return capacity_; }
|
||||
Size capacity() { return capacity_; }
|
||||
|
||||
/**
|
||||
* Returns the attributes in lexicographically sorted order.
|
||||
@@ -108,7 +108,7 @@ public:
|
||||
{
|
||||
std::vector<const Attr *> res;
|
||||
res.reserve(size_);
|
||||
for (size_t n = 0; n < size_; n++)
|
||||
for (Size n = 0; n < size_; n++)
|
||||
res.emplace_back(&attrs[n]);
|
||||
std::sort(res.begin(), res.end(), [&](const Attr * a, const Attr * b) {
|
||||
std::string_view sa = symbols[a->name], sb = symbols[b->name];
|
||||
|
||||
+117
-148
@@ -21,8 +21,6 @@ struct AttrDb
|
||||
{
|
||||
std::atomic_bool failed{false};
|
||||
|
||||
const Store & cfg;
|
||||
|
||||
struct State
|
||||
{
|
||||
SQLite db;
|
||||
@@ -35,15 +33,8 @@ struct AttrDb
|
||||
|
||||
std::unique_ptr<Sync<State>> _state;
|
||||
|
||||
SymbolTable & symbols;
|
||||
|
||||
AttrDb(
|
||||
const Store & cfg,
|
||||
const Hash & fingerprint,
|
||||
SymbolTable & symbols)
|
||||
: cfg(cfg)
|
||||
, _state(std::make_unique<Sync<State>>())
|
||||
, symbols(symbols)
|
||||
AttrDb(const Hash & fingerprint)
|
||||
: _state(std::make_unique<Sync<State>>())
|
||||
{
|
||||
auto state(_state->lock());
|
||||
|
||||
@@ -98,7 +89,7 @@ struct AttrDb
|
||||
|
||||
AttrId setAttrs(
|
||||
AttrKey key,
|
||||
const std::vector<Symbol> & attrs)
|
||||
const fullattr_t & attrs)
|
||||
{
|
||||
return doSQLite([&]()
|
||||
{
|
||||
@@ -106,17 +97,17 @@ struct AttrDb
|
||||
|
||||
state->insertAttribute.use()
|
||||
(key.first)
|
||||
(symbols[key.second])
|
||||
(key.second)
|
||||
(AttrType::FullAttrs)
|
||||
(0, false).exec();
|
||||
|
||||
AttrId rowId = state->db.getLastInsertedRowId();
|
||||
assert(rowId);
|
||||
|
||||
for (auto & attr : attrs)
|
||||
for (auto & attr : attrs.p)
|
||||
state->insertAttribute.use()
|
||||
(rowId)
|
||||
(symbols[attr])
|
||||
(attr)
|
||||
(AttrType::Placeholder)
|
||||
(0, false).exec();
|
||||
|
||||
@@ -141,14 +132,14 @@ struct AttrDb
|
||||
}
|
||||
state->insertAttributeWithContext.use()
|
||||
(key.first)
|
||||
(symbols[key.second])
|
||||
(key.second)
|
||||
(AttrType::String)
|
||||
(s)
|
||||
(ctx).exec();
|
||||
} else {
|
||||
state->insertAttribute.use()
|
||||
(key.first)
|
||||
(symbols[key.second])
|
||||
(key.second)
|
||||
(AttrType::String)
|
||||
(s).exec();
|
||||
}
|
||||
@@ -167,7 +158,7 @@ struct AttrDb
|
||||
|
||||
state->insertAttribute.use()
|
||||
(key.first)
|
||||
(symbols[key.second])
|
||||
(key.second)
|
||||
(AttrType::Bool)
|
||||
(b ? 1 : 0).exec();
|
||||
|
||||
@@ -185,7 +176,7 @@ struct AttrDb
|
||||
|
||||
state->insertAttribute.use()
|
||||
(key.first)
|
||||
(symbols[key.second])
|
||||
(key.second)
|
||||
(AttrType::Int)
|
||||
(n).exec();
|
||||
|
||||
@@ -203,7 +194,7 @@ struct AttrDb
|
||||
|
||||
state->insertAttribute.use()
|
||||
(key.first)
|
||||
(symbols[key.second])
|
||||
(key.second)
|
||||
(AttrType::ListOfStrings)
|
||||
(concatStringsSep("\t", l)).exec();
|
||||
|
||||
@@ -219,7 +210,7 @@ struct AttrDb
|
||||
|
||||
state->insertAttribute.use()
|
||||
(key.first)
|
||||
(symbols[key.second])
|
||||
(key.second)
|
||||
(AttrType::Placeholder)
|
||||
(0, false).exec();
|
||||
|
||||
@@ -235,7 +226,7 @@ struct AttrDb
|
||||
|
||||
state->insertAttribute.use()
|
||||
(key.first)
|
||||
(symbols[key.second])
|
||||
(key.second)
|
||||
(AttrType::Missing)
|
||||
(0, false).exec();
|
||||
|
||||
@@ -251,7 +242,7 @@ struct AttrDb
|
||||
|
||||
state->insertAttribute.use()
|
||||
(key.first)
|
||||
(symbols[key.second])
|
||||
(key.second)
|
||||
(AttrType::Misc)
|
||||
(0, false).exec();
|
||||
|
||||
@@ -267,7 +258,7 @@ struct AttrDb
|
||||
|
||||
state->insertAttribute.use()
|
||||
(key.first)
|
||||
(symbols[key.second])
|
||||
(key.second)
|
||||
(AttrType::Failed)
|
||||
(0, false).exec();
|
||||
|
||||
@@ -279,7 +270,7 @@ struct AttrDb
|
||||
{
|
||||
auto state(_state->lock());
|
||||
|
||||
auto queryAttribute(state->queryAttribute.use()(key.first)(symbols[key.second]));
|
||||
auto queryAttribute(state->queryAttribute.use()(key.first)(key.second));
|
||||
if (!queryAttribute.next()) return {};
|
||||
|
||||
auto rowId = (AttrId) queryAttribute.getInt(0);
|
||||
@@ -290,10 +281,10 @@ struct AttrDb
|
||||
return {{rowId, placeholder_t()}};
|
||||
case AttrType::FullAttrs: {
|
||||
// FIXME: expensive, should separate this out.
|
||||
std::vector<Symbol> attrs;
|
||||
fullattr_t attrs;
|
||||
auto queryAttributes(state->queryAttributes.use()(rowId));
|
||||
while (queryAttributes.next())
|
||||
attrs.emplace_back(symbols.create(queryAttributes.getStr(0)));
|
||||
attrs.p.emplace_back(queryAttributes.getStr(0));
|
||||
return {{rowId, attrs}};
|
||||
}
|
||||
case AttrType::String: {
|
||||
@@ -321,44 +312,39 @@ struct AttrDb
|
||||
}
|
||||
};
|
||||
|
||||
static std::shared_ptr<AttrDb> makeAttrDb(
|
||||
const Store & cfg,
|
||||
const Hash & fingerprint,
|
||||
SymbolTable & symbols)
|
||||
static std::shared_ptr<AttrDb> makeAttrDb(const Hash & fingerprint)
|
||||
{
|
||||
try {
|
||||
return std::make_shared<AttrDb>(cfg, fingerprint, symbols);
|
||||
return std::make_shared<AttrDb>(fingerprint);
|
||||
} catch (SQLiteError &) {
|
||||
ignoreExceptionExceptInterrupt();
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
ref<EvalCache> CachingEvalState::getCacheFor(Hash hash, RootLoader rootLoader)
|
||||
ref<EvalCache> CachingEvaluator::getCacheFor(Hash hash, RootLoader rootLoader)
|
||||
{
|
||||
if (auto it = caches.find(hash); it != caches.end()) {
|
||||
return it->second;
|
||||
}
|
||||
auto cache = make_ref<EvalCache>(hash, *this, rootLoader);
|
||||
auto cache = make_ref<EvalCache>(hash, rootLoader);
|
||||
caches.emplace(hash, cache);
|
||||
return cache;
|
||||
}
|
||||
|
||||
EvalCache::EvalCache(
|
||||
std::optional<std::reference_wrapper<const Hash>> useCache,
|
||||
EvalState & state,
|
||||
RootLoader rootLoader)
|
||||
: db(useCache ? makeAttrDb(*state.store, *useCache, state.symbols) : nullptr)
|
||||
, state(state)
|
||||
: db(useCache ? makeAttrDb(*useCache) : nullptr)
|
||||
, rootLoader(rootLoader)
|
||||
{
|
||||
}
|
||||
|
||||
Value * EvalCache::getRootValue()
|
||||
Value * EvalCache::getRootValue(EvalState & state)
|
||||
{
|
||||
if (!value) {
|
||||
debug("getting root value");
|
||||
value = allocRootValue(rootLoader());
|
||||
value = allocRootValue(rootLoader(state));
|
||||
}
|
||||
return *value;
|
||||
}
|
||||
@@ -382,7 +368,7 @@ AttrCursor::AttrCursor(
|
||||
AttrKey AttrCursor::getKey()
|
||||
{
|
||||
if (!parent)
|
||||
return {0, root->state.s.epsilon};
|
||||
return {0, ""};
|
||||
if (!parent->first->cachedValue) {
|
||||
parent->first->cachedValue = root->db->getAttr(parent->first->getKey());
|
||||
assert(parent->first->cachedValue);
|
||||
@@ -390,59 +376,59 @@ AttrKey AttrCursor::getKey()
|
||||
return {parent->first->cachedValue->first, parent->second};
|
||||
}
|
||||
|
||||
Value & AttrCursor::getValue()
|
||||
Value & AttrCursor::getValue(EvalState & state)
|
||||
{
|
||||
if (!_value) {
|
||||
if (parent) {
|
||||
auto & vParent = parent->first->getValue();
|
||||
root->state.forceAttrs(vParent, noPos, "while searching for an attribute");
|
||||
auto attr = vParent.attrs->get(parent->second);
|
||||
auto & vParent = parent->first->getValue(state);
|
||||
state.forceAttrs(vParent, noPos, "while searching for an attribute");
|
||||
auto attr = vParent.attrs->get(state.ctx.symbols.create(parent->second));
|
||||
if (!attr)
|
||||
throw Error("attribute '%s' is unexpectedly missing", getAttrPathStr());
|
||||
throw Error("attribute '%s' is unexpectedly missing", getAttrPathStr(state));
|
||||
_value = allocRootValue(attr->value);
|
||||
} else
|
||||
_value = allocRootValue(root->getRootValue());
|
||||
_value = allocRootValue(root->getRootValue(state));
|
||||
}
|
||||
return **_value;
|
||||
}
|
||||
|
||||
std::vector<Symbol> AttrCursor::getAttrPath() const
|
||||
std::vector<std::string> AttrCursor::getAttrPath(EvalState & state) const
|
||||
{
|
||||
if (parent) {
|
||||
auto attrPath = parent->first->getAttrPath();
|
||||
auto attrPath = parent->first->getAttrPath(state);
|
||||
attrPath.push_back(parent->second);
|
||||
return attrPath;
|
||||
} else
|
||||
return {};
|
||||
}
|
||||
|
||||
std::vector<Symbol> AttrCursor::getAttrPath(Symbol name) const
|
||||
std::vector<std::string> AttrCursor::getAttrPath(EvalState & state, std::string_view name) const
|
||||
{
|
||||
auto attrPath = getAttrPath();
|
||||
attrPath.push_back(name);
|
||||
auto attrPath = getAttrPath(state);
|
||||
attrPath.emplace_back(name);
|
||||
return attrPath;
|
||||
}
|
||||
|
||||
std::string AttrCursor::getAttrPathStr() const
|
||||
std::string AttrCursor::getAttrPathStr(EvalState & state) const
|
||||
{
|
||||
return concatStringsSep(".", root->state.symbols.resolve(getAttrPath()));
|
||||
return concatStringsSep(".", getAttrPath(state));
|
||||
}
|
||||
|
||||
std::string AttrCursor::getAttrPathStr(Symbol name) const
|
||||
std::string AttrCursor::getAttrPathStr(EvalState & state, std::string_view name) const
|
||||
{
|
||||
return concatStringsSep(".", root->state.symbols.resolve(getAttrPath(name)));
|
||||
return concatStringsSep(".", getAttrPath(state, name));
|
||||
}
|
||||
|
||||
Value & AttrCursor::forceValue()
|
||||
Value & AttrCursor::forceValue(EvalState & state)
|
||||
{
|
||||
debug("evaluating uncached attribute '%s'", getAttrPathStr());
|
||||
debug("evaluating uncached attribute '%s'", getAttrPathStr(state));
|
||||
|
||||
auto & v = getValue();
|
||||
auto & v = getValue(state);
|
||||
|
||||
try {
|
||||
root->state.forceValue(v, noPos);
|
||||
state.forceValue(v, noPos);
|
||||
} catch (EvalError &) {
|
||||
debug("setting '%s' to failed", getAttrPathStr());
|
||||
debug("setting '%s' to failed", getAttrPathStr(state));
|
||||
if (root->db)
|
||||
cachedValue = {root->db->setFailed(getKey()), failed_t()};
|
||||
throw;
|
||||
@@ -453,8 +439,8 @@ Value & AttrCursor::forceValue()
|
||||
cachedValue = {root->db->setString(getKey(), v.string.s, v.string.context),
|
||||
string_t{v.string.s, {}}};
|
||||
else if (v.type() == nPath) {
|
||||
auto path = v.path().path;
|
||||
cachedValue = {root->db->setString(getKey(), path.abs()), string_t{path.abs(), {}}};
|
||||
auto path = v.path().canonical().abs();
|
||||
cachedValue = {root->db->setString(getKey(), path), string_t{path, {}}};
|
||||
}
|
||||
else if (v.type() == nBool)
|
||||
cachedValue = {root->db->setBool(getKey(), v.boolean), v.boolean};
|
||||
@@ -469,27 +455,23 @@ Value & AttrCursor::forceValue()
|
||||
return v;
|
||||
}
|
||||
|
||||
Suggestions AttrCursor::getSuggestionsForAttr(Symbol name)
|
||||
Suggestions AttrCursor::getSuggestionsForAttr(EvalState & state, const std::string & name)
|
||||
{
|
||||
auto attrNames = getAttrs();
|
||||
std::set<std::string> strAttrNames;
|
||||
for (auto & name : attrNames)
|
||||
strAttrNames.insert(root->state.symbols[name]);
|
||||
|
||||
return Suggestions::bestMatches(strAttrNames, root->state.symbols[name]);
|
||||
auto attrNames = getAttrs(state);
|
||||
return Suggestions::bestMatches({attrNames.begin(), attrNames.end()}, name);
|
||||
}
|
||||
|
||||
std::shared_ptr<AttrCursor> AttrCursor::maybeGetAttr(Symbol name)
|
||||
std::shared_ptr<AttrCursor> AttrCursor::maybeGetAttr(EvalState & state, const std::string & name)
|
||||
{
|
||||
if (root->db) {
|
||||
if (!cachedValue)
|
||||
cachedValue = root->db->getAttr(getKey());
|
||||
|
||||
if (cachedValue) {
|
||||
if (auto attrs = std::get_if<std::vector<Symbol>>(&cachedValue->second)) {
|
||||
for (auto & attr : *attrs)
|
||||
if (auto attrs = std::get_if<fullattr_t>(&cachedValue->second)) {
|
||||
for (auto & attr : attrs->p)
|
||||
if (attr == name)
|
||||
return std::make_shared<AttrCursor>(root, std::make_pair(shared_from_this(), attr));
|
||||
return std::make_shared<AttrCursor>(root, std::make_pair(shared_from_this(), name));
|
||||
return nullptr;
|
||||
} else if (std::get_if<placeholder_t>(&cachedValue->second)) {
|
||||
auto attr = root->db->getAttr({cachedValue->first, name});
|
||||
@@ -497,7 +479,7 @@ std::shared_ptr<AttrCursor> AttrCursor::maybeGetAttr(Symbol name)
|
||||
if (std::get_if<missing_t>(&attr->second))
|
||||
return nullptr;
|
||||
else if (std::get_if<failed_t>(&attr->second)) {
|
||||
debug("reevaluating failed cached attribute '%s'", getAttrPathStr(name));
|
||||
debug("reevaluating failed cached attribute '%s'", getAttrPathStr(state, name));
|
||||
} else
|
||||
return std::make_shared<AttrCursor>(root,
|
||||
std::make_pair(shared_from_this(), name), nullptr, std::move(attr));
|
||||
@@ -506,17 +488,17 @@ std::shared_ptr<AttrCursor> AttrCursor::maybeGetAttr(Symbol name)
|
||||
// evaluate to see whether 'name' exists
|
||||
} else
|
||||
return nullptr;
|
||||
//error<TypeError>("'%s' is not an attribute set", getAttrPathStr()).debugThrow();
|
||||
//errors.make<TypeError>("'%s' is not an attribute set", getAttrPathStr()).debugThrow();
|
||||
}
|
||||
}
|
||||
|
||||
auto & v = forceValue();
|
||||
auto & v = forceValue(state);
|
||||
|
||||
if (v.type() != nAttrs)
|
||||
return nullptr;
|
||||
//error<TypeError>("'%s' is not an attribute set", getAttrPathStr()).debugThrow();
|
||||
//errors.make<TypeError>("'%s' is not an attribute set", getAttrPathStr()).debugThrow();
|
||||
|
||||
auto attr = v.attrs->get(name);
|
||||
auto attr = v.attrs->get(state.ctx.symbols.create(name));
|
||||
|
||||
if (!attr) {
|
||||
if (root->db) {
|
||||
@@ -538,31 +520,21 @@ std::shared_ptr<AttrCursor> AttrCursor::maybeGetAttr(Symbol name)
|
||||
root, std::make_pair(shared_from_this(), name), attr->value, std::move(cachedValue2));
|
||||
}
|
||||
|
||||
std::shared_ptr<AttrCursor> AttrCursor::maybeGetAttr(std::string_view name)
|
||||
ref<AttrCursor> AttrCursor::getAttr(EvalState & state, const std::string & name)
|
||||
{
|
||||
return maybeGetAttr(root->state.symbols.create(name));
|
||||
}
|
||||
|
||||
ref<AttrCursor> AttrCursor::getAttr(Symbol name)
|
||||
{
|
||||
auto p = maybeGetAttr(name);
|
||||
auto p = maybeGetAttr(state, name);
|
||||
if (!p)
|
||||
throw Error("attribute '%s' does not exist", getAttrPathStr(name));
|
||||
throw Error("attribute '%s' does not exist", getAttrPathStr(state, name));
|
||||
return ref(p);
|
||||
}
|
||||
|
||||
ref<AttrCursor> AttrCursor::getAttr(std::string_view name)
|
||||
{
|
||||
return getAttr(root->state.symbols.create(name));
|
||||
}
|
||||
|
||||
OrSuggestions<ref<AttrCursor>> AttrCursor::findAlongAttrPath(const std::vector<Symbol> & attrPath)
|
||||
OrSuggestions<ref<AttrCursor>> AttrCursor::findAlongAttrPath(EvalState & state, const std::vector<std::string> & attrPath)
|
||||
{
|
||||
auto res = shared_from_this();
|
||||
for (auto & attr : attrPath) {
|
||||
auto child = res->maybeGetAttr(attr);
|
||||
auto child = res->maybeGetAttr(state, attr);
|
||||
if (!child) {
|
||||
auto suggestions = res->getSuggestionsForAttr(attr);
|
||||
auto suggestions = res->getSuggestionsForAttr(state, attr);
|
||||
return OrSuggestions<ref<AttrCursor>>::failed(suggestions);
|
||||
}
|
||||
res = child;
|
||||
@@ -570,30 +542,30 @@ OrSuggestions<ref<AttrCursor>> AttrCursor::findAlongAttrPath(const std::vector<S
|
||||
return ref(res);
|
||||
}
|
||||
|
||||
std::string AttrCursor::getString()
|
||||
std::string AttrCursor::getString(EvalState & state)
|
||||
{
|
||||
if (root->db) {
|
||||
if (!cachedValue)
|
||||
cachedValue = root->db->getAttr(getKey());
|
||||
if (cachedValue && !std::get_if<placeholder_t>(&cachedValue->second)) {
|
||||
if (auto s = std::get_if<string_t>(&cachedValue->second)) {
|
||||
debug("using cached string attribute '%s'", getAttrPathStr());
|
||||
debug("using cached string attribute '%s'", getAttrPathStr(state));
|
||||
return s->first;
|
||||
} else
|
||||
root->state.error<TypeError>("'%s' is not a string", getAttrPathStr()).debugThrow();
|
||||
state.ctx.errors.make<TypeError>("'%s' is not a string", getAttrPathStr(state)).debugThrow();
|
||||
}
|
||||
}
|
||||
|
||||
auto & v = forceValue();
|
||||
auto & v = forceValue(state);
|
||||
|
||||
if (v.type() != nString && v.type() != nPath) {
|
||||
root->state.error<TypeError>("'%s' is not a string but %s", getAttrPathStr(), v.type()).debugThrow();
|
||||
state.ctx.errors.make<TypeError>("'%s' is not a string but %s", getAttrPathStr(state), v.type()).debugThrow();
|
||||
}
|
||||
|
||||
return v.type() == nString ? v.string.s : v.path().to_string();
|
||||
}
|
||||
|
||||
string_t AttrCursor::getStringWithContext()
|
||||
string_t AttrCursor::getStringWithContext(EvalState & state)
|
||||
{
|
||||
if (root->db) {
|
||||
if (!cachedValue)
|
||||
@@ -613,21 +585,21 @@ string_t AttrCursor::getStringWithContext()
|
||||
return o.path;
|
||||
},
|
||||
}, c.raw);
|
||||
if (!root->state.store->isValidPath(path)) {
|
||||
if (!state.ctx.store->isValidPath(path)) {
|
||||
valid = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (valid) {
|
||||
debug("using cached string attribute '%s'", getAttrPathStr());
|
||||
debug("using cached string attribute '%s'", getAttrPathStr(state));
|
||||
return *s;
|
||||
}
|
||||
} else
|
||||
root->state.error<TypeError>("'%s' is not a string", getAttrPathStr()).debugThrow();
|
||||
state.ctx.errors.make<TypeError>("'%s' is not a string", getAttrPathStr(state)).debugThrow();
|
||||
}
|
||||
}
|
||||
|
||||
auto & v = forceValue();
|
||||
auto & v = forceValue(state);
|
||||
|
||||
if (v.type() == nString) {
|
||||
NixStringContext context;
|
||||
@@ -636,80 +608,80 @@ string_t AttrCursor::getStringWithContext()
|
||||
} else if (v.type() == nPath) {
|
||||
return {v.path().to_string(), {}};
|
||||
} else {
|
||||
root->state.error<TypeError>("'%s' is not a string but %s", getAttrPathStr(), v.type()).debugThrow();
|
||||
state.ctx.errors.make<TypeError>("'%s' is not a string but %s", getAttrPathStr(state), v.type()).debugThrow();
|
||||
}
|
||||
}
|
||||
|
||||
bool AttrCursor::getBool()
|
||||
bool AttrCursor::getBool(EvalState & state)
|
||||
{
|
||||
if (root->db) {
|
||||
if (!cachedValue)
|
||||
cachedValue = root->db->getAttr(getKey());
|
||||
if (cachedValue && !std::get_if<placeholder_t>(&cachedValue->second)) {
|
||||
if (auto b = std::get_if<bool>(&cachedValue->second)) {
|
||||
debug("using cached Boolean attribute '%s'", getAttrPathStr());
|
||||
debug("using cached Boolean attribute '%s'", getAttrPathStr(state));
|
||||
return *b;
|
||||
} else
|
||||
root->state.error<TypeError>("'%s' is not a Boolean", getAttrPathStr()).debugThrow();
|
||||
state.ctx.errors.make<TypeError>("'%s' is not a Boolean", getAttrPathStr(state)).debugThrow();
|
||||
}
|
||||
}
|
||||
|
||||
auto & v = forceValue();
|
||||
auto & v = forceValue(state);
|
||||
|
||||
if (v.type() != nBool)
|
||||
root->state.error<TypeError>("'%s' is not a Boolean", getAttrPathStr()).debugThrow();
|
||||
state.ctx.errors.make<TypeError>("'%s' is not a Boolean", getAttrPathStr(state)).debugThrow();
|
||||
|
||||
return v.boolean;
|
||||
}
|
||||
|
||||
NixInt AttrCursor::getInt()
|
||||
NixInt AttrCursor::getInt(EvalState & state)
|
||||
{
|
||||
if (root->db) {
|
||||
if (!cachedValue)
|
||||
cachedValue = root->db->getAttr(getKey());
|
||||
if (cachedValue && !std::get_if<placeholder_t>(&cachedValue->second)) {
|
||||
if (auto i = std::get_if<int_t>(&cachedValue->second)) {
|
||||
debug("using cached integer attribute '%s'", getAttrPathStr());
|
||||
debug("using cached integer attribute '%s'", getAttrPathStr(state));
|
||||
return i->x;
|
||||
} else
|
||||
root->state.error<TypeError>("'%s' is not an integer", getAttrPathStr()).debugThrow();
|
||||
state.ctx.errors.make<TypeError>("'%s' is not an integer", getAttrPathStr(state)).debugThrow();
|
||||
}
|
||||
}
|
||||
|
||||
auto & v = forceValue();
|
||||
auto & v = forceValue(state);
|
||||
|
||||
if (v.type() != nInt)
|
||||
root->state.error<TypeError>("'%s' is not an integer", getAttrPathStr()).debugThrow();
|
||||
state.ctx.errors.make<TypeError>("'%s' is not an integer", getAttrPathStr(state)).debugThrow();
|
||||
|
||||
return v.integer;
|
||||
}
|
||||
|
||||
std::vector<std::string> AttrCursor::getListOfStrings()
|
||||
std::vector<std::string> AttrCursor::getListOfStrings(EvalState & state)
|
||||
{
|
||||
if (root->db) {
|
||||
if (!cachedValue)
|
||||
cachedValue = root->db->getAttr(getKey());
|
||||
if (cachedValue && !std::get_if<placeholder_t>(&cachedValue->second)) {
|
||||
if (auto l = std::get_if<std::vector<std::string>>(&cachedValue->second)) {
|
||||
debug("using cached list of strings attribute '%s'", getAttrPathStr());
|
||||
debug("using cached list of strings attribute '%s'", getAttrPathStr(state));
|
||||
return *l;
|
||||
} else
|
||||
root->state.error<TypeError>("'%s' is not a list of strings", getAttrPathStr()).debugThrow();
|
||||
state.ctx.errors.make<TypeError>("'%s' is not a list of strings", getAttrPathStr(state)).debugThrow();
|
||||
}
|
||||
}
|
||||
|
||||
debug("evaluating uncached attribute '%s'", getAttrPathStr());
|
||||
debug("evaluating uncached attribute '%s'", getAttrPathStr(state));
|
||||
|
||||
auto & v = getValue();
|
||||
root->state.forceValue(v, noPos);
|
||||
auto & v = getValue(state);
|
||||
state.forceValue(v, noPos);
|
||||
|
||||
if (v.type() != nList)
|
||||
root->state.error<TypeError>("'%s' is not a list", getAttrPathStr()).debugThrow();
|
||||
state.ctx.errors.make<TypeError>("'%s' is not a list", getAttrPathStr(state)).debugThrow();
|
||||
|
||||
std::vector<std::string> res;
|
||||
|
||||
for (auto & elem : v.listItems())
|
||||
res.push_back(std::string(root->state.forceStringNoCtx(*elem, noPos, "while evaluating an attribute for caching")));
|
||||
res.push_back(std::string(state.forceStringNoCtx(*elem, noPos, "while evaluating an attribute for caching")));
|
||||
|
||||
if (root->db)
|
||||
cachedValue = {root->db->setListOfStrings(getKey(), res), res};
|
||||
@@ -717,56 +689,53 @@ std::vector<std::string> AttrCursor::getListOfStrings()
|
||||
return res;
|
||||
}
|
||||
|
||||
std::vector<Symbol> AttrCursor::getAttrs()
|
||||
std::vector<std::string> AttrCursor::getAttrs(EvalState & state)
|
||||
{
|
||||
if (root->db) {
|
||||
if (!cachedValue)
|
||||
cachedValue = root->db->getAttr(getKey());
|
||||
if (cachedValue && !std::get_if<placeholder_t>(&cachedValue->second)) {
|
||||
if (auto attrs = std::get_if<std::vector<Symbol>>(&cachedValue->second)) {
|
||||
debug("using cached attrset attribute '%s'", getAttrPathStr());
|
||||
return *attrs;
|
||||
if (auto attrs = std::get_if<fullattr_t>(&cachedValue->second)) {
|
||||
debug("using cached attrset attribute '%s'", getAttrPathStr(state));
|
||||
return attrs->p;
|
||||
} else
|
||||
root->state.error<TypeError>("'%s' is not an attribute set", getAttrPathStr()).debugThrow();
|
||||
state.ctx.errors.make<TypeError>("'%s' is not an attribute set", getAttrPathStr(state)).debugThrow();
|
||||
}
|
||||
}
|
||||
|
||||
auto & v = forceValue();
|
||||
auto & v = forceValue(state);
|
||||
|
||||
if (v.type() != nAttrs)
|
||||
root->state.error<TypeError>("'%s' is not an attribute set", getAttrPathStr()).debugThrow();
|
||||
state.ctx.errors.make<TypeError>("'%s' is not an attribute set", getAttrPathStr(state)).debugThrow();
|
||||
|
||||
std::vector<Symbol> attrs;
|
||||
for (auto & attr : *getValue().attrs)
|
||||
attrs.push_back(attr.name);
|
||||
std::sort(attrs.begin(), attrs.end(), [&](Symbol a, Symbol b) {
|
||||
std::string_view sa = root->state.symbols[a], sb = root->state.symbols[b];
|
||||
return sa < sb;
|
||||
});
|
||||
fullattr_t attrs;
|
||||
for (auto & attr : *getValue(state).attrs)
|
||||
attrs.p.push_back(state.ctx.symbols[attr.name]);
|
||||
std::sort(attrs.p.begin(), attrs.p.end());
|
||||
|
||||
if (root->db)
|
||||
cachedValue = {root->db->setAttrs(getKey(), attrs), attrs};
|
||||
|
||||
return attrs;
|
||||
return attrs.p;
|
||||
}
|
||||
|
||||
bool AttrCursor::isDerivation()
|
||||
bool AttrCursor::isDerivation(EvalState & state)
|
||||
{
|
||||
auto aType = maybeGetAttr("type");
|
||||
return aType && aType->getString() == "derivation";
|
||||
auto aType = maybeGetAttr(state, "type");
|
||||
return aType && aType->getString(state) == "derivation";
|
||||
}
|
||||
|
||||
StorePath AttrCursor::forceDerivation()
|
||||
StorePath AttrCursor::forceDerivation(EvalState & state)
|
||||
{
|
||||
auto aDrvPath = getAttr(root->state.s.drvPath);
|
||||
auto drvPath = root->state.store->parseStorePath(aDrvPath->getString());
|
||||
if (!root->state.store->isValidPath(drvPath) && !settings.readOnlyMode) {
|
||||
auto aDrvPath = getAttr(state, "drvPath");
|
||||
auto drvPath = state.ctx.store->parseStorePath(aDrvPath->getString(state));
|
||||
if (!state.ctx.store->isValidPath(drvPath) && !settings.readOnlyMode) {
|
||||
/* The eval cache contains 'drvPath', but the actual path has
|
||||
been garbage-collected. So force it to be regenerated. */
|
||||
aDrvPath->forceValue();
|
||||
if (!root->state.store->isValidPath(drvPath))
|
||||
aDrvPath->forceValue(state);
|
||||
if (!state.ctx.store->isValidPath(drvPath))
|
||||
throw Error("don't know how to recreate store derivation '%s'!",
|
||||
root->state.store->printStorePath(drvPath));
|
||||
state.ctx.store->printStorePath(drvPath));
|
||||
}
|
||||
return drvPath;
|
||||
}
|
||||
|
||||
+26
-31
@@ -13,7 +13,7 @@ namespace nix::eval_cache {
|
||||
struct AttrDb;
|
||||
class AttrCursor;
|
||||
|
||||
typedef std::function<Value *()> RootLoader;
|
||||
typedef std::function<Value *(EvalState &)> RootLoader;
|
||||
|
||||
/**
|
||||
* EvalState with caching support. Historically this was part of EvalState,
|
||||
@@ -22,7 +22,7 @@ typedef std::function<Value *()> RootLoader;
|
||||
* the rather strong connection between EvalState and these caches. At some
|
||||
* future time the cache interface should be changed to hide its EvalState.
|
||||
*/
|
||||
class CachingEvalState : public EvalState
|
||||
class CachingEvaluator : public Evaluator
|
||||
{
|
||||
/**
|
||||
* A cache for evaluation caches, so as to reuse the same root value if possible
|
||||
@@ -30,7 +30,7 @@ class CachingEvalState : public EvalState
|
||||
std::map<Hash, ref<EvalCache>> caches;
|
||||
|
||||
public:
|
||||
using EvalState::EvalState;
|
||||
using Evaluator::Evaluator;
|
||||
|
||||
ref<EvalCache> getCacheFor(Hash hash, RootLoader rootLoader);
|
||||
};
|
||||
@@ -40,17 +40,15 @@ class EvalCache : public std::enable_shared_from_this<EvalCache>
|
||||
friend class AttrCursor;
|
||||
|
||||
std::shared_ptr<AttrDb> db;
|
||||
EvalState & state;
|
||||
RootLoader rootLoader;
|
||||
RootValue value;
|
||||
|
||||
Value * getRootValue();
|
||||
Value * getRootValue(EvalState & state);
|
||||
|
||||
public:
|
||||
|
||||
EvalCache(
|
||||
std::optional<std::reference_wrapper<const Hash>> useCache,
|
||||
EvalState & state,
|
||||
RootLoader rootLoader);
|
||||
|
||||
ref<AttrCursor> getRoot();
|
||||
@@ -69,16 +67,17 @@ enum AttrType {
|
||||
};
|
||||
|
||||
struct placeholder_t {};
|
||||
struct fullattr_t { std::vector<std::string> p; };
|
||||
struct missing_t {};
|
||||
struct misc_t {};
|
||||
struct failed_t {};
|
||||
struct int_t { NixInt x; };
|
||||
typedef uint64_t AttrId;
|
||||
typedef std::pair<AttrId, Symbol> AttrKey;
|
||||
typedef std::pair<AttrId, std::string> AttrKey;
|
||||
typedef std::pair<std::string, NixStringContext> string_t;
|
||||
|
||||
typedef std::variant<
|
||||
std::vector<Symbol>,
|
||||
fullattr_t,
|
||||
string_t,
|
||||
placeholder_t,
|
||||
missing_t,
|
||||
@@ -94,14 +93,14 @@ class AttrCursor : public std::enable_shared_from_this<AttrCursor>
|
||||
friend class EvalCache;
|
||||
|
||||
ref<EvalCache> root;
|
||||
typedef std::optional<std::pair<std::shared_ptr<AttrCursor>, Symbol>> Parent;
|
||||
typedef std::optional<std::pair<std::shared_ptr<AttrCursor>, std::string>> Parent;
|
||||
Parent parent;
|
||||
RootValue _value;
|
||||
std::optional<std::pair<AttrId, AttrValue>> cachedValue;
|
||||
|
||||
AttrKey getKey();
|
||||
|
||||
Value & getValue();
|
||||
Value & getValue(EvalState & state);
|
||||
|
||||
public:
|
||||
|
||||
@@ -111,50 +110,46 @@ public:
|
||||
Value * value = nullptr,
|
||||
std::optional<std::pair<AttrId, AttrValue>> && cachedValue = {});
|
||||
|
||||
std::vector<Symbol> getAttrPath() const;
|
||||
std::vector<std::string> getAttrPath(EvalState & state) const;
|
||||
|
||||
std::vector<Symbol> getAttrPath(Symbol name) const;
|
||||
std::vector<std::string> getAttrPath(EvalState & state, std::string_view name) const;
|
||||
|
||||
std::string getAttrPathStr() const;
|
||||
std::string getAttrPathStr(EvalState & state) const;
|
||||
|
||||
std::string getAttrPathStr(Symbol name) const;
|
||||
std::string getAttrPathStr(EvalState & state, std::string_view name) const;
|
||||
|
||||
Suggestions getSuggestionsForAttr(Symbol name);
|
||||
Suggestions getSuggestionsForAttr(EvalState & state, const std::string & name);
|
||||
|
||||
std::shared_ptr<AttrCursor> maybeGetAttr(Symbol name);
|
||||
std::shared_ptr<AttrCursor> maybeGetAttr(EvalState & state, const std::string & name);
|
||||
|
||||
std::shared_ptr<AttrCursor> maybeGetAttr(std::string_view name);
|
||||
|
||||
ref<AttrCursor> getAttr(Symbol name);
|
||||
|
||||
ref<AttrCursor> getAttr(std::string_view name);
|
||||
ref<AttrCursor> getAttr(EvalState & state, const std::string & name);
|
||||
|
||||
/**
|
||||
* Get an attribute along a chain of attrsets. Note that this does
|
||||
* not auto-call functors or functions.
|
||||
*/
|
||||
OrSuggestions<ref<AttrCursor>> findAlongAttrPath(const std::vector<Symbol> & attrPath);
|
||||
OrSuggestions<ref<AttrCursor>> findAlongAttrPath(EvalState & state, const std::vector<std::string> & attrPath);
|
||||
|
||||
std::string getString();
|
||||
std::string getString(EvalState & state);
|
||||
|
||||
string_t getStringWithContext();
|
||||
string_t getStringWithContext(EvalState & state);
|
||||
|
||||
bool getBool();
|
||||
bool getBool(EvalState & state);
|
||||
|
||||
NixInt getInt();
|
||||
NixInt getInt(EvalState & state);
|
||||
|
||||
std::vector<std::string> getListOfStrings();
|
||||
std::vector<std::string> getListOfStrings(EvalState & state);
|
||||
|
||||
std::vector<Symbol> getAttrs();
|
||||
std::vector<std::string> getAttrs(EvalState & state);
|
||||
|
||||
bool isDerivation();
|
||||
bool isDerivation(EvalState & state);
|
||||
|
||||
Value & forceValue();
|
||||
Value & forceValue(EvalState & state);
|
||||
|
||||
/**
|
||||
* Force creation of the .drv file in the Nix store.
|
||||
*/
|
||||
StorePath forceDerivation();
|
||||
StorePath forceDerivation(EvalState & state);
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
+32
-39
@@ -5,91 +5,84 @@
|
||||
namespace nix {
|
||||
|
||||
template<class T>
|
||||
EvalErrorBuilder<T> & EvalErrorBuilder<T>::withExitStatus(unsigned int exitStatus)
|
||||
EvalErrorBuilder<T> EvalErrorBuilder<T>::withExitStatus(unsigned int exitStatus) &&
|
||||
{
|
||||
error.withExitStatus(exitStatus);
|
||||
return *this;
|
||||
error->withExitStatus(exitStatus);
|
||||
return std::move(*this);
|
||||
}
|
||||
|
||||
template<class T>
|
||||
EvalErrorBuilder<T> & EvalErrorBuilder<T>::atPos(PosIdx pos)
|
||||
EvalErrorBuilder<T> EvalErrorBuilder<T>::atPos(PosIdx pos) &&
|
||||
{
|
||||
error.err.pos = state.positions[pos];
|
||||
return *this;
|
||||
error->err.pos = positions[pos];
|
||||
return std::move(*this);
|
||||
}
|
||||
|
||||
template<class T>
|
||||
EvalErrorBuilder<T> & EvalErrorBuilder<T>::atPos(Value & value, PosIdx fallback)
|
||||
EvalErrorBuilder<T> EvalErrorBuilder<T>::atPos(Value & value, PosIdx fallback) &&
|
||||
{
|
||||
return atPos(value.determinePos(fallback));
|
||||
return std::move(*this).atPos(value.determinePos(fallback));
|
||||
}
|
||||
|
||||
template<class T>
|
||||
EvalErrorBuilder<T> & EvalErrorBuilder<T>::withTrace(PosIdx pos, const std::string_view text)
|
||||
EvalErrorBuilder<T> EvalErrorBuilder<T>::withTrace(PosIdx pos, const std::string_view text) &&
|
||||
{
|
||||
error.err.traces.push_front(
|
||||
Trace{.pos = state.positions[pos], .hint = HintFmt(std::string(text))});
|
||||
return *this;
|
||||
error->err.traces.push_front(
|
||||
Trace{.pos = positions[pos], .hint = HintFmt(std::string(text))});
|
||||
return std::move(*this);
|
||||
}
|
||||
|
||||
template<class T>
|
||||
EvalErrorBuilder<T> & EvalErrorBuilder<T>::withSuggestions(Suggestions & s)
|
||||
EvalErrorBuilder<T> EvalErrorBuilder<T>::withSuggestions(Suggestions & s) &&
|
||||
{
|
||||
error.err.suggestions = s;
|
||||
return *this;
|
||||
error->err.suggestions = s;
|
||||
return std::move(*this);
|
||||
}
|
||||
|
||||
template<class T>
|
||||
EvalErrorBuilder<T> & EvalErrorBuilder<T>::withFrame(const Env & env, const Expr & expr)
|
||||
EvalErrorBuilder<T> EvalErrorBuilder<T>::withFrame(const Env & env, const Expr & expr) &&
|
||||
{
|
||||
if (state.debug) {
|
||||
error.frame = state.debug->addTrace(DebugTrace{
|
||||
.pos = state.positions[expr.getPos()],
|
||||
if (debug) {
|
||||
error->frame = debug->addTrace(DebugTrace{
|
||||
.pos = positions[expr.getPos()],
|
||||
.expr = expr,
|
||||
.env = env,
|
||||
.hint = HintFmt("Fake frame for debugging purposes"),
|
||||
.isError = true
|
||||
}).entry;
|
||||
}
|
||||
return *this;
|
||||
return std::move(*this);
|
||||
}
|
||||
|
||||
template<class T>
|
||||
EvalErrorBuilder<T> & EvalErrorBuilder<T>::addTrace(PosIdx pos, HintFmt hint)
|
||||
EvalErrorBuilder<T> EvalErrorBuilder<T>::addTrace(PosIdx pos, HintFmt hint) &&
|
||||
{
|
||||
error.addTrace(state.positions[pos], hint);
|
||||
return *this;
|
||||
error->addTrace(positions[pos], hint);
|
||||
return std::move(*this);
|
||||
}
|
||||
|
||||
template<class T>
|
||||
template<typename... Args>
|
||||
EvalErrorBuilder<T> &
|
||||
EvalErrorBuilder<T>::addTrace(PosIdx pos, std::string_view formatString, const Args &... formatArgs)
|
||||
EvalErrorBuilder<T>
|
||||
EvalErrorBuilder<T>::addTrace(PosIdx pos, std::string_view formatString, const Args &... formatArgs) &&
|
||||
{
|
||||
|
||||
addTrace(state.positions[pos], HintFmt(std::string(formatString), formatArgs...));
|
||||
return *this;
|
||||
addTrace(positions[pos], HintFmt(std::string(formatString), formatArgs...));
|
||||
return std::move(*this);
|
||||
}
|
||||
|
||||
template<class T>
|
||||
void EvalErrorBuilder<T>::debugThrow()
|
||||
void EvalErrorBuilder<T>::debugThrow() &&
|
||||
{
|
||||
if (state.debug) {
|
||||
if (auto last = state.debug->traces().next()) {
|
||||
if (debug) {
|
||||
if (auto last = debug->traces().next()) {
|
||||
const Env * env = &(*last)->env;
|
||||
const Expr * expr = &(*last)->expr;
|
||||
state.debug->runDebugRepl(state, &error, *env, *expr);
|
||||
debug->onEvalError(error.get(), *env, *expr);
|
||||
}
|
||||
}
|
||||
|
||||
// `EvalState` is the only class that can construct an `EvalErrorBuilder`,
|
||||
// and it does so in dynamic storage. This is the final method called on
|
||||
// any such instance and must delete itself before throwing the underlying
|
||||
// error.
|
||||
auto error = std::move(this->error);
|
||||
delete this;
|
||||
|
||||
throw error;
|
||||
throw *error;
|
||||
}
|
||||
|
||||
template class EvalErrorBuilder<EvalError>;
|
||||
|
||||
+23
-25
@@ -1,12 +1,15 @@
|
||||
#pragma once
|
||||
///@file
|
||||
|
||||
#include "lix/libutil/box_ptr.hh"
|
||||
#include "lix/libutil/error.hh"
|
||||
#include "lix/libutil/types.hh"
|
||||
#include "lix/libexpr/pos-idx.hh"
|
||||
#include "lix/libexpr/pos-table.hh"
|
||||
|
||||
namespace nix {
|
||||
|
||||
struct DebugState;
|
||||
struct DebugTrace;
|
||||
struct Env;
|
||||
struct Expr;
|
||||
@@ -51,49 +54,44 @@ public:
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* `EvalErrorBuilder`s may only be constructed by `EvalState`. The `debugThrow`
|
||||
* method must be the final method in any such `EvalErrorBuilder` usage, and it
|
||||
* handles deleting the object.
|
||||
*/
|
||||
template<class T>
|
||||
class EvalErrorBuilder final
|
||||
class [[nodiscard]] EvalErrorBuilder final
|
||||
{
|
||||
friend class EvalState;
|
||||
|
||||
EvalState & state;
|
||||
const PosTable & positions;
|
||||
DebugState * debug;
|
||||
box_ptr<T> error;
|
||||
|
||||
public:
|
||||
template<typename... Args>
|
||||
explicit EvalErrorBuilder(EvalState & state, const Args &... args)
|
||||
: state(state), error(T(args...))
|
||||
explicit EvalErrorBuilder(const PosTable & positions, DebugState * debug, const Args &... args)
|
||||
: positions(positions)
|
||||
, debug{debug}
|
||||
, error(make_box_ptr<T>(args...))
|
||||
{
|
||||
}
|
||||
|
||||
public:
|
||||
T error;
|
||||
[[gnu::noinline]] EvalErrorBuilder<T> withExitStatus(unsigned int exitStatus) &&;
|
||||
|
||||
[[nodiscard, gnu::noinline]] EvalErrorBuilder<T> & withExitStatus(unsigned int exitStatus);
|
||||
[[gnu::noinline]] EvalErrorBuilder<T> atPos(PosIdx pos) &&;
|
||||
|
||||
[[nodiscard, gnu::noinline]] EvalErrorBuilder<T> & atPos(PosIdx pos);
|
||||
[[gnu::noinline]] EvalErrorBuilder<T> atPos(Value & value, PosIdx fallback = noPos) &&;
|
||||
|
||||
[[nodiscard, gnu::noinline]] EvalErrorBuilder<T> & atPos(Value & value, PosIdx fallback = noPos);
|
||||
[[gnu::noinline]] EvalErrorBuilder<T> withTrace(PosIdx pos, const std::string_view text) &&;
|
||||
|
||||
[[nodiscard, gnu::noinline]] EvalErrorBuilder<T> & withTrace(PosIdx pos, const std::string_view text);
|
||||
[[gnu::noinline]] EvalErrorBuilder<T> withSuggestions(Suggestions & s) &&;
|
||||
|
||||
[[nodiscard, gnu::noinline]] EvalErrorBuilder<T> & withSuggestions(Suggestions & s);
|
||||
[[gnu::noinline]] EvalErrorBuilder<T> withFrame(const Env & e, const Expr & ex) &&;
|
||||
|
||||
[[nodiscard, gnu::noinline]] EvalErrorBuilder<T> & withFrame(const Env & e, const Expr & ex);
|
||||
|
||||
[[nodiscard, gnu::noinline]] EvalErrorBuilder<T> & addTrace(PosIdx pos, HintFmt hint);
|
||||
[[gnu::noinline]] EvalErrorBuilder<T> addTrace(PosIdx pos, HintFmt hint) &&;
|
||||
|
||||
template<typename... Args>
|
||||
[[nodiscard, gnu::noinline]] EvalErrorBuilder<T> &
|
||||
addTrace(PosIdx pos, std::string_view formatString, const Args &... formatArgs);
|
||||
[[gnu::noinline]] EvalErrorBuilder<T>
|
||||
addTrace(PosIdx pos, std::string_view formatString, const Args &... formatArgs) &&;
|
||||
|
||||
/**
|
||||
* Delete the `EvalErrorBuilder` and throw the underlying exception.
|
||||
* Throw the underlying exception.
|
||||
*/
|
||||
[[gnu::noinline, gnu::noreturn]] void debugThrow();
|
||||
[[gnu::noinline, gnu::noreturn]] void debugThrow() &&;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@@ -91,7 +91,7 @@ inline void EvalState::forceAttrs(Value & v, const PosIdx pos, std::string_view
|
||||
{
|
||||
forceValue(v, pos);
|
||||
if (v.type() != nAttrs) {
|
||||
error<TypeError>(
|
||||
ctx.errors.make<TypeError>(
|
||||
"expected a set but found %1%: %2%",
|
||||
showType(v),
|
||||
ValuePrinter(*this, v, errorPrintOptions)
|
||||
@@ -105,7 +105,7 @@ inline void EvalState::forceList(Value & v, const PosIdx pos, std::string_view e
|
||||
{
|
||||
forceValue(v, pos);
|
||||
if (!v.isList()) {
|
||||
error<TypeError>(
|
||||
ctx.errors.make<TypeError>(
|
||||
"expected a list but found %1%: %2%",
|
||||
showType(v),
|
||||
ValuePrinter(*this, v, errorPrintOptions)
|
||||
|
||||
+381
-319
File diff suppressed because it is too large
Load Diff
+254
-180
@@ -4,6 +4,7 @@
|
||||
#include "lix/libexpr/attr-set.hh"
|
||||
#include "lix/libexpr/eval-error.hh"
|
||||
#include "lix/libexpr/gc-alloc.hh"
|
||||
#include "lix/libutil/box_ptr.hh"
|
||||
#include "lix/libutil/generator.hh"
|
||||
#include "lix/libutil/types.hh"
|
||||
#include "lix/libexpr/value.hh"
|
||||
@@ -130,8 +131,6 @@ void initLibExpr();
|
||||
|
||||
struct RegexCache;
|
||||
|
||||
std::shared_ptr<RegexCache> makeRegexCache();
|
||||
|
||||
struct DebugTrace {
|
||||
std::shared_ptr<Pos> pos;
|
||||
const Expr & expr;
|
||||
@@ -145,23 +144,29 @@ struct DebugState
|
||||
{
|
||||
private:
|
||||
std::weak_ptr<const DebugTrace> latestTrace;
|
||||
const PosTable & positions;
|
||||
const SymbolTable & symbols;
|
||||
|
||||
public:
|
||||
std::function<ReplExitStatus(EvalState & es, ValMap const & extraEnv)> repl;
|
||||
std::function<ReplExitStatus(ValMap const & extraEnv)> errorCallback;
|
||||
bool stop = false;
|
||||
bool inDebugger = false;
|
||||
std::map<const Expr *, const std::shared_ptr<const StaticEnv>> exprEnvs;
|
||||
int trylevel = 0;
|
||||
|
||||
explicit DebugState(std::function<ReplExitStatus(EvalState & es, ValMap const & extraEnv)> repl)
|
||||
: repl(repl)
|
||||
explicit DebugState(
|
||||
const PosTable & positions,
|
||||
const SymbolTable & symbols,
|
||||
std::function<ReplExitStatus(ValMap const & extraEnv)> errorCallback
|
||||
)
|
||||
: positions(positions)
|
||||
, symbols(symbols)
|
||||
, errorCallback(errorCallback)
|
||||
{
|
||||
assert(repl);
|
||||
assert(errorCallback);
|
||||
}
|
||||
|
||||
void runDebugRepl(
|
||||
EvalState & evalState, const EvalError * error, const Env & env, const Expr & expr
|
||||
);
|
||||
void onEvalError(const EvalError * error, const Env & env, const Expr & expr);
|
||||
|
||||
const std::shared_ptr<const StaticEnv> staticEnvFor(const Expr & expr) const
|
||||
{
|
||||
@@ -206,7 +211,7 @@ struct StaticSymbols
|
||||
ignoreNulls, file, line, column, functor, toString, right, wrong, structuredAttrs,
|
||||
allowedReferences, allowedRequisites, disallowedReferences, disallowedRequisites, maxSize,
|
||||
maxClosureSize, builder, args, contentAddressed, impure, outputHash, outputHashAlgo,
|
||||
outputHashMode, recurseForDerivations, description, self, epsilon, startSet, operator_, key,
|
||||
outputHashMode, recurseForDerivations, description, self, startSet, operator_, key,
|
||||
path, prefix, outputSpecified;
|
||||
|
||||
const Expr::AstSymbols exprSymbols;
|
||||
@@ -326,89 +331,84 @@ public:
|
||||
std::optional<Doc> getDoc(Value & v);
|
||||
};
|
||||
|
||||
struct CachedEvalFile;
|
||||
|
||||
class EvalState
|
||||
struct EvalRuntimeCaches
|
||||
{
|
||||
friend class EvalBuiltins;
|
||||
|
||||
public:
|
||||
SymbolTable symbols;
|
||||
PosTable positions;
|
||||
const StaticSymbols s;
|
||||
EvalMemory mem;
|
||||
|
||||
private:
|
||||
SearchPath searchPath;
|
||||
|
||||
public:
|
||||
EvalBuiltins builtins;
|
||||
RootValue vCallFlake;
|
||||
RootValue vImportedDrvToDerivation;
|
||||
|
||||
/**
|
||||
* If set, force copying files to the Nix store even if they
|
||||
* already exist there.
|
||||
* Cache used by prim_match() and other regex functions.
|
||||
*/
|
||||
RepairFlag repair;
|
||||
std::shared_ptr<RegexCache> regexes;
|
||||
|
||||
/**
|
||||
* A cache from path names to values for evalFile().
|
||||
*/
|
||||
std::map<SourcePath, std::shared_ptr<CachedEvalFile>> fileEval;
|
||||
};
|
||||
|
||||
struct EvalErrorContext
|
||||
{
|
||||
const PosTable & positions;
|
||||
DebugState * debug;
|
||||
|
||||
template<class T, typename... Args>
|
||||
[[gnu::noinline]]
|
||||
EvalErrorBuilder<T> make(const Args & ... args) {
|
||||
return EvalErrorBuilder<T>(positions, debug, args...);
|
||||
}
|
||||
};
|
||||
|
||||
class EvalPaths
|
||||
{
|
||||
ref<Store> store;
|
||||
/**
|
||||
* Store used to build stuff.
|
||||
*/
|
||||
ref<Store> buildStore;
|
||||
SearchPath searchPath_;
|
||||
EvalErrorContext & errors;
|
||||
|
||||
public:
|
||||
EvalPaths(const ref<Store> & store, const ref<Store> buildStore, SearchPath searchPath, EvalErrorContext & errors);
|
||||
|
||||
const SearchPath & searchPath() const { return searchPath_; }
|
||||
|
||||
private:
|
||||
struct AllowedPath
|
||||
{
|
||||
struct ComponentLess : std::less<>
|
||||
{
|
||||
// we'll only use this for string-likes, it's fine. trust me sis.
|
||||
using is_transparent = void;
|
||||
};
|
||||
|
||||
std::map<std::string, AllowedPath, ComponentLess> children;
|
||||
|
||||
bool allowAllChildren = false;
|
||||
};
|
||||
|
||||
/**
|
||||
* The allowed filesystem paths in restricted or pure evaluation
|
||||
* mode.
|
||||
*/
|
||||
std::optional<PathSet> allowedPaths;
|
||||
std::optional<AllowedPath> allowedPaths;
|
||||
|
||||
/**
|
||||
* Store used to materialise .drv files.
|
||||
*/
|
||||
const ref<Store> store;
|
||||
|
||||
/**
|
||||
* Store used to build stuff.
|
||||
*/
|
||||
const ref<Store> buildStore;
|
||||
|
||||
RootValue vCallFlake = nullptr;
|
||||
RootValue vImportedDrvToDerivation = nullptr;
|
||||
|
||||
std::unique_ptr<DebugState> debug;
|
||||
|
||||
template<class T, typename... Args>
|
||||
[[nodiscard, gnu::noinline]]
|
||||
EvalErrorBuilder<T> & error(const Args & ... args) {
|
||||
// `EvalErrorBuilder::debugThrow` performs the corresponding `delete`.
|
||||
return *new EvalErrorBuilder<T>(*this, args...);
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
/* Cache for calls to addToStore(); maps source paths to the store
|
||||
paths. */
|
||||
std::map<SourcePath, StorePath> srcToStore;
|
||||
|
||||
/**
|
||||
* A cache from path names to values.
|
||||
*/
|
||||
using FileEvalCache = GcMap<SourcePath, Value>;
|
||||
FileEvalCache fileEvalCache;
|
||||
|
||||
std::map<std::string, std::optional<std::string>> searchPathResolved;
|
||||
|
||||
/**
|
||||
* Cache used by checkSourcePath().
|
||||
*/
|
||||
std::unordered_map<Path, SourcePath> resolvedPaths;
|
||||
|
||||
/**
|
||||
* Cache used by prim_match().
|
||||
*/
|
||||
std::shared_ptr<RegexCache> regexCache;
|
||||
std::unordered_map<Path, CheckedSourcePath> resolvedPaths;
|
||||
|
||||
public:
|
||||
|
||||
EvalState(
|
||||
const SearchPath & _searchPath,
|
||||
ref<Store> store,
|
||||
std::shared_ptr<Store> buildStore = nullptr);
|
||||
~EvalState();
|
||||
|
||||
/**
|
||||
* Allow access to a path.
|
||||
*/
|
||||
@@ -429,7 +429,12 @@ public:
|
||||
* Check whether access to a path is allowed and throw an error if
|
||||
* not. Otherwise return the canonicalised path.
|
||||
*/
|
||||
SourcePath checkSourcePath(const SourcePath & path);
|
||||
CheckedSourcePath checkSourcePath(const SourcePath & path);
|
||||
|
||||
/**
|
||||
* If `path` refers to a directory, then append "/default.nix".
|
||||
*/
|
||||
CheckedSourcePath resolveExprPath(SourcePath path);
|
||||
|
||||
void checkURI(const std::string & uri);
|
||||
|
||||
@@ -444,11 +449,108 @@ public:
|
||||
*/
|
||||
Path toRealPath(const Path & path, const NixStringContext & context);
|
||||
|
||||
/**
|
||||
* Look up a file in the search path.
|
||||
*/
|
||||
SourcePath findFile(const std::string_view path);
|
||||
SourcePath findFile(const SearchPath & searchPath, const std::string_view path, const PosIdx pos = noPos);
|
||||
|
||||
/**
|
||||
* Try to resolve a search path value (not the optinal key part)
|
||||
*
|
||||
* If the specified search path element is a URI, download it.
|
||||
*
|
||||
* If it is not found, return `std::nullopt`
|
||||
*/
|
||||
std::optional<std::string> resolveSearchPathPath(const SearchPath::Path & path);
|
||||
|
||||
StorePath copyPathToStore(
|
||||
NixStringContext & context, const SourcePath & path, RepairFlag repair = NoRepair
|
||||
);
|
||||
|
||||
/**
|
||||
* Create a string representing a store path.
|
||||
*
|
||||
* The string is the printed store path with a context containing a
|
||||
* single `NixStringContextElem::Opaque` element of that store path.
|
||||
*/
|
||||
void mkStorePathString(const StorePath & storePath, Value & v);
|
||||
|
||||
/**
|
||||
* Realise the given context, and return a mapping from the placeholders
|
||||
* used to construct the associated value to their final store path
|
||||
*/
|
||||
[[nodiscard]] StringMap realiseContext(const NixStringContext & context);
|
||||
};
|
||||
|
||||
struct EvalStatistics
|
||||
{
|
||||
unsigned long nrLookups = 0;
|
||||
unsigned long nrAvoided = 0;
|
||||
unsigned long nrOpUpdates = 0;
|
||||
unsigned long nrOpUpdateValuesCopied = 0;
|
||||
unsigned long nrListConcats = 0;
|
||||
unsigned long nrPrimOpCalls = 0;
|
||||
unsigned long nrFunctionCalls = 0;
|
||||
unsigned long nrThunks = 0;
|
||||
|
||||
bool countCalls = false;
|
||||
|
||||
std::map<std::string, size_t> primOpCalls;
|
||||
std::map<ExprLambda *, size_t> functionCalls;
|
||||
std::map<PosIdx, size_t> attrSelects;
|
||||
|
||||
void addCall(ExprLambda & fun);
|
||||
};
|
||||
|
||||
class Evaluator
|
||||
{
|
||||
friend class EvalBuiltins;
|
||||
friend class EvalState;
|
||||
|
||||
EvalState * activeEval = nullptr;
|
||||
|
||||
public:
|
||||
SymbolTable symbols;
|
||||
PosTable positions;
|
||||
const StaticSymbols s;
|
||||
EvalMemory mem;
|
||||
EvalRuntimeCaches caches;
|
||||
EvalPaths paths;
|
||||
EvalBuiltins builtins;
|
||||
EvalStatistics stats;
|
||||
|
||||
/**
|
||||
* If set, force copying files to the Nix store even if they
|
||||
* already exist there.
|
||||
*/
|
||||
RepairFlag repair;
|
||||
|
||||
/**
|
||||
* Store used to materialise .drv files.
|
||||
*/
|
||||
const ref<Store> store;
|
||||
|
||||
std::unique_ptr<DebugState> debug;
|
||||
EvalErrorContext errors;
|
||||
|
||||
Evaluator(
|
||||
const SearchPath & _searchPath,
|
||||
ref<Store> store,
|
||||
std::shared_ptr<Store> buildStore = nullptr,
|
||||
std::function<ReplExitStatus(EvalState & es, ValMap const & extraEnv)> debugRepl = nullptr
|
||||
);
|
||||
|
||||
Evaluator(const Evaluator &) = delete;
|
||||
Evaluator(Evaluator &&) = delete;
|
||||
Evaluator & operator=(const Evaluator &) = delete;
|
||||
Evaluator & operator=(Evaluator &&) = delete;
|
||||
|
||||
/**
|
||||
* Parse a Nix expression from the specified file.
|
||||
*/
|
||||
Expr & parseExprFromFile(const SourcePath & path);
|
||||
Expr & parseExprFromFile(const SourcePath & path, std::shared_ptr<StaticEnv> & staticEnv);
|
||||
Expr & parseExprFromFile(const CheckedSourcePath & path);
|
||||
Expr & parseExprFromFile(const CheckedSourcePath & path, std::shared_ptr<StaticEnv> & staticEnv);
|
||||
|
||||
/**
|
||||
* Parse a Nix expression from the specified string.
|
||||
@@ -467,6 +569,84 @@ public:
|
||||
|
||||
Expr & parseStdin();
|
||||
|
||||
/**
|
||||
* Creates a thunk that will evaluate the given expression when forced.
|
||||
*/
|
||||
void evalLazily(Expr & e, Value & v);
|
||||
|
||||
private:
|
||||
Expr * parse(
|
||||
char * text,
|
||||
size_t length,
|
||||
Pos::Origin origin,
|
||||
const SourcePath & basePath,
|
||||
std::shared_ptr<StaticEnv> & staticEnv,
|
||||
const FeatureSettings & xpSettings = featureSettings);
|
||||
|
||||
public:
|
||||
BindingsBuilder buildBindings(size_t capacity)
|
||||
{
|
||||
return mem.buildBindings(symbols, capacity);
|
||||
}
|
||||
|
||||
/**
|
||||
* Print statistics, if enabled.
|
||||
*
|
||||
* Performs a full memory GC before printing the statistics, so that the
|
||||
* GC statistics are more accurate.
|
||||
*/
|
||||
void maybePrintStats();
|
||||
|
||||
/**
|
||||
* Print statistics, unconditionally, cheaply, without performing a GC first.
|
||||
*/
|
||||
void printStatistics();
|
||||
|
||||
/**
|
||||
* Perform a full memory garbage collection - not incremental.
|
||||
*
|
||||
* @return true if Nix was built with GC and a GC was performed, false if not.
|
||||
* The return value is currently not thread safe - just the return value.
|
||||
*/
|
||||
bool fullGC();
|
||||
|
||||
/**
|
||||
* Create an `EvalState` in prepation to evaluate some amount of Nix code.
|
||||
*
|
||||
* While preparation of evaluation can be done with Evaluator itself only,
|
||||
* actually evaluating things requires an EvalState. This function creates
|
||||
* an EvalState and returns it. At most one EvalState per Evaluator may be
|
||||
* live at any given point, and references to this EvalState must not live
|
||||
* anywhere except in the returned box, local variables, or arguments. Any
|
||||
* reference held in an object type is illegal, be it in a lambda capture,
|
||||
* a pointer member of an object, a hidden member such as arguments passed
|
||||
* to functions by `std::thread` or `std::async`—all references held where
|
||||
* they could be copied are moved from are disallowed. EvalState is thus a
|
||||
* witness type that a given thread may evaluate nix code and must *never*
|
||||
* be run inside `kj::Promise` context. This is due to a kj limitation, in
|
||||
* which it is not possible to block on a promise while already running in
|
||||
* a promise without doing this blocking on a different event loop/thread.
|
||||
*/
|
||||
box_ptr<EvalState> begin();
|
||||
};
|
||||
|
||||
|
||||
class EvalState
|
||||
{
|
||||
friend class Evaluator;
|
||||
|
||||
explicit EvalState(Evaluator & ctx);
|
||||
|
||||
public:
|
||||
Evaluator & ctx;
|
||||
|
||||
EvalState(const EvalState &) = delete;
|
||||
EvalState(EvalState &&) = delete;
|
||||
EvalState & operator=(const EvalState &) = delete;
|
||||
EvalState & operator=(EvalState &&) = delete;
|
||||
|
||||
~EvalState();
|
||||
|
||||
/**
|
||||
* Evaluate an expression read from the given file to normal form.
|
||||
*/
|
||||
@@ -474,21 +654,6 @@ public:
|
||||
|
||||
void resetFileCache();
|
||||
|
||||
/**
|
||||
* Look up a file in the search path.
|
||||
*/
|
||||
SourcePath findFile(const std::string_view path);
|
||||
SourcePath findFile(const SearchPath & searchPath, const std::string_view path, const PosIdx pos = noPos);
|
||||
|
||||
/**
|
||||
* Try to resolve a search path value (not the optinal key part)
|
||||
*
|
||||
* If the specified search path element is a URI, download it.
|
||||
*
|
||||
* If it is not found, return `std::nullopt`
|
||||
*/
|
||||
std::optional<std::string> resolveSearchPathPath(const SearchPath::Path & path);
|
||||
|
||||
/**
|
||||
* Evaluate an expression to normal form
|
||||
*
|
||||
@@ -560,8 +725,6 @@ public:
|
||||
bool coerceMore = false, bool copyToStore = true,
|
||||
bool canonicalizePath = true);
|
||||
|
||||
StorePath copyPathToStore(NixStringContext & context, const SourcePath & path);
|
||||
|
||||
/**
|
||||
* Path coercion.
|
||||
*
|
||||
@@ -603,17 +766,9 @@ private:
|
||||
inline Value * lookupVar(Env * env, const ExprVar & var, bool noEval);
|
||||
|
||||
friend struct ExprVar;
|
||||
friend struct ExprAttrs;
|
||||
friend struct ExprSet;
|
||||
friend struct ExprLet;
|
||||
|
||||
Expr * parse(
|
||||
char * text,
|
||||
size_t length,
|
||||
Pos::Origin origin,
|
||||
const SourcePath & basePath,
|
||||
std::shared_ptr<StaticEnv> & staticEnv,
|
||||
const FeatureSettings & xpSettings = featureSettings);
|
||||
|
||||
/**
|
||||
* Current Nix call stack depth, used with `max-call-depth` setting to throw stack overflow hopefully before we run out of system stack.
|
||||
*/
|
||||
@@ -644,22 +799,8 @@ public:
|
||||
*/
|
||||
void autoCallFunction(Bindings & args, Value & fun, Value & res);
|
||||
|
||||
BindingsBuilder buildBindings(size_t capacity)
|
||||
{
|
||||
return mem.buildBindings(symbols, capacity);
|
||||
}
|
||||
|
||||
void mkThunk_(Value & v, Expr & expr);
|
||||
void mkPos(Value & v, PosIdx pos);
|
||||
|
||||
/**
|
||||
* Create a string representing a store path.
|
||||
*
|
||||
* The string is the printed store path with a context containing a
|
||||
* single `NixStringContextElem::Opaque` element of that store path.
|
||||
*/
|
||||
void mkStorePathString(const StorePath & storePath, Value & v);
|
||||
|
||||
/**
|
||||
* Create a string representing a `SingleDerivedPath::Built`.
|
||||
*
|
||||
@@ -697,33 +838,6 @@ public:
|
||||
|
||||
void concatLists(Value & v, size_t nrLists, Value * * lists, const PosIdx pos, std::string_view errorCtx);
|
||||
|
||||
/**
|
||||
* Print statistics, if enabled.
|
||||
*
|
||||
* Performs a full memory GC before printing the statistics, so that the
|
||||
* GC statistics are more accurate.
|
||||
*/
|
||||
void maybePrintStats();
|
||||
|
||||
/**
|
||||
* Print statistics, unconditionally, cheaply, without performing a GC first.
|
||||
*/
|
||||
void printStatistics();
|
||||
|
||||
/**
|
||||
* Perform a full memory garbage collection - not incremental.
|
||||
*
|
||||
* @return true if Nix was built with GC and a GC was performed, false if not.
|
||||
* The return value is currently not thread safe - just the return value.
|
||||
*/
|
||||
bool fullGC();
|
||||
|
||||
/**
|
||||
* Realise the given context, and return a mapping from the placeholders
|
||||
* used to construct the associated value to their final store path
|
||||
*/
|
||||
[[nodiscard]] StringMap realiseContext(const NixStringContext & context);
|
||||
|
||||
private:
|
||||
|
||||
/**
|
||||
@@ -741,41 +855,6 @@ private:
|
||||
*/
|
||||
std::string mkSingleDerivedPathStringRaw(
|
||||
const SingleDerivedPath & p);
|
||||
|
||||
unsigned long nrLookups = 0;
|
||||
unsigned long nrAvoided = 0;
|
||||
unsigned long nrOpUpdates = 0;
|
||||
unsigned long nrOpUpdateValuesCopied = 0;
|
||||
unsigned long nrListConcats = 0;
|
||||
unsigned long nrPrimOpCalls = 0;
|
||||
unsigned long nrFunctionCalls = 0;
|
||||
|
||||
bool countCalls;
|
||||
|
||||
using PrimOpCalls = std::map<std::string, size_t>;
|
||||
PrimOpCalls primOpCalls;
|
||||
|
||||
using FunctionCalls = std::map<ExprLambda *, size_t>;
|
||||
FunctionCalls functionCalls;
|
||||
|
||||
void incrFunctionCall(ExprLambda * fun);
|
||||
|
||||
using AttrSelects = std::map<PosIdx, size_t>;
|
||||
AttrSelects attrSelects;
|
||||
|
||||
friend struct ExprOpUpdate;
|
||||
friend struct ExprOpConcatLists;
|
||||
friend struct ExprVar;
|
||||
friend struct ExprString;
|
||||
friend struct ExprInt;
|
||||
friend struct ExprFloat;
|
||||
friend struct ExprPath;
|
||||
friend struct ExprSelect;
|
||||
friend void prim_getAttr(EvalState & state, const PosIdx pos, Value * * args, Value & v);
|
||||
friend void prim_match(EvalState & state, const PosIdx pos, Value * * args, Value & v);
|
||||
friend void prim_split(EvalState & state, const PosIdx pos, Value * * args, Value & v);
|
||||
|
||||
friend struct Value;
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -787,11 +866,6 @@ private:
|
||||
std::string_view showType(ValueType type, bool withArticle = true);
|
||||
std::string showType(const Value & v);
|
||||
|
||||
/**
|
||||
* If `path` refers to a directory, then append "/default.nix".
|
||||
*/
|
||||
SourcePath resolveExprPath(SourcePath path);
|
||||
|
||||
static constexpr std::string_view corepkgsPrefix{"/__corepkgs__/"};
|
||||
|
||||
|
||||
|
||||
+65
-65
@@ -37,7 +37,7 @@ static std::optional<FetchedFlake> lookupInFlakeCache(
|
||||
}
|
||||
|
||||
static std::tuple<fetchers::Tree, FlakeRef, FlakeRef> fetchOrSubstituteTree(
|
||||
EvalState & state,
|
||||
Evaluator & state,
|
||||
const FlakeRef & originalRef,
|
||||
bool allowLookup,
|
||||
FlakeCache & flakeCache)
|
||||
@@ -68,7 +68,7 @@ static std::tuple<fetchers::Tree, FlakeRef, FlakeRef> fetchOrSubstituteTree(
|
||||
debug("got tree '%s' from '%s'",
|
||||
state.store->printStorePath(tree.storePath), lockedRef);
|
||||
|
||||
state.allowPath(tree.storePath);
|
||||
state.paths.allowPath(tree.storePath);
|
||||
|
||||
assert(!originalRef.input.getNarHash() || tree.storePath == originalRef.input.computeStorePath(*state.store));
|
||||
|
||||
@@ -88,7 +88,7 @@ static void expectType(EvalState & state, ValueType type,
|
||||
forceTrivialValue(state, value, pos);
|
||||
if (value.type() != type)
|
||||
throw Error("expected %s but got %s at %s",
|
||||
showType(type), showType(value.type()), state.positions[pos]);
|
||||
showType(type), showType(value.type()), state.ctx.positions[pos]);
|
||||
}
|
||||
|
||||
static std::map<FlakeId, FlakeInput> parseFlakeInputs(
|
||||
@@ -103,10 +103,10 @@ static FlakeInput parseFlakeInput(EvalState & state,
|
||||
|
||||
FlakeInput input;
|
||||
|
||||
auto sInputs = state.symbols.create("inputs");
|
||||
auto sUrl = state.symbols.create("url");
|
||||
auto sFlake = state.symbols.create("flake");
|
||||
auto sFollows = state.symbols.create("follows");
|
||||
auto sInputs = state.ctx.symbols.create("inputs");
|
||||
auto sUrl = state.ctx.symbols.create("url");
|
||||
auto sFlake = state.ctx.symbols.create("flake");
|
||||
auto sFollows = state.ctx.symbols.create("follows");
|
||||
|
||||
fetchers::Attrs attrs;
|
||||
std::optional<std::string> url;
|
||||
@@ -133,31 +133,31 @@ static FlakeInput parseFlakeInput(EvalState & state,
|
||||
#pragma GCC diagnostic ignored "-Wswitch-enum"
|
||||
switch (attr.value->type()) {
|
||||
case nString:
|
||||
attrs.emplace(state.symbols[attr.name], attr.value->string.s);
|
||||
attrs.emplace(state.ctx.symbols[attr.name], attr.value->string.s);
|
||||
break;
|
||||
case nBool:
|
||||
attrs.emplace(state.symbols[attr.name], Explicit<bool> { attr.value->boolean });
|
||||
attrs.emplace(state.ctx.symbols[attr.name], Explicit<bool> { attr.value->boolean });
|
||||
break;
|
||||
case nInt: {
|
||||
auto intValue = attr.value->integer.value;
|
||||
|
||||
if (intValue < 0) {
|
||||
state.error<EvalError>("negative value given for flake input attribute %1%: %2%", state.symbols[attr.name], intValue).debugThrow();
|
||||
state.ctx.errors.make<EvalError>("negative value given for flake input attribute %1%: %2%", state.ctx.symbols[attr.name], intValue).debugThrow();
|
||||
}
|
||||
uint64_t asUnsigned = intValue;
|
||||
attrs.emplace(state.symbols[attr.name], asUnsigned);
|
||||
attrs.emplace(state.ctx.symbols[attr.name], asUnsigned);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
state.error<TypeError>("flake input attribute '%s' is %s while a string, Boolean, or integer is expected",
|
||||
state.symbols[attr.name], showType(*attr.value)).debugThrow();
|
||||
state.ctx.errors.make<TypeError>("flake input attribute '%s' is %s while a string, Boolean, or integer is expected",
|
||||
state.ctx.symbols[attr.name], showType(*attr.value)).debugThrow();
|
||||
}
|
||||
#pragma GCC diagnostic pop
|
||||
}
|
||||
} catch (Error & e) {
|
||||
e.addTrace(
|
||||
state.positions[attr.pos],
|
||||
HintFmt("while evaluating flake attribute '%s'", state.symbols[attr.name]));
|
||||
state.ctx.positions[attr.pos],
|
||||
HintFmt("while evaluating flake attribute '%s'", state.ctx.symbols[attr.name]));
|
||||
throw;
|
||||
}
|
||||
}
|
||||
@@ -166,13 +166,13 @@ static FlakeInput parseFlakeInput(EvalState & state,
|
||||
try {
|
||||
input.ref = FlakeRef::fromAttrs(attrs);
|
||||
} catch (Error & e) {
|
||||
e.addTrace(state.positions[pos], HintFmt("while evaluating flake input"));
|
||||
e.addTrace(state.ctx.positions[pos], HintFmt("while evaluating flake input"));
|
||||
throw;
|
||||
}
|
||||
else {
|
||||
attrs.erase("url");
|
||||
if (!attrs.empty())
|
||||
throw Error("unexpected flake input attribute '%s', at %s", attrs.begin()->first, state.positions[pos]);
|
||||
throw Error("unexpected flake input attribute '%s', at %s", attrs.begin()->first, state.ctx.positions[pos]);
|
||||
if (url)
|
||||
input.ref = parseFlakeRef(*url, baseDir, true, input.isFlake);
|
||||
}
|
||||
@@ -196,9 +196,9 @@ static std::map<FlakeId, FlakeInput> parseFlakeInputs(
|
||||
expectType(state, nAttrs, *value, pos);
|
||||
|
||||
for (nix::Attr & inputAttr : *(*value).attrs) {
|
||||
inputs.emplace(state.symbols[inputAttr.name],
|
||||
inputs.emplace(state.ctx.symbols[inputAttr.name],
|
||||
parseFlakeInput(state,
|
||||
state.symbols[inputAttr.name],
|
||||
state.ctx.symbols[inputAttr.name],
|
||||
inputAttr.value,
|
||||
inputAttr.pos,
|
||||
baseDir,
|
||||
@@ -217,7 +217,7 @@ static Flake getFlake(
|
||||
InputPath lockRootPath)
|
||||
{
|
||||
auto [sourceInfo, resolvedRef, lockedRef] = fetchOrSubstituteTree(
|
||||
state, originalRef, allowLookup, flakeCache);
|
||||
state.ctx, originalRef, allowLookup, flakeCache);
|
||||
|
||||
// We need to guard against symlink attacks, but before we start doing
|
||||
// filesystem operations we should make sure there's a flake.nix in the
|
||||
@@ -232,7 +232,7 @@ static Flake getFlake(
|
||||
auto flakeFile = canonPath(flakeDir + "/flake.nix", true);
|
||||
if (!isInDir(flakeFile, sourceInfo.actualPath))
|
||||
throw Error("'flake.nix' file of flake '%s' escapes from '%s'",
|
||||
lockedRef, state.store->printStorePath(sourceInfo.storePath));
|
||||
lockedRef, state.ctx.store->printStorePath(sourceInfo.storePath));
|
||||
|
||||
Flake flake {
|
||||
.originalRef = originalRef,
|
||||
@@ -242,35 +242,35 @@ static Flake getFlake(
|
||||
};
|
||||
|
||||
// FIXME: symlink attack
|
||||
auto resolvedFlakeFile = resolveExprPath(state.checkSourcePath(CanonPath(flakeFile)));
|
||||
Expr & flakeExpr = state.parseExprFromFile(state.checkSourcePath(resolvedFlakeFile));
|
||||
auto resolvedFlakeFile = state.ctx.paths.resolveExprPath(CanonPath(flakeFile));
|
||||
Expr & flakeExpr = state.ctx.parseExprFromFile(resolvedFlakeFile);
|
||||
|
||||
// Enforce that 'flake.nix' is a direct attrset, not a computation.
|
||||
if (!(dynamic_cast<ExprAttrs *>(&flakeExpr))) {
|
||||
state.error<EvalError>("file '%s' must be an attribute set", resolvedFlakeFile).debugThrow();
|
||||
state.ctx.errors.make<EvalError>("file '%s' must be an attribute set", resolvedFlakeFile).debugThrow();
|
||||
}
|
||||
|
||||
Value vInfo;
|
||||
state.eval(flakeExpr, vInfo);
|
||||
|
||||
if (auto description = vInfo.attrs->get(state.s.description)) {
|
||||
if (auto description = vInfo.attrs->get(state.ctx.s.description)) {
|
||||
expectType(state, nString, *description->value, description->pos);
|
||||
flake.description = description->value->string.s;
|
||||
}
|
||||
|
||||
auto sInputs = state.symbols.create("inputs");
|
||||
auto sInputs = state.ctx.symbols.create("inputs");
|
||||
|
||||
if (auto inputs = vInfo.attrs->get(sInputs))
|
||||
flake.inputs = parseFlakeInputs(state, inputs->value, inputs->pos, flakeDir, lockRootPath, 0);
|
||||
|
||||
if (auto outputs = vInfo.attrs->get(state.s.outputs)) {
|
||||
if (auto outputs = vInfo.attrs->get(state.ctx.s.outputs)) {
|
||||
expectType(state, nFunction, *outputs->value, outputs->pos);
|
||||
|
||||
if (outputs->value->isLambda() && outputs->value->lambda.fun->hasFormals()) {
|
||||
for (auto & formal : outputs->value->lambda.fun->formals->formals) {
|
||||
if (formal.name != state.s.self)
|
||||
flake.inputs.emplace(state.symbols[formal.name], FlakeInput {
|
||||
.ref = parseFlakeRef(state.symbols[formal.name])
|
||||
if (formal.name != state.ctx.s.self)
|
||||
flake.inputs.emplace(state.ctx.symbols[formal.name], FlakeInput {
|
||||
.ref = parseFlakeRef(state.ctx.symbols[formal.name])
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -278,7 +278,7 @@ static Flake getFlake(
|
||||
} else
|
||||
throw Error("flake '%s' lacks attribute 'outputs'", lockedRef);
|
||||
|
||||
auto sNixConfig = state.symbols.create("nixConfig");
|
||||
auto sNixConfig = state.ctx.symbols.create("nixConfig");
|
||||
|
||||
if (auto nixConfig = vInfo.attrs->get(sNixConfig)) {
|
||||
expectType(state, nAttrs, *nixConfig->value, nixConfig->pos);
|
||||
@@ -287,45 +287,45 @@ static Flake getFlake(
|
||||
forceTrivialValue(state, *setting.value, setting.pos);
|
||||
if (setting.value->type() == nString)
|
||||
flake.config.settings.emplace(
|
||||
state.symbols[setting.name],
|
||||
state.ctx.symbols[setting.name],
|
||||
std::string(state.forceStringNoCtx(*setting.value, setting.pos, "")));
|
||||
else if (setting.value->type() == nPath) {
|
||||
NixStringContext emptyContext = {};
|
||||
flake.config.settings.emplace(
|
||||
state.symbols[setting.name],
|
||||
state.ctx.symbols[setting.name],
|
||||
state.coerceToString(setting.pos, *setting.value, emptyContext, "", false, true, true) .toOwned());
|
||||
}
|
||||
else if (setting.value->type() == nInt)
|
||||
flake.config.settings.emplace(
|
||||
state.symbols[setting.name],
|
||||
state.ctx.symbols[setting.name],
|
||||
state.forceInt(*setting.value, setting.pos, "").value);
|
||||
else if (setting.value->type() == nBool)
|
||||
flake.config.settings.emplace(
|
||||
state.symbols[setting.name],
|
||||
state.ctx.symbols[setting.name],
|
||||
Explicit<bool> { state.forceBool(*setting.value, setting.pos, "") });
|
||||
else if (setting.value->type() == nList) {
|
||||
std::vector<std::string> ss;
|
||||
for (auto elem : setting.value->listItems()) {
|
||||
if (elem->type() != nString)
|
||||
state.error<TypeError>("list element in flake configuration setting '%s' is %s while a string is expected",
|
||||
state.symbols[setting.name], showType(*setting.value)).debugThrow();
|
||||
state.ctx.errors.make<TypeError>("list element in flake configuration setting '%s' is %s while a string is expected",
|
||||
state.ctx.symbols[setting.name], showType(*setting.value)).debugThrow();
|
||||
ss.emplace_back(state.forceStringNoCtx(*elem, setting.pos, ""));
|
||||
}
|
||||
flake.config.settings.emplace(state.symbols[setting.name], ss);
|
||||
flake.config.settings.emplace(state.ctx.symbols[setting.name], ss);
|
||||
}
|
||||
else
|
||||
state.error<TypeError>("flake configuration setting '%s' is %s",
|
||||
state.symbols[setting.name], showType(*setting.value)).debugThrow();
|
||||
state.ctx.errors.make<TypeError>("flake configuration setting '%s' is %s",
|
||||
state.ctx.symbols[setting.name], showType(*setting.value)).debugThrow();
|
||||
}
|
||||
}
|
||||
|
||||
for (auto & attr : *vInfo.attrs) {
|
||||
if (attr.name != state.s.description &&
|
||||
if (attr.name != state.ctx.s.description &&
|
||||
attr.name != sInputs &&
|
||||
attr.name != state.s.outputs &&
|
||||
attr.name != state.ctx.s.outputs &&
|
||||
attr.name != sNixConfig)
|
||||
throw Error("flake '%s' has an unsupported attribute '%s', at %s",
|
||||
lockedRef, state.symbols[attr.name], state.positions[attr.pos]);
|
||||
lockedRef, state.ctx.symbols[attr.name], state.ctx.positions[attr.pos]);
|
||||
}
|
||||
|
||||
return flake;
|
||||
@@ -385,7 +385,7 @@ LockedFlake lockFlake(
|
||||
|
||||
if (lockFlags.applyNixConfig) {
|
||||
flake.config.apply();
|
||||
state.store->setOptions();
|
||||
state.ctx.store->setOptions();
|
||||
}
|
||||
|
||||
try {
|
||||
@@ -634,7 +634,7 @@ LockedFlake lockFlake(
|
||||
|
||||
else {
|
||||
auto [sourceInfo, resolvedRef, lockedRef] = fetchOrSubstituteTree(
|
||||
state, *input.ref, useRegistries, flakeCache);
|
||||
state.ctx, *input.ref, useRegistries, flakeCache);
|
||||
|
||||
auto childNode = make_ref<LockedNode>(lockedRef, ref, false);
|
||||
|
||||
@@ -773,16 +773,16 @@ void callFlake(EvalState & state,
|
||||
const LockedFlake & lockedFlake,
|
||||
Value & vRes)
|
||||
{
|
||||
auto vLocks = state.mem.allocValue();
|
||||
auto vRootSrc = state.mem.allocValue();
|
||||
auto vRootSubdir = state.mem.allocValue();
|
||||
auto vTmp1 = state.mem.allocValue();
|
||||
auto vTmp2 = state.mem.allocValue();
|
||||
auto vLocks = state.ctx.mem.allocValue();
|
||||
auto vRootSrc = state.ctx.mem.allocValue();
|
||||
auto vRootSubdir = state.ctx.mem.allocValue();
|
||||
auto vTmp1 = state.ctx.mem.allocValue();
|
||||
auto vTmp2 = state.ctx.mem.allocValue();
|
||||
|
||||
vLocks->mkString(lockedFlake.lockFile.to_string());
|
||||
|
||||
emitTreeAttrs(
|
||||
state,
|
||||
state.ctx,
|
||||
*lockedFlake.flake.sourceInfo,
|
||||
lockedFlake.flake.lockedRef.input,
|
||||
*vRootSrc,
|
||||
@@ -791,14 +791,14 @@ void callFlake(EvalState & state,
|
||||
|
||||
vRootSubdir->mkString(lockedFlake.flake.lockedRef.subdir);
|
||||
|
||||
if (!state.vCallFlake) {
|
||||
state.vCallFlake = allocRootValue(state.mem.allocValue());
|
||||
state.eval(state.parseExprFromString(
|
||||
if (!state.ctx.caches.vCallFlake) {
|
||||
state.ctx.caches.vCallFlake = allocRootValue(state.ctx.mem.allocValue());
|
||||
state.eval(state.ctx.parseExprFromString(
|
||||
#include "call-flake.nix.gen.hh"
|
||||
, CanonPath::root), **state.vCallFlake);
|
||||
, CanonPath::root), **state.ctx.caches.vCallFlake);
|
||||
}
|
||||
|
||||
state.callFunction(**state.vCallFlake, *vLocks, *vTmp1, noPos);
|
||||
state.callFunction(**state.ctx.caches.vCallFlake, *vLocks, *vTmp1, noPos);
|
||||
state.callFunction(*vTmp1, *vRootSrc, *vTmp2, noPos);
|
||||
state.callFunction(*vTmp2, *vRootSubdir, vRes, noPos);
|
||||
}
|
||||
@@ -808,7 +808,7 @@ void prim_getFlake(EvalState & state, const PosIdx pos, Value * * args, Value &
|
||||
std::string flakeRefS(state.forceStringNoCtx(*args[0], pos, "while evaluating the argument passed to builtins.getFlake"));
|
||||
auto flakeRef = parseFlakeRef(flakeRefS, {}, true);
|
||||
if (evalSettings.pureEval && !flakeRef.input.isLocked())
|
||||
throw Error("cannot call 'getFlake' on unlocked flake reference '%s', at %s (use --impure to override)", flakeRefS, state.positions[pos]);
|
||||
throw Error("cannot call 'getFlake' on unlocked flake reference '%s', at %s (use --impure to override)", flakeRefS, state.ctx.positions[pos]);
|
||||
|
||||
callFlake(state,
|
||||
lockFlake(state, flakeRef,
|
||||
@@ -830,9 +830,9 @@ void prim_parseFlakeRef(
|
||||
std::string flakeRefS(state.forceStringNoCtx(*args[0], pos,
|
||||
"while evaluating the argument passed to builtins.parseFlakeRef"));
|
||||
auto attrs = parseFlakeRef(flakeRefS, {}, true).toAttrs();
|
||||
auto binds = state.buildBindings(attrs.size());
|
||||
auto binds = state.ctx.buildBindings(attrs.size());
|
||||
for (const auto & [key, value] : attrs) {
|
||||
auto s = state.symbols.create(key);
|
||||
auto s = state.ctx.symbols.create(key);
|
||||
auto & vv = binds.alloc(s);
|
||||
std::visit(overloaded {
|
||||
[&vv](const std::string & value) { vv.mkString(value); },
|
||||
@@ -858,22 +858,22 @@ void prim_flakeRefToString(
|
||||
auto intValue = attr.value->integer.value;
|
||||
|
||||
if (intValue < 0) {
|
||||
state.error<EvalError>("negative value given for flake ref attr %1%: %2%", state.symbols[attr.name], intValue).atPos(pos).debugThrow();
|
||||
state.ctx.errors.make<EvalError>("negative value given for flake ref attr %1%: %2%", state.ctx.symbols[attr.name], intValue).atPos(pos).debugThrow();
|
||||
}
|
||||
uint64_t asUnsigned = intValue;
|
||||
|
||||
attrs.emplace(state.symbols[attr.name], asUnsigned);
|
||||
attrs.emplace(state.ctx.symbols[attr.name], asUnsigned);
|
||||
} else if (t == nBool) {
|
||||
attrs.emplace(state.symbols[attr.name],
|
||||
attrs.emplace(state.ctx.symbols[attr.name],
|
||||
Explicit<bool> { attr.value->boolean });
|
||||
} else if (t == nString) {
|
||||
attrs.emplace(state.symbols[attr.name],
|
||||
attrs.emplace(state.ctx.symbols[attr.name],
|
||||
std::string(attr.value->str()));
|
||||
} else {
|
||||
state.error<EvalError>(
|
||||
state.ctx.errors.make<EvalError>(
|
||||
"flake reference attribute sets may only contain integers, Booleans, "
|
||||
"and strings, but attribute '%s' is %s",
|
||||
state.symbols[attr.name],
|
||||
state.ctx.symbols[attr.name],
|
||||
showType(*attr.value)).debugThrow();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#pragma once
|
||||
///@file
|
||||
|
||||
#include "lix/libexpr/eval.hh"
|
||||
#include "lix/libutil/types.hh"
|
||||
#include "lix/libexpr/flake/flakeref.hh"
|
||||
#include "lix/libexpr/flake/lockfile.hh"
|
||||
@@ -192,7 +193,7 @@ void callFlake(
|
||||
}
|
||||
|
||||
void emitTreeAttrs(
|
||||
EvalState & state,
|
||||
Evaluator & state,
|
||||
const fetchers::Tree & tree,
|
||||
const fetchers::Input & input,
|
||||
Value & v,
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
#include "lix/libexpr/flake/lockfile.hh"
|
||||
#include "json-to-value.hh"
|
||||
#include "lix/libstore/store-api.hh"
|
||||
#include "lix/libutil/error.hh"
|
||||
#include "lix/libutil/url-parts.hh"
|
||||
|
||||
#include <iomanip>
|
||||
@@ -186,8 +188,16 @@ std::string LockFile::to_string() const
|
||||
|
||||
LockFile LockFile::read(const Path & path)
|
||||
{
|
||||
if (!pathExists(path)) return LockFile();
|
||||
return LockFile(nlohmann::json::parse(readFile(path)), path);
|
||||
if (!pathExists(path)) {
|
||||
return LockFile();
|
||||
}
|
||||
try {
|
||||
return LockFile(nlohmann::json::parse(readFile(path)), path);
|
||||
} catch (nlohmann::json::parse_error &nlohmann_json_parse_exc) {
|
||||
auto json_parse_error = JSONParseError(nlohmann_json_parse_exc.what());
|
||||
json_parse_error.addTrace(nullptr, "while parsing the lock file at %s", path);
|
||||
throw json_parse_error;
|
||||
}
|
||||
}
|
||||
|
||||
std::ostream & operator <<(std::ostream & stream, const LockFile & lockFile)
|
||||
|
||||
+72
-87
@@ -12,14 +12,14 @@
|
||||
namespace nix {
|
||||
|
||||
|
||||
DrvInfo::DrvInfo(EvalState & state, std::string attrPath, Bindings * attrs)
|
||||
: state(&state), attrs(attrs), attrPath(std::move(attrPath))
|
||||
DrvInfo::DrvInfo(std::string attrPath, Bindings * attrs)
|
||||
: attrs(attrs), attrPath(std::move(attrPath))
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
DrvInfo::DrvInfo(EvalState & state, ref<Store> store, const std::string & drvPathWithOutputs)
|
||||
: state(&state), attrs(nullptr), attrPath("")
|
||||
DrvInfo::DrvInfo(ref<Store> store, const std::string & drvPathWithOutputs)
|
||||
: attrs(nullptr), attrPath("")
|
||||
{
|
||||
auto [drvPath, selectedOutputs] = parsePathWithOutputs(*store, drvPathWithOutputs);
|
||||
|
||||
@@ -46,68 +46,68 @@ DrvInfo::DrvInfo(EvalState & state, ref<Store> store, const std::string & drvPat
|
||||
}
|
||||
|
||||
|
||||
std::string DrvInfo::queryName()
|
||||
std::string DrvInfo::queryName(EvalState & state)
|
||||
{
|
||||
if (name == "" && attrs) {
|
||||
auto i = attrs->find(state->s.name);
|
||||
if (i == attrs->end()) state->error<TypeError>("derivation name missing").debugThrow();
|
||||
name = state->forceStringNoCtx(*i->value, noPos, "while evaluating the 'name' attribute of a derivation");
|
||||
auto i = attrs->find(state.ctx.s.name);
|
||||
if (i == attrs->end()) state.ctx.errors.make<TypeError>("derivation name missing").debugThrow();
|
||||
name = state.forceStringNoCtx(*i->value, noPos, "while evaluating the 'name' attribute of a derivation");
|
||||
}
|
||||
return name;
|
||||
}
|
||||
|
||||
|
||||
std::string DrvInfo::querySystem()
|
||||
std::string DrvInfo::querySystem(EvalState & state)
|
||||
{
|
||||
if (system == "" && attrs) {
|
||||
auto i = attrs->find(state->s.system);
|
||||
system = i == attrs->end() ? "unknown" : state->forceStringNoCtx(*i->value, i->pos, "while evaluating the 'system' attribute of a derivation");
|
||||
auto i = attrs->find(state.ctx.s.system);
|
||||
system = i == attrs->end() ? "unknown" : state.forceStringNoCtx(*i->value, i->pos, "while evaluating the 'system' attribute of a derivation");
|
||||
}
|
||||
return system;
|
||||
}
|
||||
|
||||
|
||||
std::optional<StorePath> DrvInfo::queryDrvPath()
|
||||
std::optional<StorePath> DrvInfo::queryDrvPath(EvalState & state)
|
||||
{
|
||||
if (!drvPath && attrs) {
|
||||
Bindings::iterator i = attrs->find(state->s.drvPath);
|
||||
Bindings::iterator i = attrs->find(state.ctx.s.drvPath);
|
||||
NixStringContext context;
|
||||
if (i == attrs->end())
|
||||
drvPath = {std::nullopt};
|
||||
else
|
||||
drvPath = {state->coerceToStorePath(i->pos, *i->value, context, "while evaluating the 'drvPath' attribute of a derivation")};
|
||||
drvPath = {state.coerceToStorePath(i->pos, *i->value, context, "while evaluating the 'drvPath' attribute of a derivation")};
|
||||
}
|
||||
return drvPath.value_or(std::nullopt);
|
||||
}
|
||||
|
||||
|
||||
StorePath DrvInfo::requireDrvPath()
|
||||
StorePath DrvInfo::requireDrvPath(EvalState & state)
|
||||
{
|
||||
if (auto drvPath = queryDrvPath())
|
||||
if (auto drvPath = queryDrvPath(state))
|
||||
return *drvPath;
|
||||
throw Error("derivation does not contain a 'drvPath' attribute");
|
||||
}
|
||||
|
||||
|
||||
StorePath DrvInfo::queryOutPath()
|
||||
StorePath DrvInfo::queryOutPath(EvalState & state)
|
||||
{
|
||||
if (!outPath && attrs) {
|
||||
Bindings::iterator i = attrs->find(state->s.outPath);
|
||||
Bindings::iterator i = attrs->find(state.ctx.s.outPath);
|
||||
NixStringContext context;
|
||||
if (i != attrs->end())
|
||||
outPath = state->coerceToStorePath(i->pos, *i->value, context, "while evaluating the output path of a derivation");
|
||||
outPath = state.coerceToStorePath(i->pos, *i->value, context, "while evaluating the output path of a derivation");
|
||||
}
|
||||
if (!outPath)
|
||||
throw UnimplementedError("CA derivations are not yet supported");
|
||||
return *outPath;
|
||||
}
|
||||
|
||||
void DrvInfo::fillOutputs(bool withPaths)
|
||||
void DrvInfo::fillOutputs(EvalState & state, bool withPaths)
|
||||
{
|
||||
auto fillDefault = [&]() {
|
||||
std::optional<StorePath> outPath = std::nullopt;
|
||||
if (withPaths) {
|
||||
outPath.emplace(this->queryOutPath());
|
||||
outPath.emplace(this->queryOutPath(state));
|
||||
}
|
||||
this->outputs.emplace("out", outPath);
|
||||
};
|
||||
@@ -118,14 +118,14 @@ void DrvInfo::fillOutputs(bool withPaths)
|
||||
return;
|
||||
}
|
||||
|
||||
Attr * outputs = this->attrs->get(this->state->s.outputs);
|
||||
Attr * outputs = this->attrs->get(state.ctx.s.outputs);
|
||||
if (outputs == nullptr) {
|
||||
fillDefault();
|
||||
return;
|
||||
}
|
||||
|
||||
// NOTE(Qyriad): I don't think there is any codepath that can cause this to error.
|
||||
this->state->forceList(
|
||||
state.forceList(
|
||||
*outputs->value,
|
||||
outputs->pos,
|
||||
"while evaluating the 'outputs' attribute of a derivation"
|
||||
@@ -138,7 +138,7 @@ void DrvInfo::fillOutputs(bool withPaths)
|
||||
// it is only possible by overriding a derivation attrset already created by
|
||||
// one of those with `//` to introduce the failing `outputs` entry.
|
||||
auto errMsg = fmt("while evaluating output %d of a derivation", idx);
|
||||
std::string_view outputName = state->forceStringNoCtx(
|
||||
std::string_view outputName = state.forceStringNoCtx(
|
||||
*elem,
|
||||
outputs->pos,
|
||||
errMsg
|
||||
@@ -146,7 +146,7 @@ void DrvInfo::fillOutputs(bool withPaths)
|
||||
|
||||
if (withPaths) {
|
||||
// Find the attr with this output's name...
|
||||
Attr * out = this->attrs->get(this->state->symbols.create(outputName));
|
||||
Attr * out = this->attrs->get(state.ctx.symbols.create(outputName));
|
||||
if (out == nullptr) {
|
||||
// FIXME: throw error?
|
||||
continue;
|
||||
@@ -154,10 +154,10 @@ void DrvInfo::fillOutputs(bool withPaths)
|
||||
|
||||
// Meanwhile we couldn't figure out any circumstances
|
||||
// that cause this to error.
|
||||
state->forceAttrs(*out->value, outputs->pos, errMsg);
|
||||
state.forceAttrs(*out->value, outputs->pos, errMsg);
|
||||
|
||||
// ...and evaluate its `outPath` attribute.
|
||||
Attr * outPath = out->value->attrs->get(this->state->s.outPath);
|
||||
Attr * outPath = out->value->attrs->get(state.ctx.s.outPath);
|
||||
if (outPath == nullptr) {
|
||||
continue;
|
||||
// FIXME: throw error?
|
||||
@@ -166,7 +166,7 @@ void DrvInfo::fillOutputs(bool withPaths)
|
||||
NixStringContext context;
|
||||
// And idk what could possibly cause this one to error
|
||||
// that wouldn't error before here.
|
||||
auto storePath = state->coerceToStorePath(
|
||||
auto storePath = state.coerceToStorePath(
|
||||
outPath->pos,
|
||||
*outPath->value,
|
||||
context,
|
||||
@@ -179,14 +179,14 @@ void DrvInfo::fillOutputs(bool withPaths)
|
||||
}
|
||||
}
|
||||
|
||||
DrvInfo::Outputs DrvInfo::queryOutputs(bool withPaths, bool onlyOutputsToInstall)
|
||||
DrvInfo::Outputs DrvInfo::queryOutputs(EvalState & state, bool withPaths, bool onlyOutputsToInstall)
|
||||
{
|
||||
// If we haven't already cached the outputs set, then do so now.
|
||||
if (outputs.empty()) {
|
||||
// FIXME: this behavior seems kind of busted, since whether or not this
|
||||
// DrvInfo will have paths is forever determined by the *first* call to
|
||||
// this function??
|
||||
fillOutputs(withPaths);
|
||||
fillOutputs(state, withPaths);
|
||||
}
|
||||
|
||||
// Things that operate on derivations like packages, like `nix-env` and `nix build`,
|
||||
@@ -201,23 +201,23 @@ DrvInfo::Outputs DrvInfo::queryOutputs(bool withPaths, bool onlyOutputsToInstall
|
||||
// output by its attribute, e.g. `pkgs.lix.dev`, which (lol?) sets the magic
|
||||
// attribute `outputSpecified = true`, and changes the `outputName` attr to the
|
||||
// explicitly selected-into output.
|
||||
if (Attr * outSpecAttr = attrs->get(state->s.outputSpecified)) {
|
||||
bool outputSpecified = this->state->forceBool(
|
||||
if (Attr * outSpecAttr = attrs->get(state.ctx.s.outputSpecified)) {
|
||||
bool outputSpecified = state.forceBool(
|
||||
*outSpecAttr->value,
|
||||
outSpecAttr->pos,
|
||||
"while evaluating the 'outputSpecified' attribute of a derivation"
|
||||
);
|
||||
if (outputSpecified) {
|
||||
auto maybeOut = outputs.find(queryOutputName());
|
||||
auto maybeOut = outputs.find(queryOutputName(state));
|
||||
if (maybeOut == outputs.end()) {
|
||||
throw Error("derivation does not have output '%s'", queryOutputName());
|
||||
throw Error("derivation does not have output '%s'", queryOutputName(state));
|
||||
}
|
||||
return Outputs{*maybeOut};
|
||||
}
|
||||
}
|
||||
|
||||
/* Check for `meta.outputsToInstall` and return `outputs` reduced to that. */
|
||||
const Value * outTI = queryMeta("outputsToInstall");
|
||||
const Value * outTI = queryMeta(state, "outputsToInstall");
|
||||
if (!outTI) return outputs;
|
||||
auto errMsg = Error("this derivation has bad 'meta.outputsToInstall'");
|
||||
/* ^ this shows during `nix-env -i` right under the bad derivation */
|
||||
@@ -233,51 +233,51 @@ DrvInfo::Outputs DrvInfo::queryOutputs(bool withPaths, bool onlyOutputsToInstall
|
||||
}
|
||||
|
||||
|
||||
std::string DrvInfo::queryOutputName()
|
||||
std::string DrvInfo::queryOutputName(EvalState & state)
|
||||
{
|
||||
if (outputName == "" && attrs) {
|
||||
Bindings::iterator i = attrs->find(state->s.outputName);
|
||||
outputName = i != attrs->end() ? state->forceStringNoCtx(*i->value, noPos, "while evaluating the output name of a derivation") : "";
|
||||
Bindings::iterator i = attrs->find(state.ctx.s.outputName);
|
||||
outputName = i != attrs->end() ? state.forceStringNoCtx(*i->value, noPos, "while evaluating the output name of a derivation") : "";
|
||||
}
|
||||
return outputName;
|
||||
}
|
||||
|
||||
|
||||
Bindings * DrvInfo::getMeta()
|
||||
Bindings * DrvInfo::getMeta(EvalState & state)
|
||||
{
|
||||
if (meta) return meta;
|
||||
if (!attrs) return 0;
|
||||
Bindings::iterator a = attrs->find(state->s.meta);
|
||||
Bindings::iterator a = attrs->find(state.ctx.s.meta);
|
||||
if (a == attrs->end()) return 0;
|
||||
state->forceAttrs(*a->value, a->pos, "while evaluating the 'meta' attribute of a derivation");
|
||||
state.forceAttrs(*a->value, a->pos, "while evaluating the 'meta' attribute of a derivation");
|
||||
meta = a->value->attrs;
|
||||
return meta;
|
||||
}
|
||||
|
||||
|
||||
StringSet DrvInfo::queryMetaNames()
|
||||
StringSet DrvInfo::queryMetaNames(EvalState & state)
|
||||
{
|
||||
StringSet res;
|
||||
if (!getMeta()) return res;
|
||||
if (!getMeta(state)) return res;
|
||||
for (auto & i : *meta)
|
||||
res.emplace(state->symbols[i.name]);
|
||||
res.emplace(state.ctx.symbols[i.name]);
|
||||
return res;
|
||||
}
|
||||
|
||||
|
||||
bool DrvInfo::checkMeta(Value & v)
|
||||
bool DrvInfo::checkMeta(EvalState & state, Value & v)
|
||||
{
|
||||
state->forceValue(v, v.determinePos(noPos));
|
||||
state.forceValue(v, v.determinePos(noPos));
|
||||
if (v.type() == nList) {
|
||||
for (auto elem : v.listItems())
|
||||
if (!checkMeta(*elem)) return false;
|
||||
if (!checkMeta(state, *elem)) return false;
|
||||
return true;
|
||||
}
|
||||
else if (v.type() == nAttrs) {
|
||||
Bindings::iterator i = v.attrs->find(state->s.outPath);
|
||||
Bindings::iterator i = v.attrs->find(state.ctx.s.outPath);
|
||||
if (i != v.attrs->end()) return false;
|
||||
for (auto & i : *v.attrs)
|
||||
if (!checkMeta(*i.value)) return false;
|
||||
if (!checkMeta(state, *i.value)) return false;
|
||||
return true;
|
||||
}
|
||||
else return v.type() == nInt || v.type() == nBool || v.type() == nString ||
|
||||
@@ -285,26 +285,26 @@ bool DrvInfo::checkMeta(Value & v)
|
||||
}
|
||||
|
||||
|
||||
Value * DrvInfo::queryMeta(const std::string & name)
|
||||
Value * DrvInfo::queryMeta(EvalState & state, const std::string & name)
|
||||
{
|
||||
if (!getMeta()) return 0;
|
||||
Bindings::iterator a = meta->find(state->symbols.create(name));
|
||||
if (a == meta->end() || !checkMeta(*a->value)) return 0;
|
||||
if (!getMeta(state)) return 0;
|
||||
Bindings::iterator a = meta->find(state.ctx.symbols.create(name));
|
||||
if (a == meta->end() || !checkMeta(state, *a->value)) return 0;
|
||||
return a->value;
|
||||
}
|
||||
|
||||
|
||||
std::string DrvInfo::queryMetaString(const std::string & name)
|
||||
std::string DrvInfo::queryMetaString(EvalState & state, const std::string & name)
|
||||
{
|
||||
Value * v = queryMeta(name);
|
||||
Value * v = queryMeta(state, name);
|
||||
if (!v || v->type() != nString) return "";
|
||||
return v->string.s;
|
||||
}
|
||||
|
||||
|
||||
NixInt DrvInfo::queryMetaInt(const std::string & name, NixInt def)
|
||||
NixInt DrvInfo::queryMetaInt(EvalState & state, const std::string & name, NixInt def)
|
||||
{
|
||||
Value * v = queryMeta(name);
|
||||
Value * v = queryMeta(state, name);
|
||||
if (!v) return def;
|
||||
if (v->type() == nInt) return v->integer;
|
||||
if (v->type() == nString) {
|
||||
@@ -316,24 +316,9 @@ NixInt DrvInfo::queryMetaInt(const std::string & name, NixInt def)
|
||||
return def;
|
||||
}
|
||||
|
||||
NixFloat DrvInfo::queryMetaFloat(const std::string & name, NixFloat def)
|
||||
bool DrvInfo::queryMetaBool(EvalState & state, const std::string & name, bool def)
|
||||
{
|
||||
Value * v = queryMeta(name);
|
||||
if (!v) return def;
|
||||
if (v->type() == nFloat) return v->fpoint;
|
||||
if (v->type() == nString) {
|
||||
/* Backwards compatibility with before we had support for
|
||||
float meta fields. */
|
||||
if (auto n = string2Float<NixFloat>(v->string.s))
|
||||
return *n;
|
||||
}
|
||||
return def;
|
||||
}
|
||||
|
||||
|
||||
bool DrvInfo::queryMetaBool(const std::string & name, bool def)
|
||||
{
|
||||
Value * v = queryMeta(name);
|
||||
Value * v = queryMeta(state, name);
|
||||
if (!v) return def;
|
||||
if (v->type() == nBool) return v->boolean;
|
||||
if (v->type() == nString) {
|
||||
@@ -346,11 +331,11 @@ bool DrvInfo::queryMetaBool(const std::string & name, bool def)
|
||||
}
|
||||
|
||||
|
||||
void DrvInfo::setMeta(const std::string & name, Value * v)
|
||||
void DrvInfo::setMeta(EvalState & state, const std::string & name, Value * v)
|
||||
{
|
||||
getMeta();
|
||||
auto attrs = state->buildBindings(1 + (meta ? meta->size() : 0));
|
||||
auto sym = state->symbols.create(name);
|
||||
getMeta(state);
|
||||
auto attrs = state.ctx.buildBindings(1 + (meta ? meta->size() : 0));
|
||||
auto sym = state.ctx.symbols.create(name);
|
||||
if (meta)
|
||||
for (auto i : *meta)
|
||||
if (i.name != sym)
|
||||
@@ -374,9 +359,9 @@ static bool getDerivation(EvalState & state, Value & v,
|
||||
state.forceValue(v, v.determinePos(noPos));
|
||||
if (!state.isDerivation(v)) return true;
|
||||
|
||||
DrvInfo drv(state, attrPath, v.attrs);
|
||||
DrvInfo drv(attrPath, v.attrs);
|
||||
|
||||
drv.queryName();
|
||||
drv.queryName(state);
|
||||
|
||||
drvs.push_back(drv);
|
||||
|
||||
@@ -446,7 +431,7 @@ static void getDerivations(EvalState & state, Value & vIn,
|
||||
|
||||
return;
|
||||
} else if (v.type() != nAttrs) {
|
||||
state.error<TypeError>(
|
||||
state.ctx.errors.make<TypeError>(
|
||||
"expression was expected to be a derivation or collection of derivations, but instead was %s",
|
||||
showType(v.type(), true)
|
||||
).debugThrow();
|
||||
@@ -462,20 +447,20 @@ static void getDerivations(EvalState & state, Value & vIn,
|
||||
// FIXME: what the fuck???
|
||||
/* !!! undocumented hackery to support combining channels in
|
||||
nix-env.cc. */
|
||||
bool combineChannels = v.attrs->find(state.symbols.create("_combineChannels")) != v.attrs->end();
|
||||
bool combineChannels = v.attrs->find(state.ctx.symbols.create("_combineChannels")) != v.attrs->end();
|
||||
|
||||
/* Consider the attributes in sorted order to get more
|
||||
deterministic behaviour in nix-env operations (e.g. when
|
||||
there are names clashes between derivations, the derivation
|
||||
bound to the attribute with the "lower" name should take
|
||||
precedence). */
|
||||
for (auto & attr : v.attrs->lexicographicOrder(state.symbols)) {
|
||||
debug("evaluating attribute '%1%'", state.symbols[attr->name]);
|
||||
for (auto & attr : v.attrs->lexicographicOrder(state.ctx.symbols)) {
|
||||
debug("evaluating attribute '%1%'", state.ctx.symbols[attr->name]);
|
||||
// FIXME: only consider attrs with identifier-like names?? Why???
|
||||
if (!std::regex_match(std::string(state.symbols[attr->name]), attrRegex)) {
|
||||
if (!std::regex_match(std::string(state.ctx.symbols[attr->name]), attrRegex)) {
|
||||
continue;
|
||||
}
|
||||
std::string joinedAttrPath = addToPath(pathPrefix, state.symbols[attr->name]);
|
||||
std::string joinedAttrPath = addToPath(pathPrefix, state.ctx.symbols[attr->name]);
|
||||
if (combineChannels) {
|
||||
getDerivations(state, *attr->value, joinedAttrPath, autoArgs, drvs, done, ignoreAssertionFailures);
|
||||
} else if (getDerivation(state, *attr->value, joinedAttrPath, drvs, ignoreAssertionFailures)) {
|
||||
@@ -483,7 +468,7 @@ static void getDerivations(EvalState & state, Value & vIn,
|
||||
should we recurse into it? => Only if it has a
|
||||
`recurseForDerivations = true' attribute. */
|
||||
if (attr->value->type() == nAttrs) {
|
||||
Attr * recurseForDrvs = attr->value->attrs->get(state.s.recurseForDerivations);
|
||||
Attr * recurseForDrvs = attr->value->attrs->get(state.ctx.s.recurseForDerivations);
|
||||
if (recurseForDrvs == nullptr) {
|
||||
continue;
|
||||
}
|
||||
|
||||
+18
-21
@@ -17,8 +17,6 @@ public:
|
||||
typedef std::map<std::string, std::optional<StorePath>> Outputs;
|
||||
|
||||
private:
|
||||
EvalState * state;
|
||||
|
||||
std::string name;
|
||||
std::string system;
|
||||
std::optional<std::optional<StorePath>> drvPath;
|
||||
@@ -33,11 +31,11 @@ private:
|
||||
|
||||
Bindings * attrs = nullptr, * meta = nullptr;
|
||||
|
||||
Bindings * getMeta();
|
||||
Bindings * getMeta(EvalState & state);
|
||||
|
||||
bool checkMeta(Value & v);
|
||||
bool checkMeta(EvalState & state, Value & v);
|
||||
|
||||
void fillOutputs(bool withPaths = true);
|
||||
void fillOutputs(EvalState & state, bool withPaths = true);
|
||||
|
||||
public:
|
||||
/**
|
||||
@@ -45,28 +43,27 @@ public:
|
||||
*/
|
||||
std::string attrPath;
|
||||
|
||||
DrvInfo(EvalState & state, std::string attrPath, Bindings * attrs);
|
||||
DrvInfo(EvalState & state, ref<Store> store, const std::string & drvPathWithOutputs);
|
||||
DrvInfo(std::string attrPath, Bindings * attrs);
|
||||
DrvInfo(ref<Store> store, const std::string & drvPathWithOutputs);
|
||||
|
||||
std::string queryName();
|
||||
std::string querySystem();
|
||||
std::optional<StorePath> queryDrvPath();
|
||||
StorePath requireDrvPath();
|
||||
StorePath queryOutPath();
|
||||
std::string queryOutputName();
|
||||
std::string queryName(EvalState & state);
|
||||
std::string querySystem(EvalState & state);
|
||||
std::optional<StorePath> queryDrvPath(EvalState & state);
|
||||
StorePath requireDrvPath(EvalState & state);
|
||||
StorePath queryOutPath(EvalState & state);
|
||||
std::string queryOutputName(EvalState & state);
|
||||
/**
|
||||
* Return the unordered map of output names to (optional) output paths.
|
||||
* The "outputs to install" are determined by `meta.outputsToInstall`.
|
||||
*/
|
||||
Outputs queryOutputs(bool withPaths = true, bool onlyOutputsToInstall = false);
|
||||
Outputs queryOutputs(EvalState & state, bool withPaths = true, bool onlyOutputsToInstall = false);
|
||||
|
||||
StringSet queryMetaNames();
|
||||
Value * queryMeta(const std::string & name);
|
||||
std::string queryMetaString(const std::string & name);
|
||||
NixInt queryMetaInt(const std::string & name, NixInt def);
|
||||
NixFloat queryMetaFloat(const std::string & name, NixFloat def);
|
||||
bool queryMetaBool(const std::string & name, bool def);
|
||||
void setMeta(const std::string & name, Value * v);
|
||||
StringSet queryMetaNames(EvalState & state);
|
||||
Value * queryMeta(EvalState & state, const std::string & name);
|
||||
std::string queryMetaString(EvalState & state, const std::string & name);
|
||||
NixInt queryMetaInt(EvalState & state, const std::string & name, NixInt def);
|
||||
bool queryMetaBool(EvalState & state, const std::string & name, bool def);
|
||||
void setMeta(EvalState & state, const std::string & name, Value * v);
|
||||
|
||||
/*
|
||||
MetaInfo queryMetaInfo(EvalState & state) const;
|
||||
|
||||
@@ -28,7 +28,7 @@ class JSONSax : nlohmann::json_sax<json> {
|
||||
Value & value(EvalState & state)
|
||||
{
|
||||
if (!v)
|
||||
v = allocRootValue(state.mem.allocValue());
|
||||
v = allocRootValue(state.ctx.mem.allocValue());
|
||||
return **v;
|
||||
}
|
||||
virtual ~JSONState() {}
|
||||
@@ -40,7 +40,7 @@ class JSONSax : nlohmann::json_sax<json> {
|
||||
ValueMap attrs;
|
||||
std::unique_ptr<JSONState> resolve(EvalState & state) override
|
||||
{
|
||||
auto attrs2 = state.buildBindings(attrs.size());
|
||||
auto attrs2 = state.ctx.buildBindings(attrs.size());
|
||||
for (auto & i : attrs)
|
||||
attrs2.insert(i.first, i.second);
|
||||
parent->value(state).mkAttrs(attrs2.alreadySorted());
|
||||
@@ -50,7 +50,7 @@ class JSONSax : nlohmann::json_sax<json> {
|
||||
public:
|
||||
void key(string_t & name, EvalState & state)
|
||||
{
|
||||
attrs.insert_or_assign(state.symbols.create(name), &value(state));
|
||||
attrs.insert_or_assign(state.ctx.symbols.create(name), &value(state));
|
||||
}
|
||||
};
|
||||
|
||||
@@ -59,7 +59,7 @@ class JSONSax : nlohmann::json_sax<json> {
|
||||
std::unique_ptr<JSONState> resolve(EvalState & state) override
|
||||
{
|
||||
Value & v = parent->value(state);
|
||||
v = state.mem.newList(values.size());
|
||||
v = state.ctx.mem.newList(values.size());
|
||||
for (size_t n = 0; n < values.size(); ++n) {
|
||||
v.listElems()[n] = values[n];
|
||||
}
|
||||
|
||||
@@ -7,4 +7,3 @@ Description: Lix Package Manager (libexpr)
|
||||
Version: @PACKAGE_VERSION@
|
||||
Requires: lix-base lix-util lix-fetchers lix-store bdw-gc
|
||||
Libs: -L${libdir} -llixexpr
|
||||
Cflags: -I${includedir}/lix/libexpr
|
||||
|
||||
+33
-33
@@ -21,11 +21,11 @@ std::ostream & operator <<(std::ostream & str, const SymbolStr & symbol)
|
||||
return printIdentifier(str, s);
|
||||
}
|
||||
|
||||
AttrName::AttrName(Symbol s) : symbol(s)
|
||||
AttrName::AttrName(PosIdx pos, Symbol s) : pos(pos), symbol(s)
|
||||
{
|
||||
}
|
||||
|
||||
AttrName::AttrName(std::unique_ptr<Expr> e) : expr(std::move(e))
|
||||
AttrName::AttrName(PosIdx pos, std::unique_ptr<Expr> e) : pos(pos), expr(std::move(e))
|
||||
{
|
||||
}
|
||||
|
||||
@@ -139,7 +139,7 @@ void ExprAttrs::showBindings(const SymbolTable & symbols, std::ostream & str) co
|
||||
}
|
||||
}
|
||||
|
||||
void ExprAttrs::show(const SymbolTable & symbols, std::ostream & str) const
|
||||
void ExprSet::show(const SymbolTable & symbols, std::ostream & str) const
|
||||
{
|
||||
if (recursive) str << "rec ";
|
||||
str << "{ ";
|
||||
@@ -202,7 +202,7 @@ void ExprCall::show(const SymbolTable & symbols, std::ostream & str) const
|
||||
void ExprLet::show(const SymbolTable & symbols, std::ostream & str) const
|
||||
{
|
||||
str << "(let ";
|
||||
attrs->showBindings(symbols, str);
|
||||
showBindings(symbols, str);
|
||||
str << "in ";
|
||||
body->show(symbols, str);
|
||||
str << ")";
|
||||
@@ -295,36 +295,36 @@ std::string showAttrPath(const SymbolTable & symbols, const AttrPath & attrPath)
|
||||
|
||||
/* Computing levels/displacements for variables. */
|
||||
|
||||
void Expr::bindVars(EvalState & es, const std::shared_ptr<const StaticEnv> & env)
|
||||
void Expr::bindVars(Evaluator & es, const std::shared_ptr<const StaticEnv> & env)
|
||||
{
|
||||
abort();
|
||||
}
|
||||
|
||||
void ExprInt::bindVars(EvalState & es, const std::shared_ptr<const StaticEnv> & env)
|
||||
void ExprInt::bindVars(Evaluator & es, const std::shared_ptr<const StaticEnv> & env)
|
||||
{
|
||||
if (es.debug)
|
||||
es.debug->exprEnvs.insert(std::make_pair(this, env));
|
||||
}
|
||||
|
||||
void ExprFloat::bindVars(EvalState & es, const std::shared_ptr<const StaticEnv> & env)
|
||||
void ExprFloat::bindVars(Evaluator & es, const std::shared_ptr<const StaticEnv> & env)
|
||||
{
|
||||
if (es.debug)
|
||||
es.debug->exprEnvs.insert(std::make_pair(this, env));
|
||||
}
|
||||
|
||||
void ExprString::bindVars(EvalState & es, const std::shared_ptr<const StaticEnv> & env)
|
||||
void ExprString::bindVars(Evaluator & es, const std::shared_ptr<const StaticEnv> & env)
|
||||
{
|
||||
if (es.debug)
|
||||
es.debug->exprEnvs.insert(std::make_pair(this, env));
|
||||
}
|
||||
|
||||
void ExprPath::bindVars(EvalState & es, const std::shared_ptr<const StaticEnv> & env)
|
||||
void ExprPath::bindVars(Evaluator & es, const std::shared_ptr<const StaticEnv> & env)
|
||||
{
|
||||
if (es.debug)
|
||||
es.debug->exprEnvs.insert(std::make_pair(this, env));
|
||||
}
|
||||
|
||||
void ExprVar::bindVars(EvalState & es, const std::shared_ptr<const StaticEnv> & env)
|
||||
void ExprVar::bindVars(Evaluator & es, const std::shared_ptr<const StaticEnv> & env)
|
||||
{
|
||||
if (es.debug)
|
||||
es.debug->exprEnvs.insert(std::make_pair(this, env));
|
||||
@@ -364,7 +364,7 @@ void ExprVar::bindVars(EvalState & es, const std::shared_ptr<const StaticEnv> &
|
||||
enclosing `with'. If there is no `with', then we can issue an
|
||||
"undefined variable" error now. */
|
||||
if (withLevel == -1)
|
||||
es.error<UndefinedVarError>(
|
||||
es.errors.make<UndefinedVarError>(
|
||||
"undefined variable '%1%'",
|
||||
es.symbols[name]
|
||||
).atPos(pos).debugThrow();
|
||||
@@ -373,13 +373,13 @@ void ExprVar::bindVars(EvalState & es, const std::shared_ptr<const StaticEnv> &
|
||||
this->level = withLevel;
|
||||
}
|
||||
|
||||
void ExprInheritFrom::bindVars(EvalState & es, const std::shared_ptr<const StaticEnv> & env)
|
||||
void ExprInheritFrom::bindVars(Evaluator & es, const std::shared_ptr<const StaticEnv> & env)
|
||||
{
|
||||
if (es.debug)
|
||||
es.debug->exprEnvs.insert(std::make_pair(this, env));
|
||||
}
|
||||
|
||||
void ExprSelect::bindVars(EvalState & es, const std::shared_ptr<const StaticEnv> & env)
|
||||
void ExprSelect::bindVars(Evaluator & es, const std::shared_ptr<const StaticEnv> & env)
|
||||
{
|
||||
if (es.debug)
|
||||
es.debug->exprEnvs.insert(std::make_pair(this, env));
|
||||
@@ -391,7 +391,7 @@ void ExprSelect::bindVars(EvalState & es, const std::shared_ptr<const StaticEnv>
|
||||
i.expr->bindVars(es, env);
|
||||
}
|
||||
|
||||
void ExprOpHasAttr::bindVars(EvalState & es, const std::shared_ptr<const StaticEnv> & env)
|
||||
void ExprOpHasAttr::bindVars(Evaluator & es, const std::shared_ptr<const StaticEnv> & env)
|
||||
{
|
||||
if (es.debug)
|
||||
es.debug->exprEnvs.insert(std::make_pair(this, env));
|
||||
@@ -403,7 +403,7 @@ void ExprOpHasAttr::bindVars(EvalState & es, const std::shared_ptr<const StaticE
|
||||
}
|
||||
|
||||
std::shared_ptr<const StaticEnv> ExprAttrs::bindInheritSources(
|
||||
EvalState & es, const std::shared_ptr<const StaticEnv> & env)
|
||||
Evaluator & es, const std::shared_ptr<const StaticEnv> & env)
|
||||
{
|
||||
if (!inheritFromExprs)
|
||||
return nullptr;
|
||||
@@ -423,7 +423,7 @@ std::shared_ptr<const StaticEnv> ExprAttrs::bindInheritSources(
|
||||
return inner;
|
||||
}
|
||||
|
||||
void ExprAttrs::bindVars(EvalState & es, const std::shared_ptr<const StaticEnv> & env)
|
||||
void ExprSet::bindVars(Evaluator & es, const std::shared_ptr<const StaticEnv> & env)
|
||||
{
|
||||
if (es.debug)
|
||||
es.debug->exprEnvs.insert(std::make_pair(this, env));
|
||||
@@ -462,7 +462,7 @@ void ExprAttrs::bindVars(EvalState & es, const std::shared_ptr<const StaticEnv>
|
||||
}
|
||||
}
|
||||
|
||||
void ExprList::bindVars(EvalState & es, const std::shared_ptr<const StaticEnv> & env)
|
||||
void ExprList::bindVars(Evaluator & es, const std::shared_ptr<const StaticEnv> & env)
|
||||
{
|
||||
if (es.debug)
|
||||
es.debug->exprEnvs.insert(std::make_pair(this, env));
|
||||
@@ -471,7 +471,7 @@ void ExprList::bindVars(EvalState & es, const std::shared_ptr<const StaticEnv> &
|
||||
i->bindVars(es, env);
|
||||
}
|
||||
|
||||
void ExprLambda::bindVars(EvalState & es, const std::shared_ptr<const StaticEnv> & env)
|
||||
void ExprLambda::bindVars(Evaluator & es, const std::shared_ptr<const StaticEnv> & env)
|
||||
{
|
||||
if (es.debug)
|
||||
es.debug->exprEnvs.insert(std::make_pair(this, env));
|
||||
@@ -498,7 +498,7 @@ void ExprLambda::bindVars(EvalState & es, const std::shared_ptr<const StaticEnv>
|
||||
body->bindVars(es, newEnv);
|
||||
}
|
||||
|
||||
void ExprCall::bindVars(EvalState & es, const std::shared_ptr<const StaticEnv> & env)
|
||||
void ExprCall::bindVars(Evaluator & es, const std::shared_ptr<const StaticEnv> & env)
|
||||
{
|
||||
if (es.debug)
|
||||
es.debug->exprEnvs.insert(std::make_pair(this, env));
|
||||
@@ -508,21 +508,21 @@ void ExprCall::bindVars(EvalState & es, const std::shared_ptr<const StaticEnv> &
|
||||
e->bindVars(es, env);
|
||||
}
|
||||
|
||||
void ExprLet::bindVars(EvalState & es, const std::shared_ptr<const StaticEnv> & env)
|
||||
void ExprLet::bindVars(Evaluator & es, const std::shared_ptr<const StaticEnv> & env)
|
||||
{
|
||||
auto newEnv = [&] () -> std::shared_ptr<const StaticEnv> {
|
||||
auto newEnv = std::make_shared<StaticEnv>(nullptr, env.get(), attrs->attrs.size());
|
||||
auto newEnv = std::make_shared<StaticEnv>(nullptr, env.get(), attrs.size());
|
||||
|
||||
Displacement displ = 0;
|
||||
for (auto & i : attrs->attrs)
|
||||
for (auto & i : attrs)
|
||||
newEnv->vars.emplace_back(i.first, i.second.displ = displ++);
|
||||
return newEnv;
|
||||
}();
|
||||
|
||||
// No need to sort newEnv since attrs->attrs is in sorted order.
|
||||
// No need to sort newEnv since attrs is in sorted order.
|
||||
|
||||
auto inheritFromEnv = attrs->bindInheritSources(es, newEnv);
|
||||
for (auto & i : attrs->attrs)
|
||||
auto inheritFromEnv = bindInheritSources(es, newEnv);
|
||||
for (auto & i : attrs)
|
||||
i.second.e->bindVars(es, i.second.chooseByKind(newEnv, env, inheritFromEnv));
|
||||
|
||||
if (es.debug)
|
||||
@@ -531,7 +531,7 @@ void ExprLet::bindVars(EvalState & es, const std::shared_ptr<const StaticEnv> &
|
||||
body->bindVars(es, newEnv);
|
||||
}
|
||||
|
||||
void ExprWith::bindVars(EvalState & es, const std::shared_ptr<const StaticEnv> & env)
|
||||
void ExprWith::bindVars(Evaluator & es, const std::shared_ptr<const StaticEnv> & env)
|
||||
{
|
||||
if (es.debug)
|
||||
es.debug->exprEnvs.insert(std::make_pair(this, env));
|
||||
@@ -557,7 +557,7 @@ void ExprWith::bindVars(EvalState & es, const std::shared_ptr<const StaticEnv> &
|
||||
body->bindVars(es, newEnv);
|
||||
}
|
||||
|
||||
void ExprIf::bindVars(EvalState & es, const std::shared_ptr<const StaticEnv> & env)
|
||||
void ExprIf::bindVars(Evaluator & es, const std::shared_ptr<const StaticEnv> & env)
|
||||
{
|
||||
if (es.debug)
|
||||
es.debug->exprEnvs.insert(std::make_pair(this, env));
|
||||
@@ -567,7 +567,7 @@ void ExprIf::bindVars(EvalState & es, const std::shared_ptr<const StaticEnv> & e
|
||||
else_->bindVars(es, env);
|
||||
}
|
||||
|
||||
void ExprAssert::bindVars(EvalState & es, const std::shared_ptr<const StaticEnv> & env)
|
||||
void ExprAssert::bindVars(Evaluator & es, const std::shared_ptr<const StaticEnv> & env)
|
||||
{
|
||||
if (es.debug)
|
||||
es.debug->exprEnvs.insert(std::make_pair(this, env));
|
||||
@@ -576,7 +576,7 @@ void ExprAssert::bindVars(EvalState & es, const std::shared_ptr<const StaticEnv>
|
||||
body->bindVars(es, env);
|
||||
}
|
||||
|
||||
void ExprOpNot::bindVars(EvalState & es, const std::shared_ptr<const StaticEnv> & env)
|
||||
void ExprOpNot::bindVars(Evaluator & es, const std::shared_ptr<const StaticEnv> & env)
|
||||
{
|
||||
if (es.debug)
|
||||
es.debug->exprEnvs.insert(std::make_pair(this, env));
|
||||
@@ -584,7 +584,7 @@ void ExprOpNot::bindVars(EvalState & es, const std::shared_ptr<const StaticEnv>
|
||||
e->bindVars(es, env);
|
||||
}
|
||||
|
||||
void ExprConcatStrings::bindVars(EvalState & es, const std::shared_ptr<const StaticEnv> & env)
|
||||
void ExprConcatStrings::bindVars(Evaluator & es, const std::shared_ptr<const StaticEnv> & env)
|
||||
{
|
||||
if (es.debug)
|
||||
es.debug->exprEnvs.insert(std::make_pair(this, env));
|
||||
@@ -593,7 +593,7 @@ void ExprConcatStrings::bindVars(EvalState & es, const std::shared_ptr<const Sta
|
||||
i.second->bindVars(es, env);
|
||||
}
|
||||
|
||||
void ExprPos::bindVars(EvalState & es, const std::shared_ptr<const StaticEnv> & env)
|
||||
void ExprPos::bindVars(Evaluator & es, const std::shared_ptr<const StaticEnv> & env)
|
||||
{
|
||||
if (es.debug)
|
||||
es.debug->exprEnvs.insert(std::make_pair(this, env));
|
||||
@@ -617,9 +617,9 @@ void ExprLambda::setName(Symbol name)
|
||||
std::string ExprLambda::showNamePos(const EvalState & state) const
|
||||
{
|
||||
std::string id(name
|
||||
? concatStrings("'", state.symbols[name], "'")
|
||||
? concatStrings("'", state.ctx.symbols[name], "'")
|
||||
: "anonymous function");
|
||||
return fmt("%1% at %2%", id, state.positions[pos]);
|
||||
return fmt("%1% at %2%", id, state.ctx.positions[pos]);
|
||||
}
|
||||
|
||||
|
||||
|
||||
+31
-20
@@ -18,7 +18,7 @@ namespace nix {
|
||||
|
||||
struct Env;
|
||||
struct Value;
|
||||
class EvalState;
|
||||
class Evaluator;
|
||||
struct ExprWith;
|
||||
struct StaticEnv;
|
||||
|
||||
@@ -28,10 +28,11 @@ struct StaticEnv;
|
||||
*/
|
||||
struct AttrName
|
||||
{
|
||||
PosIdx pos;
|
||||
Symbol symbol;
|
||||
std::unique_ptr<Expr> expr;
|
||||
AttrName(Symbol s);
|
||||
AttrName(std::unique_ptr<Expr> e);
|
||||
AttrName(PosIdx pos, Symbol s);
|
||||
AttrName(PosIdx pos, std::unique_ptr<Expr> e);
|
||||
};
|
||||
|
||||
typedef std::vector<AttrName> AttrPath;
|
||||
@@ -59,7 +60,7 @@ public:
|
||||
virtual ~Expr() { };
|
||||
|
||||
virtual void show(const SymbolTable & symbols, std::ostream & str) const;
|
||||
virtual void bindVars(EvalState & es, const std::shared_ptr<const StaticEnv> & env);
|
||||
virtual void bindVars(Evaluator & es, const std::shared_ptr<const StaticEnv> & env);
|
||||
virtual void eval(EvalState & state, Env & env, Value & v);
|
||||
virtual Value * maybeThunk(EvalState & state, Env & env);
|
||||
virtual void setName(Symbol name);
|
||||
@@ -69,7 +70,7 @@ public:
|
||||
#define COMMON_METHODS \
|
||||
void show(const SymbolTable & symbols, std::ostream & str) const override; \
|
||||
void eval(EvalState & state, Env & env, Value & v) override; \
|
||||
void bindVars(EvalState & es, const std::shared_ptr<const StaticEnv> & env) override;
|
||||
void bindVars(Evaluator & es, const std::shared_ptr<const StaticEnv> & env) override;
|
||||
|
||||
struct ExprInt : Expr
|
||||
{
|
||||
@@ -163,7 +164,7 @@ struct ExprInheritFrom : ExprVar
|
||||
}
|
||||
|
||||
void show(SymbolTable const & symbols, std::ostream & str) const override;
|
||||
void bindVars(EvalState & es, const std::shared_ptr<const StaticEnv> & env) override;
|
||||
void bindVars(Evaluator & es, const std::shared_ptr<const StaticEnv> & env) override;
|
||||
};
|
||||
|
||||
struct ExprSelect : Expr
|
||||
@@ -182,7 +183,7 @@ struct ExprSelect : Expr
|
||||
AttrPath attrPath;
|
||||
|
||||
ExprSelect(const PosIdx & pos, std::unique_ptr<Expr> e, AttrPath attrPath, std::unique_ptr<Expr> def) : pos(pos), e(std::move(e)), def(std::move(def)), attrPath(std::move(attrPath)) { };
|
||||
ExprSelect(const PosIdx & pos, std::unique_ptr<Expr> e, Symbol name) : pos(pos), e(std::move(e)) { attrPath.push_back(AttrName(name)); };
|
||||
ExprSelect(const PosIdx & pos, std::unique_ptr<Expr> e, const PosIdx namePos, Symbol name) : pos(pos), e(std::move(e)) { attrPath.push_back(AttrName(namePos, name)); };
|
||||
PosIdx getPos() const override { return pos; }
|
||||
COMMON_METHODS
|
||||
};
|
||||
@@ -196,10 +197,16 @@ struct ExprOpHasAttr : Expr
|
||||
COMMON_METHODS
|
||||
};
|
||||
|
||||
struct ExprAttrs : Expr
|
||||
/* Helper struct to contain the data shared across lets and sets */
|
||||
struct ExprAttrs
|
||||
{
|
||||
bool recursive;
|
||||
PosIdx pos;
|
||||
ExprAttrs() = default;
|
||||
ExprAttrs(const ExprAttrs &) = delete;
|
||||
ExprAttrs & operator=(const ExprAttrs &) = delete;
|
||||
ExprAttrs(ExprAttrs &&) = default;
|
||||
ExprAttrs & operator=(ExprAttrs &&) = default;
|
||||
virtual ~ExprAttrs() = default;
|
||||
|
||||
struct AttrDef {
|
||||
enum class Kind {
|
||||
/** `attr = expr;` */
|
||||
@@ -243,17 +250,23 @@ struct ExprAttrs : Expr
|
||||
};
|
||||
typedef std::vector<DynamicAttrDef> DynamicAttrDefs;
|
||||
DynamicAttrDefs dynamicAttrs;
|
||||
ExprAttrs(const PosIdx &pos) : recursive(false), pos(pos) { };
|
||||
ExprAttrs() : recursive(false) { };
|
||||
PosIdx getPos() const override { return pos; }
|
||||
COMMON_METHODS
|
||||
|
||||
std::shared_ptr<const StaticEnv> bindInheritSources(
|
||||
EvalState & es, const std::shared_ptr<const StaticEnv> & env);
|
||||
Evaluator & es, const std::shared_ptr<const StaticEnv> & env);
|
||||
Env * buildInheritFromEnv(EvalState & state, Env & up);
|
||||
void showBindings(const SymbolTable & symbols, std::ostream & str) const;
|
||||
};
|
||||
|
||||
struct ExprSet : Expr, ExprAttrs {
|
||||
PosIdx pos;
|
||||
bool recursive = false;
|
||||
|
||||
ExprSet(const PosIdx &pos, bool recursive = false) : pos(pos), recursive(recursive) { };
|
||||
ExprSet() { };
|
||||
PosIdx getPos() const override { return pos; }
|
||||
COMMON_METHODS
|
||||
};
|
||||
|
||||
struct ExprList : Expr
|
||||
{
|
||||
std::vector<std::unique_ptr<Expr>> elems;
|
||||
@@ -368,11 +381,9 @@ struct ExprCall : Expr
|
||||
COMMON_METHODS
|
||||
};
|
||||
|
||||
struct ExprLet : Expr
|
||||
struct ExprLet : Expr, ExprAttrs
|
||||
{
|
||||
std::unique_ptr<ExprAttrs> attrs;
|
||||
std::unique_ptr<Expr> body;
|
||||
ExprLet(std::unique_ptr<ExprAttrs> attrs, std::unique_ptr<Expr> body) : attrs(std::move(attrs)), body(std::move(body)) { };
|
||||
COMMON_METHODS
|
||||
};
|
||||
|
||||
@@ -424,7 +435,7 @@ struct ExprOpNot : Expr
|
||||
{ \
|
||||
str << "("; e1->show(symbols, str); str << " " s " "; e2->show(symbols, str); str << ")"; \
|
||||
} \
|
||||
void bindVars(EvalState & es, const std::shared_ptr<const StaticEnv> & env) override \
|
||||
void bindVars(Evaluator & es, const std::shared_ptr<const StaticEnv> & env) override \
|
||||
{ \
|
||||
e1->bindVars(es, env); e2->bindVars(es, env); \
|
||||
} \
|
||||
@@ -464,7 +475,7 @@ struct ExprBlackHole : Expr
|
||||
{
|
||||
void show(const SymbolTable & symbols, std::ostream & str) const override {}
|
||||
void eval(EvalState & state, Env & env, Value & v) override;
|
||||
void bindVars(EvalState & es, const std::shared_ptr<const StaticEnv> & env) override {}
|
||||
void bindVars(Evaluator & es, const std::shared_ptr<const StaticEnv> & env) override {}
|
||||
};
|
||||
|
||||
extern ExprBlackHole eBlackHole;
|
||||
|
||||
@@ -260,10 +260,10 @@ template<> struct BuildAST<grammar::v1::formals> : change_head<FormalsState> {
|
||||
struct AttrState : SubexprState {
|
||||
using SubexprState::SubexprState;
|
||||
|
||||
std::vector<AttrName> attrs;
|
||||
AttrPath attrs;
|
||||
|
||||
template <typename T>
|
||||
void pushAttr(T && attr, PosIdx) { attrs.emplace_back(std::forward<T>(attr)); }
|
||||
void pushAttr(T && attr, PosIdx pos) { attrs.emplace_back(pos, std::forward<T>(attr)); }
|
||||
};
|
||||
|
||||
template<> struct BuildAST<grammar::v1::attr::simple> {
|
||||
@@ -285,22 +285,38 @@ template<> struct BuildAST<grammar::v1::attr::string> {
|
||||
template<> struct BuildAST<grammar::v1::attr::expr> : BuildAST<grammar::v1::attr::string> {};
|
||||
|
||||
struct BindingsState : SubexprState {
|
||||
using SubexprState::SubexprState;
|
||||
explicit BindingsState(ExprState & up, ExprAttrs & attrs) : SubexprState(up), attrs(attrs) {}
|
||||
|
||||
ExprAttrs attrs;
|
||||
ExprAttrs & attrs;
|
||||
PosIdx pos;
|
||||
AttrPath path;
|
||||
std::unique_ptr<Expr> value;
|
||||
};
|
||||
|
||||
struct BindingsStateSet : BindingsState {
|
||||
ExprSet set = {};
|
||||
BindingsStateSet(ExprState & up, State & ps, auto &...) : BindingsState(up, set) { }
|
||||
};
|
||||
|
||||
struct BindingsStateRecSet : BindingsState {
|
||||
ExprSet set = { PosIdx{}, true };
|
||||
BindingsStateRecSet(ExprState & up, State & ps, auto &...) : BindingsState(up, set) { }
|
||||
};
|
||||
|
||||
struct BindingsStateLet : BindingsState {
|
||||
ExprLet let = {};
|
||||
BindingsStateLet(ExprState & up, State & ps, auto &...) : BindingsState(up, let) { }
|
||||
};
|
||||
|
||||
struct InheritState : SubexprState {
|
||||
using SubexprState::SubexprState;
|
||||
|
||||
std::vector<std::pair<AttrName, PosIdx>> attrs;
|
||||
std::vector<AttrName> attrs;
|
||||
std::unique_ptr<Expr> from;
|
||||
PosIdx fromPos;
|
||||
|
||||
template <typename T>
|
||||
void pushAttr(T && attr, PosIdx pos) { attrs.emplace_back(std::forward<T>(attr), pos); }
|
||||
void pushAttr(T && attr, PosIdx pos) { attrs.emplace_back(pos, std::forward<T>(attr)); }
|
||||
};
|
||||
|
||||
template<> struct BuildAST<grammar::v1::inherit::from> {
|
||||
@@ -314,15 +330,15 @@ template<> struct BuildAST<grammar::v1::inherit> : change_head<InheritState> {
|
||||
static void success0(InheritState & s, BindingsState & b, State & ps) {
|
||||
auto & attrs = b.attrs.attrs;
|
||||
// TODO this should not reuse generic attrpath rules.
|
||||
for (auto & [i, iPos] : s.attrs) {
|
||||
for (auto & i : s.attrs) {
|
||||
if (i.symbol)
|
||||
continue;
|
||||
if (auto str = dynamic_cast<ExprString *>(i.expr.get()))
|
||||
i = AttrName(ps.symbols.create(str->s));
|
||||
i = AttrName(i.pos, ps.symbols.create(str->s));
|
||||
else {
|
||||
throw ParseError({
|
||||
.msg = HintFmt("dynamic attributes not allowed in inherit"),
|
||||
.pos = ps.positions[iPos]
|
||||
.pos = ps.positions[i.pos]
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -331,9 +347,9 @@ template<> struct BuildAST<grammar::v1::inherit> : change_head<InheritState> {
|
||||
b.attrs.inheritFromExprs = std::make_unique<std::vector<ref<Expr>>>();
|
||||
auto fromExpr = ref<Expr>(std::move(s.from));
|
||||
b.attrs.inheritFromExprs->push_back(fromExpr);
|
||||
for (auto & [i, iPos] : s.attrs) {
|
||||
for (auto & i : s.attrs) {
|
||||
if (attrs.find(i.symbol) != attrs.end())
|
||||
ps.dupAttr(i.symbol, iPos, attrs[i.symbol].pos);
|
||||
ps.dupAttr(i.symbol, i.pos, attrs[i.symbol].pos);
|
||||
auto inheritFrom = std::make_unique<ExprInheritFrom>(
|
||||
s.fromPos,
|
||||
b.attrs.inheritFromExprs->size() - 1,
|
||||
@@ -342,19 +358,19 @@ template<> struct BuildAST<grammar::v1::inherit> : change_head<InheritState> {
|
||||
attrs.emplace(
|
||||
i.symbol,
|
||||
ExprAttrs::AttrDef(
|
||||
std::make_unique<ExprSelect>(iPos, std::move(inheritFrom), i.symbol),
|
||||
iPos,
|
||||
std::make_unique<ExprSelect>(i.pos, std::move(inheritFrom), i.pos, i.symbol),
|
||||
i.pos,
|
||||
ExprAttrs::AttrDef::Kind::InheritedFrom));
|
||||
}
|
||||
} else {
|
||||
for (auto & [i, iPos] : s.attrs) {
|
||||
for (auto & i : s.attrs) {
|
||||
if (attrs.find(i.symbol) != attrs.end())
|
||||
ps.dupAttr(i.symbol, iPos, attrs[i.symbol].pos);
|
||||
ps.dupAttr(i.symbol, i.pos, attrs[i.symbol].pos);
|
||||
attrs.emplace(
|
||||
i.symbol,
|
||||
ExprAttrs::AttrDef(
|
||||
std::make_unique<ExprVar>(iPos, i.symbol),
|
||||
iPos,
|
||||
std::make_unique<ExprVar>(i.pos, i.symbol),
|
||||
i.pos,
|
||||
ExprAttrs::AttrDef::Kind::Inherited));
|
||||
}
|
||||
}
|
||||
@@ -589,7 +605,7 @@ template<> struct BuildAST<grammar::v1::path::interpolation> : BuildAST<grammar:
|
||||
|
||||
template<> struct BuildAST<grammar::v1::path::anchor> {
|
||||
static void apply(const auto & in, StringState & s, State & ps) {
|
||||
Path path(absPath(in.string(), ps.basePath.path.abs()));
|
||||
Path path(absPath(in.string(), ps.basePath.canonical().abs()));
|
||||
/* add back in the trailing '/' to the first segment */
|
||||
if (in.string_view().ends_with('/') && in.size() > 1)
|
||||
path += "/";
|
||||
@@ -610,13 +626,21 @@ template<> struct BuildAST<grammar::v1::path::searched_path> {
|
||||
static void apply(const auto & in, StringState & s, State & ps) {
|
||||
auto pos = ps.at(in);
|
||||
std::vector<std::unique_ptr<Expr>> args{2};
|
||||
args[0] = ps.mkInternalVar(pos, ps.s.nixPath);
|
||||
/* Overriding __nixPath, while being barely documented, is intended and supported:
|
||||
* https://github.com/NixOS/nix/commit/62a6eeb1f3da0a5954ad2da54c454eb7fc1c6e5d
|
||||
* (TODO: Provide a better and officially supported and documented mechanism for doing this)
|
||||
*/
|
||||
args[0] = std::make_unique<ExprVar>(pos, ps.s.nixPath);
|
||||
args[1] = std::make_unique<ExprString>(in.string());
|
||||
s.parts.emplace_back(
|
||||
pos,
|
||||
std::make_unique<ExprCall>(
|
||||
pos,
|
||||
ps.mkInternalVar(pos, ps.s.findFile),
|
||||
/* The option for overriding this should be deprecated eventually, but for now it has to stay
|
||||
* until we can figure out how to design a better replacement.
|
||||
* https://git.lix.systems/lix-project/lix/issues/599
|
||||
*/
|
||||
std::make_unique<ExprVar>(pos, ps.s.findFile),
|
||||
std::move(args)));
|
||||
}
|
||||
};
|
||||
@@ -663,8 +687,8 @@ template<> struct BuildAST<grammar::v1::expr::uri> {
|
||||
}
|
||||
};
|
||||
|
||||
template<> struct BuildAST<grammar::v1::expr::ancient_let> : change_head<BindingsState> {
|
||||
static void success(const auto & in, BindingsState & b, ExprState & s, State & ps) {
|
||||
template<> struct BuildAST<grammar::v1::expr::ancient_let> : change_head<BindingsStateRecSet> {
|
||||
static void success(const auto & in, BindingsStateRecSet & b, ExprState & s, State & ps) {
|
||||
// Added 2024-09-18. Turn into an error at some point in the future.
|
||||
// See the documentation on deprecated features for more details.
|
||||
if (!ps.featureSettings.isEnabled(Dep::AncientLet))
|
||||
@@ -675,30 +699,23 @@ template<> struct BuildAST<grammar::v1::expr::ancient_let> : change_head<Binding
|
||||
"--extra-deprecated-features ancient-let"
|
||||
);
|
||||
|
||||
b.attrs.pos = ps.at(in);
|
||||
b.attrs.recursive = true;
|
||||
s.pushExpr<ExprSelect>(b.attrs.pos, b.attrs.pos, std::make_unique<ExprAttrs>(std::move(b.attrs)), ps.s.body);
|
||||
auto pos = ps.at(in);
|
||||
b.set.pos = pos;
|
||||
s.pushExpr<ExprSelect>(pos, pos, std::make_unique<ExprSet>(std::move(b.set)), pos, ps.s.body);
|
||||
}
|
||||
};
|
||||
|
||||
template<> struct BuildAST<grammar::v1::expr::rec_set> : change_head<BindingsState> {
|
||||
static void success(const auto & in, BindingsState & b, ExprState & s, State & ps) {
|
||||
// Before inserting new attrs, check for __override and throw an error
|
||||
// (the error will initially be a warning to ease migration)
|
||||
if (!featureSettings.isEnabled(Dep::RecSetOverrides) && b.attrs.attrs.contains(ps.s.overrides)) {
|
||||
ps.overridesFound(ps.at(in));
|
||||
}
|
||||
|
||||
b.attrs.pos = ps.at(in);
|
||||
b.attrs.recursive = true;
|
||||
s.pushExpr<ExprAttrs>(b.attrs.pos, std::move(b.attrs));
|
||||
template<> struct BuildAST<grammar::v1::expr::rec_set> : change_head<BindingsStateRecSet> {
|
||||
static void success(const auto & in, BindingsStateRecSet & b, ExprState & s, State & ps) {
|
||||
b.set.pos = ps.at(in);
|
||||
s.pushExpr<ExprSet>(ps.at(in), std::move(b.set));
|
||||
}
|
||||
};
|
||||
|
||||
template<> struct BuildAST<grammar::v1::expr::set> : change_head<BindingsState> {
|
||||
static void success(const auto & in, BindingsState & b, ExprState & s, State & ps) {
|
||||
b.attrs.pos = ps.at(in);
|
||||
s.pushExpr<ExprAttrs>(b.attrs.pos, std::move(b.attrs));
|
||||
template<> struct BuildAST<grammar::v1::expr::set> : change_head<BindingsStateSet> {
|
||||
static void success(const auto & in, BindingsStateSet & b, ExprState & s, State & ps) {
|
||||
b.set.pos = ps.at(in);
|
||||
s.pushExpr<ExprSet>(ps.at(in), std::move(b.set));
|
||||
}
|
||||
};
|
||||
|
||||
@@ -835,15 +852,15 @@ template<> struct BuildAST<grammar::v1::expr::with> {
|
||||
}
|
||||
};
|
||||
|
||||
template<> struct BuildAST<grammar::v1::expr::let> : change_head<BindingsState> {
|
||||
static void success(const auto & in, BindingsState & b, ExprState & s, State & ps) {
|
||||
if (!b.attrs.dynamicAttrs.empty())
|
||||
template<> struct BuildAST<grammar::v1::expr::let> : change_head<BindingsStateLet> {
|
||||
static void success(const auto & in, BindingsStateLet & b, ExprState & s, State & ps) {
|
||||
if (!b.let.dynamicAttrs.empty())
|
||||
throw ParseError({
|
||||
.msg = HintFmt("dynamic attributes not allowed in let"),
|
||||
.pos = ps.positions[ps.at(in)]
|
||||
});
|
||||
|
||||
s.pushExpr<ExprLet>(ps.at(in), std::make_unique<ExprAttrs>(std::move(b.attrs)), b->popExprOnly());
|
||||
b.let.body = b->popExprOnly();
|
||||
s.pushExpr<ExprLet>(ps.at(in), std::move(b.let));
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -22,7 +22,7 @@
|
||||
|
||||
namespace nix {
|
||||
|
||||
Expr * EvalState::parse(
|
||||
Expr * Evaluator::parse(
|
||||
char * text,
|
||||
size_t length,
|
||||
Pos::Origin origin,
|
||||
|
||||
@@ -102,7 +102,7 @@ inline void State::addAttr(ExprAttrs * attrs, AttrPath && attrPath, std::unique_
|
||||
ExprAttrs::AttrDefs::iterator j = attrs->attrs.find(i->symbol);
|
||||
if (j != attrs->attrs.end()) {
|
||||
if (j->second.kind != ExprAttrs::AttrDef::Kind::Inherited) {
|
||||
ExprAttrs * attrs2 = dynamic_cast<ExprAttrs *>(j->second.e.get());
|
||||
ExprSet * attrs2 = dynamic_cast<ExprSet *>(j->second.e.get());
|
||||
if (!attrs2) {
|
||||
attrPath.erase(i + 1, attrPath.end());
|
||||
dupAttr(attrPath, pos, j->second.pos);
|
||||
@@ -115,12 +115,12 @@ inline void State::addAttr(ExprAttrs * attrs, AttrPath && attrPath, std::unique_
|
||||
} else {
|
||||
auto next = attrs->attrs.emplace(std::piecewise_construct,
|
||||
std::tuple(i->symbol),
|
||||
std::tuple(std::make_unique<ExprAttrs>(), pos));
|
||||
attrs = static_cast<ExprAttrs *>(next.first->second.e.get());
|
||||
std::tuple(std::make_unique<ExprSet>(), pos));
|
||||
attrs = static_cast<ExprSet *>(next.first->second.e.get());
|
||||
}
|
||||
} else {
|
||||
auto & next = attrs->dynamicAttrs.emplace_back(std::move(i->expr), std::make_unique<ExprAttrs>(), pos);
|
||||
attrs = static_cast<ExprAttrs *>(next.valueExpr.get());
|
||||
auto & next = attrs->dynamicAttrs.emplace_back(std::move(i->expr), std::make_unique<ExprSet>(), pos);
|
||||
attrs = static_cast<ExprSet *>(next.valueExpr.get());
|
||||
}
|
||||
}
|
||||
// Expr insertion.
|
||||
@@ -132,8 +132,8 @@ inline void State::addAttr(ExprAttrs * attrs, AttrPath && attrPath, std::unique_
|
||||
// e and the expr pointed by the attr path are two attribute sets,
|
||||
// we want to merge them.
|
||||
// Otherwise, throw an error.
|
||||
auto * ae = dynamic_cast<ExprAttrs *>(e.get());
|
||||
auto * jAttrs = dynamic_cast<ExprAttrs *>(j->second.e.get());
|
||||
auto * ae = dynamic_cast<ExprSet *>(e.get());
|
||||
auto * jAttrs = dynamic_cast<ExprSet *>(j->second.e.get());
|
||||
if (jAttrs && ae) {
|
||||
if (ae->inheritFromExprs && !jAttrs->inheritFromExprs)
|
||||
jAttrs->inheritFromExprs = std::make_unique<std::vector<ref<Expr>>>();
|
||||
@@ -157,8 +157,9 @@ inline void State::addAttr(ExprAttrs * attrs, AttrPath && attrPath, std::unique_
|
||||
} else {
|
||||
// Before inserting new attrs, check for __override and throw an error
|
||||
// (the error will initially be a warning to ease migration)
|
||||
if (attrs->recursive && !featureSettings.isEnabled(Dep::RecSetOverrides) && i->symbol == s.overrides) {
|
||||
overridesFound(pos);
|
||||
if (!featureSettings.isEnabled(Dep::RecSetOverrides) && i->symbol == s.overrides) {
|
||||
if (auto set = dynamic_cast<ExprSet *>(attrs); set && set->recursive)
|
||||
overridesFound(pos);
|
||||
}
|
||||
|
||||
// This attr path is not defined. Let's create it.
|
||||
|
||||
+294
-276
File diff suppressed because it is too large
Load Diff
@@ -56,7 +56,7 @@ void prim_addDrvOutputDependencies(EvalState & state, const PosIdx pos, Value *
|
||||
|
||||
auto contextSize = context.size();
|
||||
if (contextSize != 1) {
|
||||
state.error<EvalError>(
|
||||
state.ctx.errors.make<EvalError>(
|
||||
"context of string '%s' must have exactly one element, but has %d",
|
||||
*s,
|
||||
contextSize
|
||||
@@ -66,9 +66,9 @@ void prim_addDrvOutputDependencies(EvalState & state, const PosIdx pos, Value *
|
||||
(NixStringContextElem { std::visit(overloaded {
|
||||
[&](const NixStringContextElem::Opaque & c) -> NixStringContextElem::DrvDeep {
|
||||
if (!c.path.isDerivation()) {
|
||||
state.error<EvalError>(
|
||||
state.ctx.errors.make<EvalError>(
|
||||
"path '%s' is not a derivation",
|
||||
state.store->printStorePath(c.path)
|
||||
state.ctx.store->printStorePath(c.path)
|
||||
).atPos(pos).debugThrow();
|
||||
}
|
||||
return NixStringContextElem::DrvDeep {
|
||||
@@ -76,7 +76,7 @@ void prim_addDrvOutputDependencies(EvalState & state, const PosIdx pos, Value *
|
||||
};
|
||||
},
|
||||
[&](const NixStringContextElem::Built & c) -> NixStringContextElem::DrvDeep {
|
||||
state.error<EvalError>(
|
||||
state.ctx.errors.make<EvalError>(
|
||||
"`addDrvOutputDependencies` can only act on derivations, not on a derivation output such as '%1%'",
|
||||
c.output
|
||||
).atPos(pos).debugThrow();
|
||||
@@ -129,7 +129,7 @@ void prim_getContext(EvalState & state, const PosIdx pos, Value * * args, Value
|
||||
[&](NixStringContextElem::Built && b) {
|
||||
// FIXME should eventually show string context as is, no
|
||||
// resolving here.
|
||||
auto drvPath = resolveDerivedPath(*state.store, *b.drvPath);
|
||||
auto drvPath = resolveDerivedPath(*state.ctx.store, *b.drvPath);
|
||||
contextInfos[std::move(drvPath)].outputs.emplace_back(std::move(b.output));
|
||||
},
|
||||
[&](NixStringContextElem::Opaque && o) {
|
||||
@@ -138,22 +138,22 @@ void prim_getContext(EvalState & state, const PosIdx pos, Value * * args, Value
|
||||
}, ((NixStringContextElem &&) i).raw);
|
||||
}
|
||||
|
||||
auto attrs = state.buildBindings(contextInfos.size());
|
||||
auto attrs = state.ctx.buildBindings(contextInfos.size());
|
||||
|
||||
auto sAllOutputs = state.symbols.create("allOutputs");
|
||||
auto sAllOutputs = state.ctx.symbols.create("allOutputs");
|
||||
for (const auto & info : contextInfos) {
|
||||
auto infoAttrs = state.buildBindings(3);
|
||||
auto infoAttrs = state.ctx.buildBindings(3);
|
||||
if (info.second.path)
|
||||
infoAttrs.alloc(state.s.path).mkBool(true);
|
||||
infoAttrs.alloc(state.ctx.s.path).mkBool(true);
|
||||
if (info.second.allOutputs)
|
||||
infoAttrs.alloc(sAllOutputs).mkBool(true);
|
||||
if (!info.second.outputs.empty()) {
|
||||
auto & outputsVal = infoAttrs.alloc(state.s.outputs);
|
||||
outputsVal = state.mem.newList(info.second.outputs.size());
|
||||
auto & outputsVal = infoAttrs.alloc(state.ctx.s.outputs);
|
||||
outputsVal = state.ctx.mem.newList(info.second.outputs.size());
|
||||
for (const auto & [i, output] : enumerate(info.second.outputs))
|
||||
(outputsVal.listElems()[i] = state.mem.allocValue())->mkString(output);
|
||||
(outputsVal.listElems()[i] = state.ctx.mem.allocValue())->mkString(output);
|
||||
}
|
||||
attrs.alloc(state.store->printStorePath(info.first)).mkAttrs(infoAttrs);
|
||||
attrs.alloc(state.ctx.store->printStorePath(info.first)).mkAttrs(infoAttrs);
|
||||
}
|
||||
|
||||
v.mkAttrs(attrs);
|
||||
@@ -172,19 +172,19 @@ static void prim_appendContext(EvalState & state, const PosIdx pos, Value * * ar
|
||||
|
||||
state.forceAttrs(*args[1], pos, "while evaluating the second argument passed to builtins.appendContext");
|
||||
|
||||
auto sAllOutputs = state.symbols.create("allOutputs");
|
||||
auto sAllOutputs = state.ctx.symbols.create("allOutputs");
|
||||
for (auto & i : *args[1]->attrs) {
|
||||
const auto & name = state.symbols[i.name];
|
||||
if (!state.store->isStorePath(name))
|
||||
state.error<EvalError>(
|
||||
const auto & name = state.ctx.symbols[i.name];
|
||||
if (!state.ctx.store->isStorePath(name))
|
||||
state.ctx.errors.make<EvalError>(
|
||||
"context key '%s' is not a store path",
|
||||
name
|
||||
).atPos(i.pos).debugThrow();
|
||||
auto namePath = state.store->parseStorePath(name);
|
||||
auto namePath = state.ctx.store->parseStorePath(name);
|
||||
if (!settings.readOnlyMode)
|
||||
state.store->ensurePath(namePath);
|
||||
state.ctx.store->ensurePath(namePath);
|
||||
state.forceAttrs(*i.value, i.pos, "while evaluating the value of a string context");
|
||||
auto iter = i.value->attrs->find(state.s.path);
|
||||
auto iter = i.value->attrs->find(state.ctx.s.path);
|
||||
if (iter != i.value->attrs->end()) {
|
||||
if (state.forceBool(*iter->value, iter->pos, "while evaluating the `path` attribute of a string context"))
|
||||
context.emplace(NixStringContextElem::Opaque {
|
||||
@@ -196,7 +196,7 @@ static void prim_appendContext(EvalState & state, const PosIdx pos, Value * * ar
|
||||
if (iter != i.value->attrs->end()) {
|
||||
if (state.forceBool(*iter->value, iter->pos, "while evaluating the `allOutputs` attribute of a string context")) {
|
||||
if (!isDerivation(name)) {
|
||||
state.error<EvalError>(
|
||||
state.ctx.errors.make<EvalError>(
|
||||
"tried to add all-outputs context of %s, which is not a derivation, to a string",
|
||||
name
|
||||
).atPos(i.pos).debugThrow();
|
||||
@@ -207,11 +207,11 @@ static void prim_appendContext(EvalState & state, const PosIdx pos, Value * * ar
|
||||
}
|
||||
}
|
||||
|
||||
iter = i.value->attrs->find(state.s.outputs);
|
||||
iter = i.value->attrs->find(state.ctx.s.outputs);
|
||||
if (iter != i.value->attrs->end()) {
|
||||
state.forceList(*iter->value, iter->pos, "while evaluating the `outputs` attribute of a string context");
|
||||
if (iter->value->listSize() && !isDerivation(name)) {
|
||||
state.error<EvalError>(
|
||||
state.ctx.errors.make<EvalError>(
|
||||
"tried to add derivation output context of %s, which is not a derivation, to a string",
|
||||
name
|
||||
).atPos(i.pos).debugThrow();
|
||||
|
||||
@@ -19,24 +19,24 @@ static void runFetchClosureWithRewrite(EvalState & state, const PosIdx pos, Stor
|
||||
|
||||
// establish toPath or throw
|
||||
|
||||
if (!toPathMaybe || !state.store->isValidPath(*toPathMaybe)) {
|
||||
auto rewrittenPath = makeContentAddressed(fromStore, *state.store, fromPath);
|
||||
if (!toPathMaybe || !state.ctx.store->isValidPath(*toPathMaybe)) {
|
||||
auto rewrittenPath = makeContentAddressed(fromStore, *state.ctx.store, fromPath);
|
||||
if (toPathMaybe && *toPathMaybe != rewrittenPath)
|
||||
throw Error({
|
||||
.msg = HintFmt("rewriting '%s' to content-addressed form yielded '%s', while '%s' was expected",
|
||||
state.store->printStorePath(fromPath),
|
||||
state.store->printStorePath(rewrittenPath),
|
||||
state.store->printStorePath(*toPathMaybe)),
|
||||
.pos = state.positions[pos]
|
||||
state.ctx.store->printStorePath(fromPath),
|
||||
state.ctx.store->printStorePath(rewrittenPath),
|
||||
state.ctx.store->printStorePath(*toPathMaybe)),
|
||||
.pos = state.ctx.positions[pos]
|
||||
});
|
||||
if (!toPathMaybe)
|
||||
throw Error({
|
||||
.msg = HintFmt(
|
||||
"rewriting '%s' to content-addressed form yielded '%s'\n"
|
||||
"Use this value for the 'toPath' attribute passed to 'fetchClosure'",
|
||||
state.store->printStorePath(fromPath),
|
||||
state.store->printStorePath(rewrittenPath)),
|
||||
.pos = state.positions[pos]
|
||||
state.ctx.store->printStorePath(fromPath),
|
||||
state.ctx.store->printStorePath(rewrittenPath)),
|
||||
.pos = state.ctx.positions[pos]
|
||||
});
|
||||
}
|
||||
|
||||
@@ -44,21 +44,21 @@ static void runFetchClosureWithRewrite(EvalState & state, const PosIdx pos, Stor
|
||||
|
||||
// check and return
|
||||
|
||||
auto resultInfo = state.store->queryPathInfo(toPath);
|
||||
auto resultInfo = state.ctx.store->queryPathInfo(toPath);
|
||||
|
||||
if (!resultInfo->isContentAddressed(*state.store)) {
|
||||
if (!resultInfo->isContentAddressed(*state.ctx.store)) {
|
||||
// We don't perform the rewriting when outPath already exists, as an optimisation.
|
||||
// However, we can quickly detect a mistake if the toPath is input addressed.
|
||||
throw Error({
|
||||
.msg = HintFmt(
|
||||
"The 'toPath' value '%s' is input-addressed, so it can't possibly be the result of rewriting to a content-addressed path.\n\n"
|
||||
"Set 'toPath' to an empty string to make Lix report the correct content-addressed path.",
|
||||
state.store->printStorePath(toPath)),
|
||||
.pos = state.positions[pos]
|
||||
state.ctx.store->printStorePath(toPath)),
|
||||
.pos = state.ctx.positions[pos]
|
||||
});
|
||||
}
|
||||
|
||||
state.mkStorePathString(toPath, v);
|
||||
state.ctx.paths.mkStorePathString(toPath, v);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -66,12 +66,12 @@ static void runFetchClosureWithRewrite(EvalState & state, const PosIdx pos, Stor
|
||||
*/
|
||||
static void runFetchClosureWithContentAddressedPath(EvalState & state, const PosIdx pos, Store & fromStore, const StorePath & fromPath, Value & v) {
|
||||
|
||||
if (!state.store->isValidPath(fromPath))
|
||||
copyClosure(fromStore, *state.store, RealisedPath::Set { fromPath });
|
||||
if (!state.ctx.store->isValidPath(fromPath))
|
||||
copyClosure(fromStore, *state.ctx.store, RealisedPath::Set { fromPath });
|
||||
|
||||
auto info = state.store->queryPathInfo(fromPath);
|
||||
auto info = state.ctx.store->queryPathInfo(fromPath);
|
||||
|
||||
if (!info->isContentAddressed(*state.store)) {
|
||||
if (!info->isContentAddressed(*state.ctx.store)) {
|
||||
throw Error({
|
||||
.msg = HintFmt(
|
||||
"The 'fromPath' value '%s' is input-addressed, but 'inputAddressed' is set to 'false' (default).\n\n"
|
||||
@@ -79,12 +79,12 @@ static void runFetchClosureWithContentAddressedPath(EvalState & state, const Pos
|
||||
" inputAddressed = true;\n\n"
|
||||
"to the 'fetchClosure' arguments.\n\n"
|
||||
"Note that to ensure authenticity input-addressed store paths, users must configure a trusted binary cache public key on their systems. This is not needed for content-addressed paths.",
|
||||
state.store->printStorePath(fromPath)),
|
||||
.pos = state.positions[pos]
|
||||
state.ctx.store->printStorePath(fromPath)),
|
||||
.pos = state.ctx.positions[pos]
|
||||
});
|
||||
}
|
||||
|
||||
state.mkStorePathString(fromPath, v);
|
||||
state.ctx.paths.mkStorePathString(fromPath, v);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -92,22 +92,22 @@ static void runFetchClosureWithContentAddressedPath(EvalState & state, const Pos
|
||||
*/
|
||||
static void runFetchClosureWithInputAddressedPath(EvalState & state, const PosIdx pos, Store & fromStore, const StorePath & fromPath, Value & v) {
|
||||
|
||||
if (!state.store->isValidPath(fromPath))
|
||||
copyClosure(fromStore, *state.store, RealisedPath::Set { fromPath });
|
||||
if (!state.ctx.store->isValidPath(fromPath))
|
||||
copyClosure(fromStore, *state.ctx.store, RealisedPath::Set { fromPath });
|
||||
|
||||
auto info = state.store->queryPathInfo(fromPath);
|
||||
auto info = state.ctx.store->queryPathInfo(fromPath);
|
||||
|
||||
if (info->isContentAddressed(*state.store)) {
|
||||
if (info->isContentAddressed(*state.ctx.store)) {
|
||||
throw Error({
|
||||
.msg = HintFmt(
|
||||
"The store object referred to by 'fromPath' at '%s' is not input-addressed, but 'inputAddressed' is set to 'true'.\n\n"
|
||||
"Remove the 'inputAddressed' attribute (it defaults to 'false') to expect 'fromPath' to be content-addressed",
|
||||
state.store->printStorePath(fromPath)),
|
||||
.pos = state.positions[pos]
|
||||
state.ctx.store->printStorePath(fromPath)),
|
||||
.pos = state.ctx.positions[pos]
|
||||
});
|
||||
}
|
||||
|
||||
state.mkStorePathString(fromPath, v);
|
||||
state.ctx.paths.mkStorePathString(fromPath, v);
|
||||
}
|
||||
|
||||
typedef std::optional<StorePath> StorePathOrGap;
|
||||
@@ -122,7 +122,7 @@ void prim_fetchClosure(EvalState & state, const PosIdx pos, Value * * args, Valu
|
||||
std::optional<bool> inputAddressedMaybe;
|
||||
|
||||
for (auto & attr : *args[0]->attrs) {
|
||||
const auto & attrName = state.symbols[attr.name];
|
||||
const auto & attrName = state.ctx.symbols[attr.name];
|
||||
auto attrHint = [&]() -> std::string {
|
||||
return "while evaluating the '" + attrName + "' attribute passed to builtins.fetchClosure";
|
||||
};
|
||||
@@ -154,14 +154,14 @@ void prim_fetchClosure(EvalState & state, const PosIdx pos, Value * * args, Valu
|
||||
else
|
||||
throw Error({
|
||||
.msg = HintFmt("attribute '%s' isn't supported in call to 'fetchClosure'", attrName),
|
||||
.pos = state.positions[pos]
|
||||
.pos = state.ctx.positions[pos]
|
||||
});
|
||||
}
|
||||
|
||||
if (!fromPath)
|
||||
throw Error({
|
||||
.msg = HintFmt("attribute '%s' is missing in call to 'fetchClosure'", "fromPath"),
|
||||
.pos = state.positions[pos]
|
||||
.pos = state.ctx.positions[pos]
|
||||
});
|
||||
|
||||
bool inputAddressed = inputAddressedMaybe.value_or(false);
|
||||
@@ -172,14 +172,14 @@ void prim_fetchClosure(EvalState & state, const PosIdx pos, Value * * args, Valu
|
||||
.msg = HintFmt("attribute '%s' is set to true, but '%s' is also set. Please remove one of them",
|
||||
"inputAddressed",
|
||||
"toPath"),
|
||||
.pos = state.positions[pos]
|
||||
.pos = state.ctx.positions[pos]
|
||||
});
|
||||
}
|
||||
|
||||
if (!fromStoreUrl)
|
||||
throw Error({
|
||||
.msg = HintFmt("attribute '%s' is missing in call to 'fetchClosure'", "fromStore"),
|
||||
.pos = state.positions[pos]
|
||||
.pos = state.ctx.positions[pos]
|
||||
});
|
||||
|
||||
auto parsedURL = parseURL(*fromStoreUrl);
|
||||
@@ -189,13 +189,13 @@ void prim_fetchClosure(EvalState & state, const PosIdx pos, Value * * args, Valu
|
||||
!(getEnv("_NIX_IN_TEST").has_value() && parsedURL.scheme == "file"))
|
||||
throw Error({
|
||||
.msg = HintFmt("'fetchClosure' only supports http:// and https:// stores"),
|
||||
.pos = state.positions[pos]
|
||||
.pos = state.ctx.positions[pos]
|
||||
});
|
||||
|
||||
if (!parsedURL.query.empty())
|
||||
throw Error({
|
||||
.msg = HintFmt("'fetchClosure' does not support URL query parameters (in '%s')", *fromStoreUrl),
|
||||
.pos = state.positions[pos]
|
||||
.pos = state.ctx.positions[pos]
|
||||
});
|
||||
|
||||
auto fromStore = openStore(parsedURL.to_string());
|
||||
|
||||
@@ -21,7 +21,7 @@ static void prim_fetchMercurial(EvalState & state, const PosIdx pos, Value * * a
|
||||
if (args[0]->type() == nAttrs) {
|
||||
|
||||
for (auto & attr : *args[0]->attrs) {
|
||||
std::string_view n(state.symbols[attr.name]);
|
||||
std::string_view n(state.ctx.symbols[attr.name]);
|
||||
if (n == "url")
|
||||
url = state.coerceToString(attr.pos, *attr.value, context,
|
||||
"while evaluating the `url` attribute passed to builtins.fetchMercurial",
|
||||
@@ -38,11 +38,11 @@ static void prim_fetchMercurial(EvalState & state, const PosIdx pos, Value * * a
|
||||
else if (n == "name")
|
||||
name = state.forceStringNoCtx(*attr.value, attr.pos, "while evaluating the `name` attribute passed to builtins.fetchMercurial");
|
||||
else
|
||||
state.error<EvalError>("unsupported argument '%s' to 'fetchMercurial'", state.symbols[attr.name]).atPos(attr.pos).debugThrow();
|
||||
state.ctx.errors.make<EvalError>("unsupported argument '%s' to 'fetchMercurial'", state.ctx.symbols[attr.name]).atPos(attr.pos).debugThrow();
|
||||
}
|
||||
|
||||
if (url.empty())
|
||||
state.error<EvalError>("'url' argument required").atPos(pos).debugThrow();
|
||||
state.ctx.errors.make<EvalError>("'url' argument required").atPos(pos).debugThrow();
|
||||
|
||||
} else
|
||||
url = state.coerceToString(pos, *args[0], context,
|
||||
@@ -51,7 +51,7 @@ static void prim_fetchMercurial(EvalState & state, const PosIdx pos, Value * * a
|
||||
|
||||
// FIXME: git externals probably can be used to bypass the URI
|
||||
// whitelist. Ah well.
|
||||
state.checkURI(url);
|
||||
state.ctx.paths.checkURI(url);
|
||||
|
||||
if (evalSettings.pureEval && !rev)
|
||||
throw Error("in pure evaluation mode, 'fetchMercurial' requires a Mercurial revision");
|
||||
@@ -65,10 +65,10 @@ static void prim_fetchMercurial(EvalState & state, const PosIdx pos, Value * * a
|
||||
auto input = fetchers::Input::fromAttrs(std::move(attrs));
|
||||
|
||||
// FIXME: use name
|
||||
auto [tree, input2] = input.fetch(state.store);
|
||||
auto [tree, input2] = input.fetch(state.ctx.store);
|
||||
|
||||
auto attrs2 = state.buildBindings(8);
|
||||
state.mkStorePathString(tree.storePath, attrs2.alloc(state.s.outPath));
|
||||
auto attrs2 = state.ctx.buildBindings(8);
|
||||
state.ctx.paths.mkStorePathString(tree.storePath, attrs2.alloc(state.ctx.s.outPath));
|
||||
if (input2.getRef())
|
||||
attrs2.alloc("branch").mkString(*input2.getRef());
|
||||
// Backward compatibility: set 'rev' to
|
||||
@@ -80,7 +80,7 @@ static void prim_fetchMercurial(EvalState & state, const PosIdx pos, Value * * a
|
||||
attrs2.alloc("revCount").mkInt(*revCount);
|
||||
v.mkAttrs(attrs2);
|
||||
|
||||
state.allowPath(tree.storePath);
|
||||
state.ctx.paths.allowPath(tree.storePath);
|
||||
}
|
||||
|
||||
static RegisterPrimOp r_fetchMercurial({
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
namespace nix {
|
||||
|
||||
void emitTreeAttrs(
|
||||
EvalState & state,
|
||||
Evaluator & state,
|
||||
const fetchers::Tree & tree,
|
||||
const fetchers::Input & input,
|
||||
Value & v,
|
||||
@@ -27,7 +27,7 @@ void emitTreeAttrs(
|
||||
auto attrs = state.buildBindings(10);
|
||||
|
||||
|
||||
state.mkStorePathString(tree.storePath, attrs.alloc(state.s.outPath));
|
||||
state.paths.mkStorePathString(tree.storePath, attrs.alloc(state.s.outPath));
|
||||
|
||||
// FIXME: support arbitrary input attributes.
|
||||
|
||||
@@ -74,7 +74,7 @@ void emitTreeAttrs(
|
||||
|
||||
std::string fixURI(std::string uri, EvalState & state, const std::string & defaultScheme = "file")
|
||||
{
|
||||
state.checkURI(uri);
|
||||
state.ctx.paths.checkURI(uri);
|
||||
if (uri.find("://") == std::string::npos) {
|
||||
const auto p = ParsedURL {
|
||||
.scheme = defaultScheme,
|
||||
@@ -122,50 +122,50 @@ static void fetchTree(
|
||||
|
||||
fetchers::Attrs attrs;
|
||||
|
||||
if (auto aType = args[0]->attrs->get(state.s.type)) {
|
||||
if (auto aType = args[0]->attrs->get(state.ctx.s.type)) {
|
||||
if (type)
|
||||
state.error<EvalError>(
|
||||
state.ctx.errors.make<EvalError>(
|
||||
"unexpected attribute 'type'"
|
||||
).atPos(pos).debugThrow();
|
||||
type = state.forceStringNoCtx(*aType->value, aType->pos, "while evaluating the `type` attribute passed to builtins.fetchTree");
|
||||
} else if (!type)
|
||||
state.error<EvalError>(
|
||||
state.ctx.errors.make<EvalError>(
|
||||
"attribute 'type' is missing in call to 'fetchTree'"
|
||||
).atPos(pos).debugThrow();
|
||||
|
||||
attrs.emplace("type", type.value());
|
||||
|
||||
for (auto & attr : *args[0]->attrs) {
|
||||
if (attr.name == state.s.type) continue;
|
||||
if (attr.name == state.ctx.s.type) continue;
|
||||
state.forceValue(*attr.value, attr.pos);
|
||||
if (attr.value->type() == nPath || attr.value->type() == nString) {
|
||||
auto s = state.coerceToString(attr.pos, *attr.value, context, "", false, false).toOwned();
|
||||
attrs.emplace(state.symbols[attr.name],
|
||||
state.symbols[attr.name] == "url"
|
||||
attrs.emplace(state.ctx.symbols[attr.name],
|
||||
state.ctx.symbols[attr.name] == "url"
|
||||
? type == "git"
|
||||
? fixURIForGit(s, state)
|
||||
: fixURI(s, state)
|
||||
: s);
|
||||
}
|
||||
else if (attr.value->type() == nBool)
|
||||
attrs.emplace(state.symbols[attr.name], Explicit<bool>{attr.value->boolean});
|
||||
attrs.emplace(state.ctx.symbols[attr.name], Explicit<bool>{attr.value->boolean});
|
||||
else if (attr.value->type() == nInt) {
|
||||
auto intValue = attr.value->integer.value;
|
||||
|
||||
if (intValue < 0) {
|
||||
state.error<EvalError>("negative value given for fetchTree attr %1%: %2%", state.symbols[attr.name], intValue).atPos(pos).debugThrow();
|
||||
state.ctx.errors.make<EvalError>("negative value given for fetchTree attr %1%: %2%", state.ctx.symbols[attr.name], intValue).atPos(pos).debugThrow();
|
||||
}
|
||||
unsigned long asUnsigned = intValue;
|
||||
|
||||
attrs.emplace(state.symbols[attr.name], asUnsigned);
|
||||
attrs.emplace(state.ctx.symbols[attr.name], asUnsigned);
|
||||
} else
|
||||
state.error<TypeError>("fetchTree argument '%s' is %s while a string, Boolean or integer is expected",
|
||||
state.symbols[attr.name], showType(*attr.value)).debugThrow();
|
||||
state.ctx.errors.make<TypeError>("fetchTree argument '%s' is %s while a string, Boolean or integer is expected",
|
||||
state.ctx.symbols[attr.name], showType(*attr.value)).debugThrow();
|
||||
}
|
||||
|
||||
if (!params.allowNameArgument)
|
||||
if (auto nameIter = attrs.find("name"); nameIter != attrs.end())
|
||||
state.error<EvalError>(
|
||||
state.ctx.errors.make<EvalError>(
|
||||
"attribute 'name' isn’t supported in call to 'fetchTree'"
|
||||
).atPos(pos).debugThrow();
|
||||
|
||||
@@ -186,17 +186,17 @@ static void fetchTree(
|
||||
}
|
||||
|
||||
if (!evalSettings.pureEval && !input.isDirect())
|
||||
input = lookupInRegistries(state.store, input).first;
|
||||
input = lookupInRegistries(state.ctx.store, input).first;
|
||||
|
||||
if (evalSettings.pureEval && !input.isLocked()) {
|
||||
state.error<EvalError>("in pure evaluation mode, 'fetchTree' requires a locked input").atPos(pos).debugThrow();
|
||||
state.ctx.errors.make<EvalError>("in pure evaluation mode, 'fetchTree' requires a locked input").atPos(pos).debugThrow();
|
||||
}
|
||||
|
||||
auto [tree, input2] = input.fetch(state.store);
|
||||
auto [tree, input2] = input.fetch(state.ctx.store);
|
||||
|
||||
state.allowPath(tree.storePath);
|
||||
state.ctx.paths.allowPath(tree.storePath);
|
||||
|
||||
emitTreeAttrs(state, tree, input2, v, params.emptyRevFallback, false);
|
||||
emitTreeAttrs(state.ctx, tree, input2, v, params.emptyRevFallback, false);
|
||||
}
|
||||
|
||||
static void prim_fetchTree(EvalState & state, const PosIdx pos, Value * * args, Value & v)
|
||||
@@ -223,7 +223,7 @@ static void fetch(EvalState & state, const PosIdx pos, Value * * args, Value & v
|
||||
if (args[0]->type() == nAttrs) {
|
||||
|
||||
for (auto & attr : *args[0]->attrs) {
|
||||
std::string_view n(state.symbols[attr.name]);
|
||||
std::string_view n(state.ctx.symbols[attr.name]);
|
||||
if (n == "url")
|
||||
url = state.forceStringNoCtx(*attr.value, attr.pos, "while evaluating the url we should fetch");
|
||||
else if (n == "sha256")
|
||||
@@ -231,12 +231,12 @@ static void fetch(EvalState & state, const PosIdx pos, Value * * args, Value & v
|
||||
else if (n == "name")
|
||||
name = state.forceStringNoCtx(*attr.value, attr.pos, "while evaluating the name of the content we should fetch");
|
||||
else
|
||||
state.error<EvalError>("unsupported argument '%s' to '%s'", n, who)
|
||||
state.ctx.errors.make<EvalError>("unsupported argument '%s' to '%s'", n, who)
|
||||
.atPos(pos).debugThrow();
|
||||
}
|
||||
|
||||
if (!url)
|
||||
state.error<EvalError>(
|
||||
state.ctx.errors.make<EvalError>(
|
||||
"'url' argument required").atPos(pos).debugThrow();
|
||||
} else
|
||||
url = state.forceStringNoCtx(*args[0], pos, "while evaluating the url we should fetch");
|
||||
@@ -244,17 +244,17 @@ static void fetch(EvalState & state, const PosIdx pos, Value * * args, Value & v
|
||||
if (who == "fetchTarball")
|
||||
url = evalSettings.resolvePseudoUrl(*url);
|
||||
|
||||
state.checkURI(*url);
|
||||
state.ctx.paths.checkURI(*url);
|
||||
|
||||
if (name == "")
|
||||
name = baseNameOf(*url);
|
||||
|
||||
if (evalSettings.pureEval && !expectedHash)
|
||||
state.error<EvalError>("in pure evaluation mode, '%s' requires a 'sha256' argument", who).atPos(pos).debugThrow();
|
||||
state.ctx.errors.make<EvalError>("in pure evaluation mode, '%s' requires a 'sha256' argument", who).atPos(pos).debugThrow();
|
||||
|
||||
// early exit if pinned and already in the store
|
||||
if (expectedHash && expectedHash->type == HashType::SHA256) {
|
||||
auto expectedPath = state.store->makeFixedOutputPath(
|
||||
auto expectedPath = state.ctx.store->makeFixedOutputPath(
|
||||
name,
|
||||
FixedOutputInfo {
|
||||
.method = unpack ? FileIngestionMethod::Recursive : FileIngestionMethod::Flat,
|
||||
@@ -262,8 +262,8 @@ static void fetch(EvalState & state, const PosIdx pos, Value * * args, Value & v
|
||||
.references = {}
|
||||
});
|
||||
|
||||
if (state.store->isValidPath(expectedPath)) {
|
||||
state.allowAndSetStorePathString(expectedPath, v);
|
||||
if (state.ctx.store->isValidPath(expectedPath)) {
|
||||
state.ctx.paths.allowAndSetStorePathString(expectedPath, v);
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -272,15 +272,15 @@ static void fetch(EvalState & state, const PosIdx pos, Value * * args, Value & v
|
||||
// https://github.com/NixOS/nix/issues/4313
|
||||
auto storePath =
|
||||
unpack
|
||||
? fetchers::downloadTarball(state.store, *url, name, (bool) expectedHash).tree.storePath
|
||||
: fetchers::downloadFile(state.store, *url, name, (bool) expectedHash).storePath;
|
||||
? fetchers::downloadTarball(state.ctx.store, *url, name, (bool) expectedHash).tree.storePath
|
||||
: fetchers::downloadFile(state.ctx.store, *url, name, (bool) expectedHash).storePath;
|
||||
|
||||
if (expectedHash) {
|
||||
auto hash = unpack
|
||||
? state.store->queryPathInfo(storePath)->narHash
|
||||
: hashFile(HashType::SHA256, state.store->toRealPath(storePath));
|
||||
? state.ctx.store->queryPathInfo(storePath)->narHash
|
||||
: hashFile(HashType::SHA256, state.ctx.store->toRealPath(storePath));
|
||||
if (hash != *expectedHash) {
|
||||
state.error<EvalError>(
|
||||
state.ctx.errors.make<EvalError>(
|
||||
"hash mismatch in file downloaded from '%s':\n specified: %s\n got: %s",
|
||||
*url,
|
||||
expectedHash->to_string(Base::Base32, true),
|
||||
@@ -290,7 +290,7 @@ static void fetch(EvalState & state, const PosIdx pos, Value * * args, Value & v
|
||||
}
|
||||
}
|
||||
|
||||
state.allowAndSetStorePathString(storePath, v);
|
||||
state.ctx.paths.allowAndSetStorePathString(storePath, v);
|
||||
}
|
||||
|
||||
void prim_fetchurl(EvalState & state, const PosIdx pos, Value * * args, Value & v)
|
||||
|
||||
@@ -26,7 +26,7 @@ void prim_fromTOML(EvalState & state, const PosIdx pos, Value * * args, Value &
|
||||
size_t size = 0;
|
||||
for (auto & i : table) { (void) i; size++; }
|
||||
|
||||
auto attrs = state.buildBindings(size);
|
||||
auto attrs = state.ctx.buildBindings(size);
|
||||
|
||||
for(auto & elem : table)
|
||||
visit(attrs.alloc(elem.first), elem.second);
|
||||
@@ -39,9 +39,9 @@ void prim_fromTOML(EvalState & state, const PosIdx pos, Value * * args, Value &
|
||||
auto array = toml::get<std::vector<toml::value>>(t);
|
||||
|
||||
size_t size = array.size();
|
||||
v = state.mem.newList(size);
|
||||
v = state.ctx.mem.newList(size);
|
||||
for (size_t i = 0; i < size; ++i)
|
||||
visit(*(v.listElems()[i] = state.mem.allocValue()), array[i]);
|
||||
visit(*(v.listElems()[i] = state.ctx.mem.allocValue()), array[i]);
|
||||
}
|
||||
break;;
|
||||
case toml::value_t::boolean:
|
||||
@@ -62,7 +62,7 @@ void prim_fromTOML(EvalState & state, const PosIdx pos, Value * * args, Value &
|
||||
case toml::value_t::local_time:
|
||||
{
|
||||
if (experimentalFeatureSettings.isEnabled(Xp::ParseTomlTimestamps)) {
|
||||
auto attrs = state.buildBindings(2);
|
||||
auto attrs = state.ctx.buildBindings(2);
|
||||
attrs.alloc("_type").mkString("timestamp");
|
||||
std::ostringstream s;
|
||||
s << t;
|
||||
@@ -83,7 +83,7 @@ void prim_fromTOML(EvalState & state, const PosIdx pos, Value * * args, Value &
|
||||
try {
|
||||
visit(val, toml::parse(tomlStream, "fromTOML" /* the "filename" */));
|
||||
} catch (std::exception & e) { // TODO: toml::syntax_error
|
||||
state.error<EvalError>("while parsing TOML: %s", e.what()).atPos(pos).debugThrow();
|
||||
state.ctx.errors.make<EvalError>("while parsing TOML: %s", e.what()).atPos(pos).debugThrow();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+5
-11
@@ -231,11 +231,11 @@ private:
|
||||
|
||||
void printDerivation(Value & v)
|
||||
{
|
||||
Bindings::iterator i = v.attrs->find(state.s.drvPath);
|
||||
Bindings::iterator i = v.attrs->find(state.ctx.s.drvPath);
|
||||
NixStringContext context;
|
||||
std::string storePath;
|
||||
if (i != v.attrs->end())
|
||||
storePath = state.store->printStorePath(state.coerceToStorePath(i->pos, *i->value, context, "while evaluating the drvPath of a derivation"));
|
||||
storePath = state.ctx.store->printStorePath(state.coerceToStorePath(i->pos, *i->value, context, "while evaluating the drvPath of a derivation"));
|
||||
|
||||
if (options.ansiColors)
|
||||
output << ANSI_GREEN;
|
||||
@@ -287,7 +287,7 @@ private:
|
||||
|
||||
AttrVec sorted;
|
||||
for (auto & i : *v.attrs)
|
||||
sorted.emplace_back(std::pair(state.symbols[i.name], i.value));
|
||||
sorted.emplace_back(std::pair(state.ctx.symbols[i.name], i.value));
|
||||
|
||||
if (options.maxAttrs == std::numeric_limits<size_t>::max())
|
||||
std::sort(sorted.begin(), sorted.end());
|
||||
@@ -396,11 +396,11 @@ private:
|
||||
output << "lambda";
|
||||
if (v.lambda.fun) {
|
||||
if (v.lambda.fun->name) {
|
||||
output << " " << state.symbols[v.lambda.fun->name];
|
||||
output << " " << state.ctx.symbols[v.lambda.fun->name];
|
||||
}
|
||||
|
||||
std::ostringstream s;
|
||||
s << state.positions[v.lambda.fun->pos];
|
||||
s << state.ctx.positions[v.lambda.fun->pos];
|
||||
output << " @ " << filterANSIEscapes(s.str());
|
||||
}
|
||||
} else if (v.isPrimOp()) {
|
||||
@@ -581,10 +581,4 @@ fmt_internal::HintFmt & fmt_internal::HintFmt::operator%(const ValuePrinter & va
|
||||
return *this;
|
||||
}
|
||||
|
||||
std::ostream & operator<<(std::ostream & output, ExprPrinter const & printer)
|
||||
{
|
||||
printer.expr.show(printer.state.symbols, output);
|
||||
return output;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -81,26 +81,4 @@ std::ostream & operator<<(std::ostream & output, const ValuePrinter & printer);
|
||||
template<>
|
||||
fmt_internal::HintFmt & fmt_internal::HintFmt::operator%(const ValuePrinter & value);
|
||||
|
||||
/**
|
||||
* A partially-applied form of Expr::show(), which can be formatted using `<<`
|
||||
* without allocating an intermediate string.
|
||||
* This class should not outlive the eval state or it will UAF.
|
||||
* FIXME: This should take `nix::ref`s, to avoid that, but our eval methods all have
|
||||
* EvalState &, not ref<EvalState>, and constructing a new shared_ptr to data that
|
||||
* already has a shared_ptr is a much bigger footgun. In the current architecture of
|
||||
* libexpr, using an ExprPrinter after an EvalState has been destroyed would be
|
||||
* pretty hard.
|
||||
*/
|
||||
class ExprPrinter
|
||||
{
|
||||
/** The eval state used to get symbols. */
|
||||
EvalState const & state;
|
||||
/** The expression to print. */
|
||||
Expr const & expr;
|
||||
|
||||
public:
|
||||
ExprPrinter(EvalState const & state, Expr const & expr) : state(state), expr(expr) { }
|
||||
friend std::ostream & operator << (std::ostream & output, ExprPrinter const & printer);
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@@ -97,15 +97,6 @@ public:
|
||||
return Symbol(idx + 1);
|
||||
}
|
||||
|
||||
std::vector<SymbolStr> resolve(const std::vector<Symbol> & symbols) const
|
||||
{
|
||||
std::vector<SymbolStr> result;
|
||||
result.reserve(symbols.size());
|
||||
for (auto sym : symbols)
|
||||
result.push_back((*this)[sym]);
|
||||
return result;
|
||||
}
|
||||
|
||||
SymbolStr operator[](Symbol s) const
|
||||
{
|
||||
if (s.id == 0 || s.id > store.size())
|
||||
|
||||
@@ -36,10 +36,10 @@ json printValueAsJSON(EvalState & state, bool strict,
|
||||
|
||||
case nPath:
|
||||
if (copyToStore)
|
||||
out = state.store->printStorePath(
|
||||
state.copyPathToStore(context, v.path()));
|
||||
out = state.ctx.store->printStorePath(
|
||||
state.ctx.paths.copyPathToStore(context, v.path(), state.ctx.repair));
|
||||
else
|
||||
out = v.path().path.abs();
|
||||
out = v.path().to_string();
|
||||
break;
|
||||
|
||||
case nNull:
|
||||
@@ -52,18 +52,18 @@ json printValueAsJSON(EvalState & state, bool strict,
|
||||
out = *maybeString;
|
||||
break;
|
||||
}
|
||||
auto i = v.attrs->find(state.s.outPath);
|
||||
auto i = v.attrs->find(state.ctx.s.outPath);
|
||||
if (i == v.attrs->end()) {
|
||||
out = json::object();
|
||||
StringSet names;
|
||||
for (auto & j : *v.attrs)
|
||||
names.emplace(state.symbols[j.name]);
|
||||
names.emplace(state.ctx.symbols[j.name]);
|
||||
for (auto & j : names) {
|
||||
Attr & a(*v.attrs->find(state.symbols.create(j)));
|
||||
Attr & a(*v.attrs->find(state.ctx.symbols.create(j)));
|
||||
try {
|
||||
out[j] = printValueAsJSON(state, strict, *a.value, a.pos, context, copyToStore);
|
||||
} catch (Error & e) {
|
||||
e.addTrace(state.positions[a.pos],
|
||||
e.addTrace(state.ctx.positions[a.pos],
|
||||
HintFmt("while evaluating attribute '%1%'", j));
|
||||
throw;
|
||||
}
|
||||
@@ -80,7 +80,7 @@ json printValueAsJSON(EvalState & state, bool strict,
|
||||
try {
|
||||
out.push_back(printValueAsJSON(state, strict, *elem, pos, context, copyToStore));
|
||||
} catch (Error & e) {
|
||||
e.addTrace(state.positions[pos],
|
||||
e.addTrace(state.ctx.positions[pos],
|
||||
HintFmt("while evaluating list element at index %1%", i));
|
||||
throw;
|
||||
}
|
||||
@@ -99,7 +99,7 @@ json printValueAsJSON(EvalState & state, bool strict,
|
||||
|
||||
case nThunk:
|
||||
case nFunction:
|
||||
state.error<TypeError>(
|
||||
state.ctx.errors.make<TypeError>(
|
||||
"cannot convert %1% to JSON",
|
||||
showType(v)
|
||||
)
|
||||
@@ -118,7 +118,7 @@ void printValueAsJSON(EvalState & state, bool strict,
|
||||
json ExternalValueBase::printValueAsJSON(EvalState & state, bool strict,
|
||||
NixStringContext & context, bool copyToStore) const
|
||||
{
|
||||
state.error<TypeError>("cannot convert %1% to JSON", showType())
|
||||
state.ctx.errors.make<TypeError>("cannot convert %1% to JSON", showType())
|
||||
.debugThrow();
|
||||
}
|
||||
|
||||
|
||||
+13
-13
@@ -21,8 +21,8 @@ static void printValueAsXML(EvalState & state, bool strict, bool location,
|
||||
|
||||
static void posToXML(EvalState & state, XMLAttrs & xmlAttrs, const Pos & pos)
|
||||
{
|
||||
if (auto path = std::get_if<SourcePath>(&pos.origin))
|
||||
xmlAttrs["path"] = path->path.abs();
|
||||
if (auto path = std::get_if<CheckedSourcePath>(&pos.origin))
|
||||
xmlAttrs["path"] = path->to_string();
|
||||
xmlAttrs["line"] = fmt("%1%", pos.line);
|
||||
xmlAttrs["column"] = fmt("%1%", pos.column);
|
||||
}
|
||||
@@ -34,14 +34,14 @@ static void showAttrs(EvalState & state, bool strict, bool location,
|
||||
StringSet names;
|
||||
|
||||
for (auto & i : attrs)
|
||||
names.emplace(state.symbols[i.name]);
|
||||
names.emplace(state.ctx.symbols[i.name]);
|
||||
|
||||
for (auto & i : names) {
|
||||
Attr & a(*attrs.find(state.symbols.create(i)));
|
||||
Attr & a(*attrs.find(state.ctx.symbols.create(i)));
|
||||
|
||||
XMLAttrs xmlAttrs;
|
||||
xmlAttrs["name"] = i;
|
||||
if (location && a.pos) posToXML(state, xmlAttrs, state.positions[a.pos]);
|
||||
if (location && a.pos) posToXML(state, xmlAttrs, state.ctx.positions[a.pos]);
|
||||
|
||||
XMLOpenElement _(doc, "attr", xmlAttrs);
|
||||
printValueAsXML(state, strict, location,
|
||||
@@ -86,17 +86,17 @@ static void printValueAsXML(EvalState & state, bool strict, bool location,
|
||||
if (state.isDerivation(v)) {
|
||||
XMLAttrs xmlAttrs;
|
||||
|
||||
Bindings::iterator a = v.attrs->find(state.symbols.create("derivation"));
|
||||
Bindings::iterator a = v.attrs->find(state.ctx.symbols.create("derivation"));
|
||||
|
||||
Path drvPath;
|
||||
a = v.attrs->find(state.s.drvPath);
|
||||
a = v.attrs->find(state.ctx.s.drvPath);
|
||||
if (a != v.attrs->end()) {
|
||||
if (strict) state.forceValue(*a->value, a->pos);
|
||||
if (a->value->type() == nString)
|
||||
xmlAttrs["drvPath"] = drvPath = a->value->string.s;
|
||||
}
|
||||
|
||||
a = v.attrs->find(state.s.outPath);
|
||||
a = v.attrs->find(state.ctx.s.outPath);
|
||||
if (a != v.attrs->end()) {
|
||||
if (strict) state.forceValue(*a->value, a->pos);
|
||||
if (a->value->type() == nString)
|
||||
@@ -132,18 +132,18 @@ static void printValueAsXML(EvalState & state, bool strict, bool location,
|
||||
break;
|
||||
}
|
||||
XMLAttrs xmlAttrs;
|
||||
if (location) posToXML(state, xmlAttrs, state.positions[v.lambda.fun->pos]);
|
||||
if (location) posToXML(state, xmlAttrs, state.ctx.positions[v.lambda.fun->pos]);
|
||||
XMLOpenElement _(doc, "function", xmlAttrs);
|
||||
|
||||
if (v.lambda.fun->hasFormals()) {
|
||||
XMLAttrs attrs;
|
||||
if (v.lambda.fun->arg) attrs["name"] = state.symbols[v.lambda.fun->arg];
|
||||
if (v.lambda.fun->arg) attrs["name"] = state.ctx.symbols[v.lambda.fun->arg];
|
||||
if (v.lambda.fun->formals->ellipsis) attrs["ellipsis"] = "1";
|
||||
XMLOpenElement _(doc, "attrspat", attrs);
|
||||
for (const Formal & i : v.lambda.fun->formals->lexicographicOrder(state.symbols))
|
||||
doc.writeEmptyElement("attr", singletonAttrs("name", state.symbols[i.name]));
|
||||
for (const Formal & i : v.lambda.fun->formals->lexicographicOrder(state.ctx.symbols))
|
||||
doc.writeEmptyElement("attr", singletonAttrs("name", state.ctx.symbols[i.name]));
|
||||
} else
|
||||
doc.writeEmptyElement("varpat", singletonAttrs("name", state.symbols[v.lambda.fun->arg]));
|
||||
doc.writeEmptyElement("varpat", singletonAttrs("name", state.ctx.symbols[v.lambda.fun->arg]));
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -55,8 +55,8 @@ bool Value::isTrivial() const
|
||||
internalType != tApp
|
||||
&& internalType != tPrimOpApp
|
||||
&& (internalType != tThunk
|
||||
|| (dynamic_cast<ExprAttrs *>(thunk.expr)
|
||||
&& static_cast<ExprAttrs *>(thunk.expr)->dynamicAttrs.empty())
|
||||
|| (dynamic_cast<ExprSet *>(thunk.expr)
|
||||
&& static_cast<ExprSet *>(thunk.expr)->dynamicAttrs.empty())
|
||||
|| dynamic_cast<ExprLambda *>(thunk.expr)
|
||||
|| dynamic_cast<ExprList *>(thunk.expr));
|
||||
}
|
||||
@@ -100,7 +100,7 @@ void Value::mkStringMove(const char * s, const NixStringContext & context)
|
||||
|
||||
void Value::mkPath(const SourcePath & path)
|
||||
{
|
||||
mkPath(gcCopyStringIfNeeded(path.path.abs()));
|
||||
*this = Value(NewValueAs::path, path);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -347,7 +347,7 @@ public:
|
||||
/// dynamic (GC) allocation to do so.
|
||||
Value(path_t, SourcePath const & path)
|
||||
: internalType(tPath)
|
||||
, _path(gcCopyStringIfNeeded(path.path.abs()))
|
||||
, _path(gcCopyStringIfNeeded(path.canonical().abs()))
|
||||
, _path_pad(0)
|
||||
{ }
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@ namespace nix {
|
||||
|
||||
StorePath fetchToStore(
|
||||
Store & store,
|
||||
const SourcePath & path,
|
||||
const CheckedSourcePath & path,
|
||||
std::string_view name,
|
||||
FileIngestionMethod method,
|
||||
PathFilter * filter,
|
||||
@@ -18,8 +18,8 @@ StorePath fetchToStore(
|
||||
|
||||
return
|
||||
settings.readOnlyMode
|
||||
? store.computeStorePathForPath(name, path.path.abs(), method, HashType::SHA256, filter2).first
|
||||
: store.addToStore(name, path.path.abs(), method, HashType::SHA256, filter2, repair);
|
||||
? store.computeStorePathForPath(name, path.canonical().abs(), method, HashType::SHA256, filter2).first
|
||||
: store.addToStore(name, path.canonical().abs(), method, HashType::SHA256, filter2, repair);
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -13,7 +13,7 @@ namespace nix {
|
||||
*/
|
||||
StorePath fetchToStore(
|
||||
Store & store,
|
||||
const SourcePath & path,
|
||||
const CheckedSourcePath & path,
|
||||
std::string_view name = "source",
|
||||
FileIngestionMethod method = FileIngestionMethod::Recursive,
|
||||
PathFilter * filter = nullptr,
|
||||
|
||||
@@ -263,9 +263,7 @@ std::optional<Hash> Input::getNarHash() const
|
||||
|
||||
std::optional<std::string> Input::getRef() const
|
||||
{
|
||||
if (auto s = maybeGetStrAttr(attrs, "ref"))
|
||||
return *s;
|
||||
return {};
|
||||
return maybeGetStrAttr(attrs, "ref");
|
||||
}
|
||||
|
||||
std::optional<Hash> Input::getRev() const
|
||||
@@ -286,16 +284,12 @@ std::optional<Hash> Input::getRev() const
|
||||
|
||||
std::optional<uint64_t> Input::getRevCount() const
|
||||
{
|
||||
if (auto n = maybeGetIntAttr(attrs, "revCount"))
|
||||
return *n;
|
||||
return {};
|
||||
return maybeGetIntAttr(attrs, "revCount");
|
||||
}
|
||||
|
||||
std::optional<time_t> Input::getLastModified() const
|
||||
{
|
||||
if (auto n = maybeGetIntAttr(attrs, "lastModified"))
|
||||
return *n;
|
||||
return {};
|
||||
return maybeGetIntAttr(attrs, "lastModified");
|
||||
}
|
||||
|
||||
ParsedURL InputScheme::toURL(const Input & input) const
|
||||
|
||||
@@ -20,6 +20,7 @@
|
||||
#include <string.h>
|
||||
#include <sys/time.h>
|
||||
#include <sys/wait.h>
|
||||
#include <unistd.h>
|
||||
|
||||
using namespace std::string_literals;
|
||||
|
||||
@@ -132,7 +133,7 @@ std::optional<std::string> readHeadCached(const std::string & actualUrl)
|
||||
// This function must behave the same way, so we return the expired
|
||||
// cached ref here.
|
||||
warn("could not get HEAD ref for repository '%s'; using expired cached ref '%s'", actualUrl, *cachedRef);
|
||||
return *cachedRef;
|
||||
return cachedRef;
|
||||
}
|
||||
|
||||
return std::nullopt;
|
||||
@@ -166,7 +167,7 @@ WorkdirInfo getWorkdirInfo(const Input & input, const Path & workdir)
|
||||
.program = "git",
|
||||
.args = { "-C", workdir, "--git-dir", gitDir, "rev-parse", "--verify", "--no-revs", "HEAD^{commit}" },
|
||||
.environment = env,
|
||||
.mergeStderrToStdout = true
|
||||
.redirections = {{.from = STDERR_FILENO, .to = STDOUT_FILENO}},
|
||||
});
|
||||
auto exitCode = WEXITSTATUS(result.first);
|
||||
auto errorMessage = result.second;
|
||||
@@ -694,7 +695,7 @@ struct GitInputScheme : InputScheme
|
||||
auto result = runProgram(RunOptions {
|
||||
.program = "git",
|
||||
.args = { "-C", repoDir, "--git-dir", gitDir, "cat-file", "commit", input.getRev()->gitRev() },
|
||||
.mergeStderrToStdout = true
|
||||
.redirections = {{.from = STDERR_FILENO, .to = STDOUT_FILENO}},
|
||||
});
|
||||
if (WEXITSTATUS(result.first) == 128
|
||||
&& result.second.find("bad file") != std::string::npos)
|
||||
@@ -759,7 +760,7 @@ struct GitInputScheme : InputScheme
|
||||
.args = { "-C", repoDir, "--git-dir", gitDir, "archive", input.getRev()->gitRev() },
|
||||
.captureStdout = true,
|
||||
});
|
||||
Finally const _wait([&] { proc.wait(); });
|
||||
Finally const _wait([&] { proc.waitAndCheck(); });
|
||||
|
||||
unpackTarfile(*proc.getStdout(), tmpDir);
|
||||
}
|
||||
|
||||
@@ -7,4 +7,3 @@ Description: Lix Package Manager (libfetchers)
|
||||
Version: @PACKAGE_VERSION@
|
||||
Requires: lix-base lix-util
|
||||
Libs: -L${libdir} -llixfetchers
|
||||
Cflags: -I${includedir}/lix/libfetchers
|
||||
|
||||
@@ -7,4 +7,3 @@ Description: Lix Package Manager (libmain)
|
||||
Version: @PACKAGE_VERSION@
|
||||
Requires: lix-base lix-util
|
||||
Libs: -L${libdir} -llixmain
|
||||
Cflags: -I${includedir}/lix/libmain
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user