forked from lix-project/lix
Compare commits
52 commits
2cc6abbaeb
...
fe13a447f0
Author | SHA1 | Date | |
---|---|---|---|
kloenk | fe13a447f0 | ||
alois31 | 6c726aa23b | ||
alois31 | 49399e481a | ||
alois31 | fed34594d8 | ||
jade | 85f282ef57 | ||
Qyriad | b338435b75 | ||
jade | c897fba787 | ||
jade | f2fff1faa4 | ||
jade | e0a3a5f226 | ||
jade | 1eef1927b6 | ||
Qyriad | 54d2c189ae | ||
eldritch horrors | a960576f58 | ||
eldritch horrors | 0b9a72524a | ||
jade | b9b1bbd22f | ||
jade | 8e6661cce7 | ||
jade | 9185ab7bf0 | ||
jade | 02ca60809d | ||
jade | 3626738b9b | ||
alois31 | aa00a5a8c9 | ||
jade | ce2b48aa41 | ||
eldritch horrors | bcb774688f | ||
eldritch horrors | ad5366c2ad | ||
eldritch horrors | b8f49a8eaf | ||
eldritch horrors | dad8bc679e | ||
eldritch horrors | 9592a9fd57 | ||
Qyriad | 19a93dd025 | ||
Qyriad | 010d93393e | ||
jade | c1f2733dd6 | ||
jade | c22a7f50cb | ||
jade | 985ce8a865 | ||
jade | 7b1d38bc4f | ||
jade | 24255748b4 | ||
jade | a17282fc66 | ||
jade | 6aead00a01 | ||
jade | 6c541e0bef | ||
Qyriad | 0ba37dc00d | ||
jade | 4e02951335 | ||
Artemis Tosini | ce2070139c | ||
jade | 4004d12483 | ||
julia | dd70044cde | ||
jade | b4035ed1d1 | ||
Robert Hensing | b588a761fe | ||
julia | 89c782b0c0 | ||
julia | 6c311a4afa | ||
julia | 0fa289f559 | ||
Qyriad | ff99f4a882 | ||
Artemis Tosini | f70b4258cd | ||
Artemis Tosini | e680b0913a | ||
jade | 4734ce7831 | ||
jade | 79404f7ffc | ||
jade | f95a47e8c4 | ||
194b6cc611 |
|
@ -60,6 +60,10 @@ jade:
|
||||||
forgejo: jade
|
forgejo: jade
|
||||||
github: lf-
|
github: lf-
|
||||||
|
|
||||||
|
kloenk:
|
||||||
|
forgejo: kloenk
|
||||||
|
github: kloenk
|
||||||
|
|
||||||
lovesegfault:
|
lovesegfault:
|
||||||
github: lovesegfault
|
github: lovesegfault
|
||||||
|
|
||||||
|
|
|
@ -110,6 +110,7 @@ manual = custom_target(
|
||||||
builtins_md,
|
builtins_md,
|
||||||
builtin_constants_md,
|
builtin_constants_md,
|
||||||
rl_next_generated,
|
rl_next_generated,
|
||||||
|
nix,
|
||||||
],
|
],
|
||||||
output : [
|
output : [
|
||||||
'manual',
|
'manual',
|
||||||
|
@ -186,6 +187,7 @@ foreach command : nix_nested_manpages
|
||||||
],
|
],
|
||||||
input : [
|
input : [
|
||||||
manual_md,
|
manual_md,
|
||||||
|
nix,
|
||||||
],
|
],
|
||||||
output : command[0] + '-' + page + '.1',
|
output : command[0] + '-' + page + '.1',
|
||||||
install : true,
|
install : true,
|
||||||
|
@ -298,6 +300,7 @@ foreach page : nix3_manpages
|
||||||
input : [
|
input : [
|
||||||
'render-manpage.sh',
|
'render-manpage.sh',
|
||||||
manual_md,
|
manual_md,
|
||||||
|
nix,
|
||||||
],
|
],
|
||||||
output : page + '.1',
|
output : page + '.1',
|
||||||
install : true,
|
install : true,
|
||||||
|
@ -341,6 +344,7 @@ foreach entry : nix_manpages
|
||||||
'render-manpage.sh',
|
'render-manpage.sh',
|
||||||
manual_md,
|
manual_md,
|
||||||
entry.get(3, []),
|
entry.get(3, []),
|
||||||
|
nix,
|
||||||
],
|
],
|
||||||
output : '@0@.@1@'.format(entry[0], entry[1]),
|
output : '@0@.@1@'.format(entry[0], entry[1]),
|
||||||
install : true,
|
install : true,
|
||||||
|
|
0
doc/manual/rl-next/.gitkeep
Normal file
0
doc/manual/rl-next/.gitkeep
Normal file
|
@ -1,14 +0,0 @@
|
||||||
---
|
|
||||||
synopsis: "Add a builtin `addDrvOutputDependencies`"
|
|
||||||
prs: 9216
|
|
||||||
issues: 7910
|
|
||||||
credits: [ericson, horrors]
|
|
||||||
category: Features
|
|
||||||
---
|
|
||||||
|
|
||||||
This builtin allows taking a `drvPath`-like string and turning it into a string
|
|
||||||
with context such that, when it lands in a derivation, it will create
|
|
||||||
dependencies on *all the outputs* in its closure (!). Although `drvPath` does this
|
|
||||||
today, this builtin starts forming a path to migrate to making `drvPath` have a
|
|
||||||
more normal and less surprising string context behaviour (see linked issue and
|
|
||||||
PR for more details).
|
|
|
@ -1,13 +0,0 @@
|
||||||
---
|
|
||||||
synopsis: "Add an option `always-allow-substitutes` to ignore `allowSubstitutes` in derivations"
|
|
||||||
prs: 8047
|
|
||||||
credits: [lovesegfault, horrors]
|
|
||||||
category: Improvements
|
|
||||||
---
|
|
||||||
|
|
||||||
You can set this setting to force a system to always allow substituting even
|
|
||||||
trivial derivations like `pkgs.writeText`. This is useful for
|
|
||||||
[`nix-fast-build --skip-cached`][skip-cached] and similar to be able to also
|
|
||||||
ignore trivial derivations.
|
|
||||||
|
|
||||||
[skip-cached]: https://github.com/Mic92/nix-fast-build?tab=readme-ov-file#avoiding-redundant-package-downloads
|
|
|
@ -1,42 +0,0 @@
|
||||||
---
|
|
||||||
synopsis: Concise error printing in `nix repl`
|
|
||||||
prs: 9928
|
|
||||||
cls: 811
|
|
||||||
category: Improvements
|
|
||||||
credits: 9999years
|
|
||||||
---
|
|
||||||
|
|
||||||
Previously, if an element of a list or attribute set threw an error while
|
|
||||||
evaluating, `nix repl` would print the entire error (including source location
|
|
||||||
information) inline. This output was clumsy and difficult to parse:
|
|
||||||
|
|
||||||
```
|
|
||||||
nix-repl> { err = builtins.throw "uh oh!"; }
|
|
||||||
{ err = «error:
|
|
||||||
… while calling the 'throw' builtin
|
|
||||||
at «string»:1:9:
|
|
||||||
1| { err = builtins.throw "uh oh!"; }
|
|
||||||
| ^
|
|
||||||
|
|
||||||
error: uh oh!»; }
|
|
||||||
```
|
|
||||||
|
|
||||||
Now, only the error message is displayed, making the output much more readable.
|
|
||||||
```
|
|
||||||
nix-repl> { err = builtins.throw "uh oh!"; }
|
|
||||||
{ err = «error: uh oh!»; }
|
|
||||||
```
|
|
||||||
|
|
||||||
However, if the whole expression being evaluated throws an error, source
|
|
||||||
locations and (if applicable) a stack trace are printed, just like you'd expect:
|
|
||||||
|
|
||||||
```
|
|
||||||
nix-repl> builtins.throw "uh oh!"
|
|
||||||
error:
|
|
||||||
… while calling the 'throw' builtin
|
|
||||||
at «string»:1:1:
|
|
||||||
1| builtins.throw "uh oh!"
|
|
||||||
| ^
|
|
||||||
|
|
||||||
error: uh oh!
|
|
||||||
```
|
|
|
@ -1,22 +0,0 @@
|
||||||
---
|
|
||||||
synopsis: Clang build timing analysis
|
|
||||||
cls: 587
|
|
||||||
category: Development
|
|
||||||
---
|
|
||||||
|
|
||||||
We now have Clang build profiling available, which generates Chrome
|
|
||||||
tracing files for each compilation unit. To enable it, run `meson configure
|
|
||||||
build -Dprofile-build=enabled` in a Clang stdenv (`nix develop
|
|
||||||
.#native-clangStdenvPackages`) then rerun the compilation.
|
|
||||||
|
|
||||||
If you want to make the build go faster, do a clang build with meson, then run
|
|
||||||
`maintainers/buildtime_report.sh build`, then contemplate how to improve the
|
|
||||||
build time.
|
|
||||||
|
|
||||||
You can also look at individual object files' traces in
|
|
||||||
<https://ui.perfetto.dev>.
|
|
||||||
|
|
||||||
See [the wiki page][improving-build-times-wiki] for more details on how to do
|
|
||||||
this.
|
|
||||||
|
|
||||||
[improving-build-times-wiki]: https://wiki.lix.systems/link/8#bkmrk-page-title
|
|
|
@ -1,9 +0,0 @@
|
||||||
---
|
|
||||||
synopsis: Show all FOD errors with `nix build --keep-going`
|
|
||||||
credits: [ma27]
|
|
||||||
category: Improvements
|
|
||||||
cls: [1108]
|
|
||||||
---
|
|
||||||
|
|
||||||
`nix build --keep-going` now behaves consistently with `nix-build --keep-going`. This means
|
|
||||||
that if e.g. multiple FODs fail to build, all hash mismatches are displayed.
|
|
|
@ -1,21 +0,0 @@
|
||||||
---
|
|
||||||
synopsis: "Fix CVE-2024-27297 (GHSA-2ffj-w4mj-pg37)"
|
|
||||||
cls: 266
|
|
||||||
credits: [puck, jade, thufschmitt, tomberek, valentin]
|
|
||||||
category: Fixes
|
|
||||||
---
|
|
||||||
|
|
||||||
Since Lix fixed-output derivations run in the host network namespace (which we
|
|
||||||
wish to change in the future, see
|
|
||||||
[lix#285](https://git.lix.systems/lix-project/lix/issues/285)), they may open
|
|
||||||
abstract-namespace Unix sockets to each other and to programs on the host. Lix
|
|
||||||
contained a now-fixed time-of-check/time-of-use vulnerability where one
|
|
||||||
derivation could send writable handles to files in their final location in the
|
|
||||||
store to another over an abstract-namespace Unix socket, exit, then the other
|
|
||||||
derivation could wait for Lix to hash the paths and overwrite them.
|
|
||||||
|
|
||||||
The impact of this vulnerability is that two malicious fixed-output derivations
|
|
||||||
could create a poisoned path for the sources to Bash or similarly important
|
|
||||||
software containing a backdoor, leading to local privilege execution.
|
|
||||||
|
|
||||||
CppNix advisory: https://github.com/NixOS/nix/security/advisories/GHSA-2ffj-w4mj-pg37
|
|
|
@ -1,11 +0,0 @@
|
||||||
---
|
|
||||||
synopsis: "`--debugger` can now access bindings from `let` expressions"
|
|
||||||
prs: 9918
|
|
||||||
issues: 8827
|
|
||||||
category: Fixes
|
|
||||||
credits: 9999years
|
|
||||||
---
|
|
||||||
|
|
||||||
Breakpoints and errors in the bindings of a `let` expression can now access
|
|
||||||
those bindings in the debugger. Previously, only the body of `let` expressions
|
|
||||||
could access those bindings.
|
|
|
@ -1,11 +0,0 @@
|
||||||
---
|
|
||||||
synopsis: Enter the `--debugger` when `builtins.trace` is called if `debugger-on-trace` is set
|
|
||||||
prs: 9914
|
|
||||||
category: Features
|
|
||||||
credits: 9999years
|
|
||||||
---
|
|
||||||
|
|
||||||
If the `debugger-on-trace` option is set and `--debugger` is given,
|
|
||||||
`builtins.trace` calls will behave similarly to `builtins.break` and will enter
|
|
||||||
the debug REPL. This is useful for determining where warnings are being emitted
|
|
||||||
from.
|
|
|
@ -1,16 +0,0 @@
|
||||||
---
|
|
||||||
synopsis: "Deprecate the online flake registries and vendor the default registry"
|
|
||||||
cls: 1127
|
|
||||||
credits: midnightveil
|
|
||||||
issues: [fj#183, fj#110, fj#116, 8953, 9087]
|
|
||||||
category: Breaking Changes
|
|
||||||
---
|
|
||||||
|
|
||||||
The online flake registry [https://channels.nixos.org/flake-registry.json](https://channels.nixos.org/flake-registry.json) is not pinned in any way,
|
|
||||||
and the targets of the indirections can both update or change entirely at any
|
|
||||||
point. Furthermore, it is refetched on every use of a flake reference, even if
|
|
||||||
there is a local flake reference, and even if you are offline (which breaks).
|
|
||||||
|
|
||||||
For now, we deprecate the (any) online flake registry, and vendor a copy of the
|
|
||||||
current online flake registry. This makes it work offline, and ensures that
|
|
||||||
it won't change in the future.
|
|
|
@ -1,9 +0,0 @@
|
||||||
---
|
|
||||||
synopsis: Stop vendoring toml11
|
|
||||||
cls: 675
|
|
||||||
category: Packaging
|
|
||||||
credits: winter
|
|
||||||
---
|
|
||||||
|
|
||||||
We don't apply any patches to it, and vendoring it locks users into
|
|
||||||
bugs (it hasn't been updated since its introduction in late 2021).
|
|
|
@ -1,8 +0,0 @@
|
||||||
---
|
|
||||||
synopsis: Fix handling of truncated `.drv` files.
|
|
||||||
prs: 9673
|
|
||||||
category: Fixes
|
|
||||||
credits: horrors
|
|
||||||
---
|
|
||||||
|
|
||||||
Previously a `.drv` that was truncated in the middle of a string would case nix to enter an infinite loop, eventually exhausting all memory and crashing.
|
|
|
@ -1,24 +0,0 @@
|
||||||
---
|
|
||||||
synopsis: Duplicate attribute reports are more accurate
|
|
||||||
cls: 557
|
|
||||||
credits: horrors
|
|
||||||
category: Improvements
|
|
||||||
---
|
|
||||||
|
|
||||||
Duplicate attribute errors are now more accurate, showing the path at which an error was detected rather than the full, possibly longer, path that caused the error.
|
|
||||||
Error reports are now
|
|
||||||
```ShellSession
|
|
||||||
$ nix eval --expr '{ a.b = 1; a.b.c.d = 1; }'
|
|
||||||
error: attribute 'a.b' already defined at «string»:1:3
|
|
||||||
at «string»:1:12:
|
|
||||||
1| { a.b = 1; a.b.c.d = 1;
|
|
||||||
| ^
|
|
||||||
```
|
|
||||||
instead of
|
|
||||||
```ShellSession
|
|
||||||
$ nix eval --expr '{ a.b = 1; a.b.c.d = 1; }'
|
|
||||||
error: attribute 'a.b.c.d' already defined at «string»:1:3
|
|
||||||
at «string»:1:12:
|
|
||||||
1| { a.b = 1; a.b.c.d = 1;
|
|
||||||
| ^
|
|
||||||
```
|
|
|
@ -1,8 +0,0 @@
|
||||||
---
|
|
||||||
synopsis: Disallow empty search regex in `nix search`
|
|
||||||
prs: 9481
|
|
||||||
credits: [iFreilicht, horrors]
|
|
||||||
category: Miscellany
|
|
||||||
---
|
|
||||||
|
|
||||||
[`nix search`](@docroot@/command-ref/new-cli/nix3-search.md) now requires a search regex to be passed. To show all packages, use `^`.
|
|
|
@ -1,13 +0,0 @@
|
||||||
---
|
|
||||||
synopsis: "Add an option `enable-core-dumps` that enables core dumps from builds"
|
|
||||||
cls: 1088
|
|
||||||
credits: midnightveil
|
|
||||||
category: Features
|
|
||||||
---
|
|
||||||
|
|
||||||
In the past, Lix disabled core dumps by setting the soft `RLIMIT_CORE` to 0
|
|
||||||
unconditionally. Although this rlimit could be altered from the builder since
|
|
||||||
it is just the soft limit, this was kind of annoying to do. By passing
|
|
||||||
`--option enable-core-dumps true` to an offending build, one can now cause the
|
|
||||||
core dumps to be handled by the system in the normal way (winding up in
|
|
||||||
`coredumpctl`, say, on Linux).
|
|
|
@ -1,27 +0,0 @@
|
||||||
---
|
|
||||||
synopsis: The `--debugger` will start more reliably in `let` expressions and function calls
|
|
||||||
prs: 9917
|
|
||||||
issues: 6649
|
|
||||||
credits: [9999years, horrors]
|
|
||||||
category: Fixes
|
|
||||||
---
|
|
||||||
|
|
||||||
Previously, if you attempted to evaluate this file with the debugger:
|
|
||||||
|
|
||||||
```nix
|
|
||||||
let
|
|
||||||
a = builtins.trace "before inner break" (
|
|
||||||
builtins.break "hello"
|
|
||||||
);
|
|
||||||
b = builtins.trace "before outer break" (
|
|
||||||
builtins.break a
|
|
||||||
);
|
|
||||||
in
|
|
||||||
b
|
|
||||||
```
|
|
||||||
|
|
||||||
Lix would correctly enter the debugger at `builtins.break a`, but if you asked
|
|
||||||
it to `:continue`, it would skip over the `builtins.break "hello"` expression
|
|
||||||
entirely.
|
|
||||||
|
|
||||||
Now, Lix will correctly enter the debugger at both breakpoints.
|
|
|
@ -1,10 +0,0 @@
|
||||||
---
|
|
||||||
synopsis: Reduce eval memory usage and wall time
|
|
||||||
prs: 9658
|
|
||||||
cls: 207
|
|
||||||
credits: horrors
|
|
||||||
category: Improvements
|
|
||||||
---
|
|
||||||
|
|
||||||
Reduce the size of the `Env` struct used in the evaluator by a pointer, or 8 bytes on most modern machines.
|
|
||||||
This reduces memory usage during eval by around 2% and wall time by around 3%.
|
|
|
@ -1,14 +0,0 @@
|
||||||
---
|
|
||||||
synopsis: Add new `eval-system` setting
|
|
||||||
prs: 4093
|
|
||||||
credits: [matthewbauer, horrors]
|
|
||||||
category: Features
|
|
||||||
---
|
|
||||||
|
|
||||||
Add a new `eval-system` option.
|
|
||||||
Unlike `system`, it just overrides the value of `builtins.currentSystem`.
|
|
||||||
This is more useful than overriding `system`, because you can build these derivations on remote builders which can work on the given system.
|
|
||||||
In contrast, `system` also effects scheduling which will cause Lix to build those derivations locally even if that doesn't make sense.
|
|
||||||
|
|
||||||
`eval-system` only takes effect if it is non-empty.
|
|
||||||
If empty (the default) `system` is used as before, so there is no breakage.
|
|
|
@ -1,10 +0,0 @@
|
||||||
---
|
|
||||||
synopsis: Creating setuid/setgid binaries with fchmodat2 is now prohibited by the build sandbox
|
|
||||||
prs: 10501
|
|
||||||
credits: ma27
|
|
||||||
category: Fixes
|
|
||||||
---
|
|
||||||
|
|
||||||
The build sandbox blocks any attempt to create setuid/setgid binaries, but didn't check
|
|
||||||
for the use of the `fchmodat2` syscall which was introduced in Linux 6.6 and is used by
|
|
||||||
glibc >=2.39. This is fixed now.
|
|
|
@ -1,24 +0,0 @@
|
||||||
---
|
|
||||||
synopsis: Fix nested flake input `follows`
|
|
||||||
prs: 6621
|
|
||||||
cls: 994
|
|
||||||
credits: [Kha, ma27]
|
|
||||||
category: Fixes
|
|
||||||
significance: significant
|
|
||||||
---
|
|
||||||
|
|
||||||
Previously nested-input overrides were ignored; that is, the following did not
|
|
||||||
override anything, in spite of the `nix3-flake` manual documenting it working:
|
|
||||||
|
|
||||||
```
|
|
||||||
{
|
|
||||||
inputs = {
|
|
||||||
foo.url = "github:bar/foo";
|
|
||||||
foo.inputs.bar.inputs.nixpkgs = "nixpkgs";
|
|
||||||
};
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
This is useful to avoid the 1000 instances of nixpkgs problem without having
|
|
||||||
each flake in the dependency tree to expose all of its transitive dependencies
|
|
||||||
for modification.
|
|
|
@ -1,30 +0,0 @@
|
||||||
---
|
|
||||||
synopsis: Warn on unknown settings anywhere in the command line
|
|
||||||
prs: 10701
|
|
||||||
credits: [cole-h]
|
|
||||||
category: Improvements
|
|
||||||
---
|
|
||||||
|
|
||||||
All `nix` commands will now properly warn when an unknown option is specified anywhere in the command line.
|
|
||||||
|
|
||||||
Before:
|
|
||||||
|
|
||||||
```console
|
|
||||||
$ nix-instantiate --option foobar baz --expr '{}'
|
|
||||||
warning: unknown setting 'foobar'
|
|
||||||
$ nix-instantiate '{}' --option foobar baz --expr
|
|
||||||
$ nix eval --expr '{}' --option foobar baz
|
|
||||||
{ }
|
|
||||||
```
|
|
||||||
|
|
||||||
After:
|
|
||||||
|
|
||||||
```console
|
|
||||||
$ nix-instantiate --option foobar baz --expr '{}'
|
|
||||||
warning: unknown setting 'foobar'
|
|
||||||
$ nix-instantiate '{}' --option foobar baz --expr
|
|
||||||
warning: unknown setting 'foobar'
|
|
||||||
$ nix eval --expr '{}' --option foobar baz
|
|
||||||
warning: unknown setting 'foobar'
|
|
||||||
{ }
|
|
||||||
```
|
|
|
@ -1,34 +0,0 @@
|
||||||
---
|
|
||||||
synopsis: Nested debuggers are no longer supported
|
|
||||||
prs: 9920
|
|
||||||
credits: 9999years
|
|
||||||
category: Improvements
|
|
||||||
---
|
|
||||||
|
|
||||||
Previously, evaluating an expression that throws an error in the debugger would
|
|
||||||
enter a second, nested debugger:
|
|
||||||
|
|
||||||
```
|
|
||||||
nix-repl> builtins.throw "what"
|
|
||||||
error: what
|
|
||||||
|
|
||||||
|
|
||||||
Starting REPL to allow you to inspect the current state of the evaluator.
|
|
||||||
|
|
||||||
Welcome to Nix 2.18.1. Type :? for help.
|
|
||||||
|
|
||||||
nix-repl>
|
|
||||||
```
|
|
||||||
|
|
||||||
Now, it just prints the error message like `nix repl`:
|
|
||||||
|
|
||||||
```
|
|
||||||
nix-repl> builtins.throw "what"
|
|
||||||
error:
|
|
||||||
… while calling the 'throw' builtin
|
|
||||||
at «string»:1:1:
|
|
||||||
1| builtins.throw "what"
|
|
||||||
| ^
|
|
||||||
|
|
||||||
error: what
|
|
||||||
```
|
|
|
@ -1,9 +0,0 @@
|
||||||
---
|
|
||||||
synopsis: consistent order of lambda formals in printed expressions
|
|
||||||
prs: 9874
|
|
||||||
credits: horrors
|
|
||||||
category: Fixes
|
|
||||||
---
|
|
||||||
|
|
||||||
Always print lambda formals in lexicographic order rather than the internal, creation-time based symbol order.
|
|
||||||
This makes printed formals independent of the context they appear in.
|
|
|
@ -1,8 +0,0 @@
|
||||||
---
|
|
||||||
synopsis: Find GC roots using libproc on Darwin
|
|
||||||
cls: 723
|
|
||||||
credits: artemist
|
|
||||||
category: Improvements
|
|
||||||
---
|
|
||||||
|
|
||||||
Previously, the garbage collector found runtime roots on Darwin by shelling out to `lsof -n -w -F n` then parsing the result. The version of `lsof` packaged in Nixpkgs is very slow on Darwin, so Lix now uses `libproc` directly to speed up GC root discovery, in some tests taking 250ms now instead of 40s.
|
|
|
@ -1,8 +0,0 @@
|
||||||
---
|
|
||||||
synopsis: fix duplicate attribute error positions for `inherit`
|
|
||||||
prs: 9874
|
|
||||||
credits: horrors
|
|
||||||
category: Fixes
|
|
||||||
---
|
|
||||||
|
|
||||||
When an inherit caused a duplicate attribute error, the position of the error was not reported correctly, placing the error with the inherit itself or at the start of the bindings block instead of the offending attribute name.
|
|
|
@ -1,9 +0,0 @@
|
||||||
---
|
|
||||||
synopsis: "`inherit (x) ...` evaluates `x` only once"
|
|
||||||
prs: 9847
|
|
||||||
category: Fixes
|
|
||||||
credits: horrors
|
|
||||||
---
|
|
||||||
|
|
||||||
`inherit (x) a b ...` now evaluates the expression `x` only once for all inherited attributes rather than once for each inherited attribute.
|
|
||||||
This does not usually have a measurable impact, but side-effects (such as `builtins.trace`) would be duplicated and expensive expressions (such as derivations) could cause a measurable slowdown.
|
|
|
@ -1,12 +0,0 @@
|
||||||
---
|
|
||||||
synopsis: Store paths are allowed to start with `.`
|
|
||||||
issues: 912
|
|
||||||
prs: [9867, 9091, 9095, 9120, 9121, 9122, 9130, 9219, 9224]
|
|
||||||
credits: [roberth, horrors]
|
|
||||||
category: Fixes
|
|
||||||
---
|
|
||||||
|
|
||||||
Leading periods were allowed by accident in Nix 2.4. The Nix team has considered this to be a bug, but this behavior has since been relied on by users, leading to unnecessary difficulties.
|
|
||||||
From now on, leading periods are officially, definitively supported. The names `.` and `..` are disallowed, as well as those starting with `.-` or `..-`.
|
|
||||||
|
|
||||||
Nix versions that denied leading periods are documented [in the issue](https://github.com/NixOS/nix/issues/912#issuecomment-1919583286).
|
|
|
@ -1,12 +0,0 @@
|
||||||
---
|
|
||||||
synopsis: Enforce syscall filtering and no-new-privileges on Linux
|
|
||||||
cls: 1063
|
|
||||||
category: Breaking Changes
|
|
||||||
credits: alois31
|
|
||||||
---
|
|
||||||
|
|
||||||
In order to improve consistency of the build environment, system call filtering and no-new-privileges are now unconditionally enabled on Linux.
|
|
||||||
The `filter-syscalls` and `allow-new-privileges` options which could be used to disable these features under some circumstances have been removed.
|
|
||||||
|
|
||||||
In order to support building on architectures without libseccomp support, the option to disable syscall filtering at build time remains.
|
|
||||||
However, other uses of this option are heavily discouraged, since it would reduce the security of the sandbox substantially.
|
|
|
@ -1,9 +0,0 @@
|
||||||
---
|
|
||||||
synopsis: Increase default stack size on macOS
|
|
||||||
prs: 9860
|
|
||||||
credits: 9999years
|
|
||||||
category: Improvements
|
|
||||||
---
|
|
||||||
|
|
||||||
Increase the default stack size on macOS to the same value as on Linux, subject to system restrictions to maximum stack size.
|
|
||||||
This should reduce the number of stack overflow crashes on macOS when evaluating Nix code with deep call stacks.
|
|
|
@ -1,15 +0,0 @@
|
||||||
---
|
|
||||||
synopsis: Lix is built with meson
|
|
||||||
# and many more
|
|
||||||
cls: [580, 627, 628, 707, 711, 712, 719]
|
|
||||||
credits: [Qyriad, horrors, jade, 9999years, winter]
|
|
||||||
category: Packaging
|
|
||||||
---
|
|
||||||
|
|
||||||
Lix is built exclusively with the meson build system thanks to a huge team-wide
|
|
||||||
effort, and the legacy `make`/`autoconf` based build system has been removed
|
|
||||||
altogether. This improves maintainability of Lix, enables things like saving
|
|
||||||
20% of compile times with precompiled headers, and generally makes the build
|
|
||||||
less able to produce obscure incremental compilation bugs.
|
|
||||||
|
|
||||||
Non-Nix-based downstream packaging needs rewriting accordingly.
|
|
|
@ -1,9 +0,0 @@
|
||||||
---
|
|
||||||
synopsis: Show more log context for failed builds
|
|
||||||
prs: 9670
|
|
||||||
credits: DavHau
|
|
||||||
category: Improvements
|
|
||||||
---
|
|
||||||
|
|
||||||
Show 25 lines of log tail instead of 10 for failed builds.
|
|
||||||
This increases the chances of having useful information in the shown logs.
|
|
14
doc/manual/rl-next/multiline-log-format.md
Normal file
14
doc/manual/rl-next/multiline-log-format.md
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
---
|
||||||
|
synopsis: Add log formats `multiline` and `multiline-with-logs`
|
||||||
|
cls: [1369]
|
||||||
|
credits: [kloenk]
|
||||||
|
category: Improvements
|
||||||
|
---
|
||||||
|
|
||||||
|
Added two new log formats (`multiline` and `multiline-with-logs`) that display
|
||||||
|
current activities below each other for better visibility.
|
||||||
|
|
||||||
|
These formats attempt to use the maximum available lines
|
||||||
|
(defaulting to 25 if unable to determine) and print up to that many lines.
|
||||||
|
The status bar is displayed as the first line, with each subsequent
|
||||||
|
activity on its own line.
|
|
@ -1,26 +0,0 @@
|
||||||
---
|
|
||||||
synopsis: Lix turns more internal bugs into crashes
|
|
||||||
cls: [797, 626]
|
|
||||||
credits: jade
|
|
||||||
category: Packaging
|
|
||||||
significance: significant
|
|
||||||
---
|
|
||||||
|
|
||||||
Lix now enables build options such as trapping on signed overflow and enabling
|
|
||||||
libstdc++ assertions by default. These may find new bugs in Lix, which will
|
|
||||||
present themselves as Lix processes aborting, potentially without an error
|
|
||||||
message.
|
|
||||||
|
|
||||||
If Lix processes abort on your machine, this is a bug. Please file a bug,
|
|
||||||
ideally with the core dump (or information from it).
|
|
||||||
|
|
||||||
On Linux, run `coredumpctl list`, find the crashed process's PID at
|
|
||||||
the bottom of the list, then run `coredumpctl info THE-PID`. You can then paste
|
|
||||||
the output into a bug report.
|
|
||||||
|
|
||||||
On macOS, open the Console app from Applications/Utilities, select Crash
|
|
||||||
Reports, select the crash report in question. Right click on it, select Open In
|
|
||||||
Finder, then include that file in your bug report. [See the Apple
|
|
||||||
documentation][apple-crashreport] for more details.
|
|
||||||
|
|
||||||
[apple-crashreport]: https://developer.apple.com/documentation/xcode/acquiring-crash-reports-and-diagnostic-logs#Locate-crash-reports-and-memory-logs-on-the-device
|
|
|
@ -1,12 +0,0 @@
|
||||||
---
|
|
||||||
synopsis: rename 'nix show-config' to 'nix config show'
|
|
||||||
issues: 7672
|
|
||||||
prs: 9477
|
|
||||||
cls: 993
|
|
||||||
credits: [thufschmitt, ma27]
|
|
||||||
category: Improvements
|
|
||||||
---
|
|
||||||
|
|
||||||
`nix show-config` was renamed to `nix config show` to be more consistent with the rest of the command-line interface.
|
|
||||||
|
|
||||||
Running `nix show-config` will now print a deprecation warning saying to use `nix config show` instead.
|
|
|
@ -1,8 +0,0 @@
|
||||||
---
|
|
||||||
synopsis: Fix `nix-env --query --drv-path --json`
|
|
||||||
prs: 9257
|
|
||||||
credits: [Artturin, horrors]
|
|
||||||
category: Fixes
|
|
||||||
---
|
|
||||||
|
|
||||||
Fixed a bug where `nix-env --query` ignored `--drv-path` when `--json` was set.
|
|
|
@ -1,9 +0,0 @@
|
||||||
---
|
|
||||||
synopsis: Print derivation paths in `nix eval`
|
|
||||||
cls: 446
|
|
||||||
credits: 9999years
|
|
||||||
category: Improvements
|
|
||||||
---
|
|
||||||
|
|
||||||
`nix eval` previously printed derivations as attribute sets, so commands that print derivations (e.g. `nix eval nixpkgs#bash`) would infinitely loop and segfault.
|
|
||||||
It now prints the `.drv` path the derivation generates instead.
|
|
|
@ -1,37 +0,0 @@
|
||||||
---
|
|
||||||
synopsis: "`nix flake check` logs the checks"
|
|
||||||
issues: 8882
|
|
||||||
prs: 8893
|
|
||||||
cls: [259, 260, 261, 262]
|
|
||||||
credits: [9999years, raito, horrors]
|
|
||||||
category: Improvements
|
|
||||||
significance: significant
|
|
||||||
---
|
|
||||||
|
|
||||||
`nix flake check` now logs the checks it runs and the derivations it evaluates:
|
|
||||||
|
|
||||||
```
|
|
||||||
$ nix flake check -v
|
|
||||||
evaluating flake...
|
|
||||||
checking flake output 'checks'...
|
|
||||||
checking derivation 'checks.aarch64-darwin.ghciwatch-tests'...
|
|
||||||
derivation evaluated to /nix/store/nh7dlvsrhds4cxl91mvgj4h5cbq6skmq-ghciwatch-test-0.3.0.drv
|
|
||||||
checking derivation 'checks.aarch64-darwin.ghciwatch-clippy'...
|
|
||||||
derivation evaluated to /nix/store/9cb5a6wmp6kf6hidqw9wphidvb8bshym-ghciwatch-clippy-0.3.0.drv
|
|
||||||
checking derivation 'checks.aarch64-darwin.ghciwatch-doc'...
|
|
||||||
derivation evaluated to /nix/store/8brdd3jbawfszpbs7vdpsrhy80as1il8-ghciwatch-doc-0.3.0.drv
|
|
||||||
checking derivation 'checks.aarch64-darwin.ghciwatch-fmt'...
|
|
||||||
derivation evaluated to /nix/store/wjhs0l1njl5pyji53xlmfjrlya0wmz8p-ghciwatch-fmt-0.3.0.drv
|
|
||||||
checking derivation 'checks.aarch64-darwin.ghciwatch-audit'...
|
|
||||||
derivation evaluated to /nix/store/z0mps8dyj2ds7c0fn0819y5h5611033z-ghciwatch-audit-0.3.0.drv
|
|
||||||
checking flake output 'packages'...
|
|
||||||
checking derivation 'packages.aarch64-darwin.default'...
|
|
||||||
derivation evaluated to /nix/store/41abbdyglw5x9vcsvd89xan3ydjf8d7r-ghciwatch-0.3.0.drv
|
|
||||||
checking flake output 'apps'...
|
|
||||||
checking flake output 'devShells'...
|
|
||||||
checking derivation 'devShells.aarch64-darwin.default'...
|
|
||||||
derivation evaluated to /nix/store/bc935gz7dylzmcpdb5cczr8gngv8pmdb-nix-shell.drv
|
|
||||||
running 5 flake checks...
|
|
||||||
warning: The check omitted these incompatible systems: aarch64-linux, x86_64-darwin, x86_64-linux
|
|
||||||
Use '--all-systems' to check all.
|
|
||||||
```
|
|
|
@ -1,19 +0,0 @@
|
||||||
---
|
|
||||||
synopsis: "Overhaul `nix flake update` and `nix flake lock` UX"
|
|
||||||
prs: 8817
|
|
||||||
credits: [iFreilicht, Lunaphied, thufschmitt]
|
|
||||||
category: Breaking Changes
|
|
||||||
---
|
|
||||||
|
|
||||||
The interface for creating and updating lock files has been overhauled:
|
|
||||||
|
|
||||||
- [`nix flake lock`](@docroot@/command-ref/new-cli/nix3-flake-lock.md) only creates lock files and adds missing inputs now.
|
|
||||||
It will *never* update existing inputs.
|
|
||||||
|
|
||||||
- [`nix flake update`](@docroot@/command-ref/new-cli/nix3-flake-update.md) does the same, but *will* update inputs.
|
|
||||||
- Passing no arguments will update all inputs of the current flake, just like it already did.
|
|
||||||
- Passing input names as arguments will ensure only those are updated. This replaces the functionality of `nix flake lock --update-input`
|
|
||||||
- To operate on a flake outside the current directory, you must now pass `--flake path/to/flake`.
|
|
||||||
|
|
||||||
- The flake-specific flags `--recreate-lock-file` and `--update-input` have been removed from all commands operating on installables.
|
|
||||||
They are superceded by `nix flake update`.
|
|
|
@ -1,11 +0,0 @@
|
||||||
---
|
|
||||||
synopsis: "`nix profile` now allows referring to elements by human-readable name, and no longer accepts indices"
|
|
||||||
prs: 8678
|
|
||||||
cls: [978, 980]
|
|
||||||
category: Breaking Changes
|
|
||||||
credits: [iFreilicht, Qyriad, edolstra]
|
|
||||||
---
|
|
||||||
|
|
||||||
[`nix profile`](@docroot@/command-ref/new-cli/nix3-profile.md) now uses names to refer to installed packages when running [`list`](@docroot@/command-ref/new-cli/nix3-profile-list.md), [`remove`](@docroot@/command-ref/new-cli/nix3-profile-remove.md) or [`upgrade`](@docroot@/command-ref/new-cli/nix3-profile-upgrade.md) as opposed to indices. Indices have been removed. Profile element names are generated when a package is installed and remain the same until the package is removed.
|
|
||||||
|
|
||||||
**Warning**: The `manifest.nix` file used to record the contents of profiles has changed. Lix will automatically upgrade profiles to the new version when you modify the profile. After that, the profile can no longer be used by older versions of Lix.
|
|
|
@ -1,18 +0,0 @@
|
||||||
---
|
|
||||||
synopsis: "Add an option `--unpack` to unpack archives in `nix store prefetch-file`"
|
|
||||||
prs: 9805
|
|
||||||
cls: 224
|
|
||||||
credits: [yshui, horrors]
|
|
||||||
category: Improvements
|
|
||||||
---
|
|
||||||
|
|
||||||
It is now possible to fetch an archive then NAR-hash it (as in, hash it in the
|
|
||||||
same manner as `builtins.fetchTarball` or fixed-output derivations with
|
|
||||||
recursive hash type) in one command.
|
|
||||||
|
|
||||||
Example:
|
|
||||||
|
|
||||||
```
|
|
||||||
~ » nix store prefetch-file --name source --unpack https://git.lix.systems/lix-project/lix/archive/2.90-beta.1.tar.gz
|
|
||||||
Downloaded 'https://git.lix.systems/lix-project/lix/archive/2.90-beta.1.tar.gz' to '/nix/store/yvfqnq52ryjc3janw02ziv7kr6gd0cs1-source' (hash 'sha256-REWlo2RYHfJkxnmZTEJu3Cd/2VM+wjjpPy7Xi4BdDTQ=').
|
|
||||||
```
|
|
|
@ -1,17 +0,0 @@
|
||||||
---
|
|
||||||
synopsis: "`builtins.nixVersion` and `builtins.langVersion` return fixed values"
|
|
||||||
cls: [558, 1144]
|
|
||||||
credits: jade
|
|
||||||
category: Breaking Changes
|
|
||||||
---
|
|
||||||
|
|
||||||
`builtins.nixVersion` now returns a fixed value `"2.18.3-lix"`.
|
|
||||||
|
|
||||||
`builtins.langVersion` returns a fixed value `6`, matching CppNix 2.18.
|
|
||||||
|
|
||||||
This prevents feature detection assuming that features that exist in Nix
|
|
||||||
post-Lix-branch-off might exist, even though the Lix version is greater than
|
|
||||||
the Nix version.
|
|
||||||
|
|
||||||
In the future, check for builtins for feature detection. If a feature cannot be
|
|
||||||
detected by *those* means, please file a Lix bug.
|
|
|
@ -1,10 +0,0 @@
|
||||||
---
|
|
||||||
synopsis: re-evaluate cached evaluation errors
|
|
||||||
cls: 771
|
|
||||||
credits: Qyriad
|
|
||||||
category: Fixes
|
|
||||||
---
|
|
||||||
|
|
||||||
"cached failure of [expr]" errors have been removed: expressions already in the
|
|
||||||
eval cache as a failure will now simply be re-evaluated, removing the need to
|
|
||||||
set `--no-eval-cache` or similar to see the error.
|
|
|
@ -1,55 +0,0 @@
|
||||||
---
|
|
||||||
synopsis: "REPL printing improvements"
|
|
||||||
prs: [9931, 10208]
|
|
||||||
cls: [375, 492]
|
|
||||||
credits: [9999years, horrors]
|
|
||||||
category: Improvements
|
|
||||||
---
|
|
||||||
|
|
||||||
The REPL printer has been improved to do the following:
|
|
||||||
- If a string is passed to `:print`, it is printed literally to the screen
|
|
||||||
- Structures will be printed as multiple lines when necessary
|
|
||||||
|
|
||||||
Before:
|
|
||||||
|
|
||||||
```
|
|
||||||
nix-repl> { attrs = { a = { b = { c = { }; }; }; }; list = [ 1 ]; list' = [ 1 2 3 ]; }
|
|
||||||
{ attrs = { ... }; list = [ ... ]; list' = [ ... ]; }
|
|
||||||
|
|
||||||
nix-repl> :p { attrs = { a = { b = { c = { }; }; }; }; list = [ 1 ]; list' = [ 1 2 3 ]; }
|
|
||||||
{ attrs = { a = { b = { c = { }; }; }; }; list = [ 1 ]; list' = [ 1 2 3 ]; }
|
|
||||||
|
|
||||||
nix-repl> :p "meow"
|
|
||||||
"meow"
|
|
||||||
```
|
|
||||||
|
|
||||||
After:
|
|
||||||
|
|
||||||
```
|
|
||||||
nix-repl> { attrs = { a = { b = { c = { }; }; }; }; list = [ 1 ]; list' = [ 1 2 3 ]; }
|
|
||||||
{
|
|
||||||
attrs = { ... };
|
|
||||||
list = [ ... ];
|
|
||||||
list' = [ ... ];
|
|
||||||
}
|
|
||||||
|
|
||||||
nix-repl> :p { attrs = { a = { b = { c = { }; }; }; }; list = [ 1 ]; list' = [ 1 2 3 ]; }
|
|
||||||
{
|
|
||||||
attrs = {
|
|
||||||
a = {
|
|
||||||
b = {
|
|
||||||
c = { };
|
|
||||||
};
|
|
||||||
};
|
|
||||||
};
|
|
||||||
list = [ 1 ];
|
|
||||||
list' = [
|
|
||||||
1
|
|
||||||
2
|
|
||||||
3
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
nix-repl> :p "meow"
|
|
||||||
meow
|
|
||||||
```
|
|
|
@ -1,26 +0,0 @@
|
||||||
---
|
|
||||||
synopsis: Coercion errors include the failing value
|
|
||||||
issues: 561
|
|
||||||
prs: 9754
|
|
||||||
credits: [9999years, horrors]
|
|
||||||
category: Improvements
|
|
||||||
---
|
|
||||||
|
|
||||||
The `error: cannot coerce a <TYPE> to a string` message now includes the value
|
|
||||||
which caused the error.
|
|
||||||
|
|
||||||
Before:
|
|
||||||
|
|
||||||
```
|
|
||||||
error: cannot coerce a set to a string
|
|
||||||
```
|
|
||||||
|
|
||||||
After:
|
|
||||||
|
|
||||||
```
|
|
||||||
error: cannot coerce a set to a string: { aesSupport = «thunk»;
|
|
||||||
avx2Support = «thunk»; avx512Support = «thunk»; avxSupport = «thunk»;
|
|
||||||
canExecute = «thunk»; config = «thunk»; darwinArch = «thunk»; darwinMinVersion
|
|
||||||
= «thunk»; darwinMinVersionVariable = «thunk»; darwinPlatform = «thunk»; «84
|
|
||||||
attributes elided»}
|
|
||||||
```
|
|
|
@ -1,20 +0,0 @@
|
||||||
---
|
|
||||||
synopsis: New-cli flake commands that expect derivations now print the failing value and its type
|
|
||||||
credits: Qyriad
|
|
||||||
category: Improvements
|
|
||||||
cls: 1177
|
|
||||||
---
|
|
||||||
|
|
||||||
In errors like `flake output attribute 'legacyPackages.x86_64-linux.lib' is not a derivation or path`, the message now includes the failing value and type.
|
|
||||||
|
|
||||||
Before:
|
|
||||||
|
|
||||||
```
|
|
||||||
error: flake output attribute 'nixosConfigurations.yuki.config' is not a derivation or path
|
|
||||||
````
|
|
||||||
|
|
||||||
After:
|
|
||||||
|
|
||||||
```
|
|
||||||
error: expected flake output attribute 'nixosConfigurations.yuki.config' to be a derivation or path but found a set: { appstream = «thunk»; assertions = «thunk»; boot = { bcache = «thunk»; binfmt = «thunk»; binfmtMiscRegistrations = «thunk»; blacklistedKernelModules = «thunk»; bootMount = «thunk»; bootspec = «thunk»; cleanTmpDir = «thunk»; consoleLogLevel = «thunk»; «43 attributes elided» }; «48 attributes elided» }
|
|
||||||
```
|
|
|
@ -1,25 +0,0 @@
|
||||||
---
|
|
||||||
synopsis: Type errors include the failing value
|
|
||||||
issues: 561
|
|
||||||
prs: 9753
|
|
||||||
credits: [9999years, horrors]
|
|
||||||
category: Improvements
|
|
||||||
---
|
|
||||||
|
|
||||||
In errors like `value is an integer while a list was expected`, the message now
|
|
||||||
includes the failing value.
|
|
||||||
|
|
||||||
Before:
|
|
||||||
|
|
||||||
```
|
|
||||||
error: value is a set while a string was expected
|
|
||||||
```
|
|
||||||
|
|
||||||
After:
|
|
||||||
|
|
||||||
```
|
|
||||||
error: expected a string but found a set: { ghc810 = «thunk»;
|
|
||||||
ghc8102Binary = «thunk»; ghc8107 = «thunk»; ghc8107Binary = «thunk»;
|
|
||||||
ghc865Binary = «thunk»; ghc90 = «thunk»; ghc902 = «thunk»; ghc92 = «thunk»;
|
|
||||||
ghc924Binary = «thunk»; ghc925 = «thunk»; «17 attributes elided»}
|
|
||||||
```
|
|
|
@ -1,39 +0,0 @@
|
||||||
---
|
|
||||||
synopsis: "Visual clutter in `--debugger` is reduced"
|
|
||||||
prs: 9919
|
|
||||||
category: Improvements
|
|
||||||
credits: [9999years, horrors]
|
|
||||||
---
|
|
||||||
|
|
||||||
Before:
|
|
||||||
```
|
|
||||||
info: breakpoint reached
|
|
||||||
|
|
||||||
|
|
||||||
Starting REPL to allow you to inspect the current state of the evaluator.
|
|
||||||
|
|
||||||
Welcome to Nix 2.20.0pre20231222_dirty. Type :? for help.
|
|
||||||
|
|
||||||
nix-repl> :continue
|
|
||||||
error: uh oh
|
|
||||||
|
|
||||||
|
|
||||||
Starting REPL to allow you to inspect the current state of the evaluator.
|
|
||||||
|
|
||||||
Welcome to Nix 2.20.0pre20231222_dirty. Type :? for help.
|
|
||||||
|
|
||||||
nix-repl>
|
|
||||||
```
|
|
||||||
|
|
||||||
After:
|
|
||||||
|
|
||||||
```
|
|
||||||
info: breakpoint reached
|
|
||||||
|
|
||||||
Nix 2.20.0pre20231222_dirty debugger
|
|
||||||
Type :? for help.
|
|
||||||
nix-repl> :continue
|
|
||||||
error: uh oh
|
|
||||||
|
|
||||||
nix-repl>
|
|
||||||
```
|
|
|
@ -1,31 +0,0 @@
|
||||||
---
|
|
||||||
synopsis: Rename all the libraries nixexpr, nixstore, etc to lixexpr, lixstore, etc
|
|
||||||
credits: jade
|
|
||||||
category: Breaking Changes
|
|
||||||
---
|
|
||||||
|
|
||||||
The Lix C++ API libraries have had the following changes:
|
|
||||||
- Includes moved from `include/nix/` to `include/lix/`
|
|
||||||
- `pkg-config` files renamed from `nix-expr` to `lix-expr` and so on.
|
|
||||||
- Libraries renamed from `libnixexpr.so` to `liblixexpr.so` and so on.
|
|
||||||
|
|
||||||
There are other changes between Nix 2.18 and Lix, since these APIs are not
|
|
||||||
stable. However, this change in particular is a deliberate compatibility break
|
|
||||||
to force downstreams linking to Lix to specifically handle Lix and avoid Lix
|
|
||||||
accidentally getting ensnared in compatibility code for newer CppNix.
|
|
||||||
|
|
||||||
Migration path:
|
|
||||||
|
|
||||||
- expr.hh -> lix/libexpr/expr.hh
|
|
||||||
- nix/config.h -> lix/config.h
|
|
||||||
|
|
||||||
To apply this migration automatically, remove all `<nix/>` from includes, so `#include <nix/expr.hh>` -> `#include <expr.hh>`.
|
|
||||||
Then, the correct paths will be resolved from the tangled mess, and the clang-tidy automated fix will work.
|
|
||||||
|
|
||||||
Then run the following for out of tree projects (header filter is set to only fix instances in headers in `../src` relative to the compiler's working directory, as would be the case in nix-eval-jobs or other things built with meson, e.g.):
|
|
||||||
|
|
||||||
```console
|
|
||||||
lix_root=$HOME/lix
|
|
||||||
(cd $lix_root/clang-tidy && nix develop -c 'meson setup build && ninja -C build')
|
|
||||||
run-clang-tidy -checks='-*,lix-fixincludes' -load=$lix_root/clang-tidy/build/liblix-clang-tidy.so -p build/ -header-filter '\.\./src/.*\.h' -fix src
|
|
||||||
```
|
|
8
doc/manual/rl-next/repl-complete-colon.md
Normal file
8
doc/manual/rl-next/repl-complete-colon.md
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
---
|
||||||
|
synopsis: "`nix repl` now allows tab-completing the special repl :colon commands"
|
||||||
|
cls: 1367
|
||||||
|
credits: Qyriad
|
||||||
|
category: Improvements
|
||||||
|
---
|
||||||
|
|
||||||
|
The REPL (`nix repl`) supports pressing `<TAB>` to complete a partial expression, but now also supports completing the special :colon commands as well (`:b`, `:edit`, `:doc`, etc), if the line starts with a colon.
|
|
@ -1,16 +0,0 @@
|
||||||
---
|
|
||||||
synopsis: Experimental REPL support for documentation comments using `:doc`
|
|
||||||
cls: 564
|
|
||||||
category: Features
|
|
||||||
credits: [Lunaphied, jade]
|
|
||||||
significance: significant
|
|
||||||
---
|
|
||||||
|
|
||||||
Using `:doc` in the REPL now supports showing documentation comments when defined on a function.
|
|
||||||
|
|
||||||
Previously this was only able to document builtins, however it now will show comments defined on a lambda as well.
|
|
||||||
|
|
||||||
This support is experimental and relies on an embedded version of [nix-doc](https://github.com/lf-/nix-doc).
|
|
||||||
|
|
||||||
The logic also supports limited Markdown formatting of doccomments and should easily support any [RFC 145](https://github.com/NixOS/rfcs/blob/master/rfcs/0145-doc-strings.md)
|
|
||||||
compatible documentation comments in addition to simple commented documentation.
|
|
|
@ -1,9 +0,0 @@
|
||||||
---
|
|
||||||
synopsis: "`nix repl` history is saved more reliably"
|
|
||||||
cls: 1164
|
|
||||||
credits: puck
|
|
||||||
---
|
|
||||||
|
|
||||||
`nix repl` now saves its history file after each line, rather than at the end
|
|
||||||
of the session; ensuring that it will remember what you typed even after it
|
|
||||||
crashes.
|
|
|
@ -1,10 +0,0 @@
|
||||||
---
|
|
||||||
synopsis: Interrupting builds in the REPL works more than once
|
|
||||||
cls: 1097
|
|
||||||
category: Fixes
|
|
||||||
credits: alois31
|
|
||||||
---
|
|
||||||
|
|
||||||
Builds in the REPL can be interrupted by pressing Ctrl+C.
|
|
||||||
Previously, this only worked once per REPL session; further attempts would be ignored.
|
|
||||||
This issue is now fixed, so that builds can be canceled consistently.
|
|
|
@ -1,39 +0,0 @@
|
||||||
---
|
|
||||||
synopsis: Add `repl-overlays` option
|
|
||||||
prs: 10203
|
|
||||||
cls: 504
|
|
||||||
credits: 9999years
|
|
||||||
significance: significant
|
|
||||||
category: Features
|
|
||||||
---
|
|
||||||
|
|
||||||
A `repl-overlays` option has been added, which specifies files that can overlay
|
|
||||||
and modify the top-level bindings in `nix repl`. For example, with the
|
|
||||||
following contents in `~/.config/nix/repl.nix`:
|
|
||||||
|
|
||||||
```nix
|
|
||||||
info: final: prev: let
|
|
||||||
optionalAttrs = predicate: attrs:
|
|
||||||
if predicate
|
|
||||||
then attrs
|
|
||||||
else {};
|
|
||||||
in
|
|
||||||
optionalAttrs (prev ? legacyPackages && prev.legacyPackages ? ${info.currentSystem})
|
|
||||||
{
|
|
||||||
pkgs = prev.legacyPackages.${info.currentSystem};
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
We can run `nix repl` and use `pkgs` to refer to `legacyPackages.${currentSystem}`:
|
|
||||||
|
|
||||||
```ShellSession
|
|
||||||
$ nix repl --repl-overlays ~/.config/nix/repl.nix nixpkgs
|
|
||||||
Lix 2.90.0
|
|
||||||
Type :? for help.
|
|
||||||
Loading installable 'flake:nixpkgs#'...
|
|
||||||
Added 5 variables.
|
|
||||||
Loading 'repl-overlays'...
|
|
||||||
Added 6 variables.
|
|
||||||
nix-repl> pkgs.bash
|
|
||||||
«derivation /nix/store/g08b5vkwwh0j8ic9rkmd8mpj878rk62z-bash-5.2p26.drv»
|
|
||||||
```
|
|
|
@ -1,7 +0,0 @@
|
||||||
---
|
|
||||||
synopsis: "REPL now supports CTRL+Z to suspend"
|
|
||||||
credits: [Qyriad]
|
|
||||||
category: Improvements
|
|
||||||
---
|
|
||||||
|
|
||||||
Editline is now built with SIGTSTP support, so now typing CTRL+Z in the REPL will suspend the REPL and allow it to be resumed later or backgrounded.
|
|
|
@ -1,13 +0,0 @@
|
||||||
---
|
|
||||||
synopsis: Allow single quotes in nix-shell shebangs
|
|
||||||
prs: 8470
|
|
||||||
credits: [ncfavier, horrors]
|
|
||||||
category: Improvements
|
|
||||||
---
|
|
||||||
|
|
||||||
Example:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
#! /usr/bin/env nix-shell
|
|
||||||
#! nix-shell -i bash --packages 'terraform.withPlugins (plugins: [ plugins.openstack ])'
|
|
||||||
```
|
|
|
@ -1,16 +0,0 @@
|
||||||
---
|
|
||||||
synopsis: reintroduce shortened `-E` form for `--expr` to new CLI
|
|
||||||
cls: 605
|
|
||||||
credits: Lunaphied
|
|
||||||
category: Improvements
|
|
||||||
---
|
|
||||||
|
|
||||||
In the old CLI, it was possible to supply a shorter `-E` flag instead of fully
|
|
||||||
specifying `--expr` every time you wished to provide an expression that would
|
|
||||||
be evaluated to produce the given command's input. This was retained for the
|
|
||||||
`--file` flag when the new CLI utilities were written with `-f`, but `-E` was
|
|
||||||
dropped.
|
|
||||||
|
|
||||||
We now restore the `-E` short form for better UX. This is most useful for
|
|
||||||
`nix eval` but most any command that takes an Installable argument should benefit
|
|
||||||
from it as well.
|
|
|
@ -1,25 +0,0 @@
|
||||||
---
|
|
||||||
synopsis: "In the debugger, `while evaluating the attribute` errors now include position information"
|
|
||||||
prs: 9915
|
|
||||||
credits: 9999years
|
|
||||||
category: Fixes
|
|
||||||
---
|
|
||||||
|
|
||||||
Before:
|
|
||||||
|
|
||||||
```
|
|
||||||
0: while evaluating the attribute 'python311.pythonForBuild.pkgs'
|
|
||||||
0x600001522598
|
|
||||||
```
|
|
||||||
|
|
||||||
After:
|
|
||||||
|
|
||||||
```
|
|
||||||
0: while evaluating the attribute 'python311.pythonForBuild.pkgs'
|
|
||||||
/nix/store/hg65h51xnp74ikahns9hyf3py5mlbbqq-source/overrides/default.nix:132:27
|
|
||||||
|
|
||||||
131|
|
|
||||||
132| bootstrappingBase = pkgs.${self.python.pythonAttr}.pythonForBuild.pkgs;
|
|
||||||
| ^
|
|
||||||
133| in
|
|
||||||
```
|
|
|
@ -1,44 +0,0 @@
|
||||||
---
|
|
||||||
synopsis: Source locations are printed more consistently in errors
|
|
||||||
issues: 561
|
|
||||||
prs: 9555
|
|
||||||
credits: [9999years, horrors]
|
|
||||||
category: Improvements
|
|
||||||
---
|
|
||||||
|
|
||||||
Source location information is now included in error messages more
|
|
||||||
consistently. Given this code:
|
|
||||||
|
|
||||||
```nix
|
|
||||||
let
|
|
||||||
attr = {foo = "bar";};
|
|
||||||
key = {};
|
|
||||||
in
|
|
||||||
attr.${key}
|
|
||||||
```
|
|
||||||
|
|
||||||
Previously, Nix would show this unhelpful message when attempting to evaluate
|
|
||||||
it:
|
|
||||||
|
|
||||||
```
|
|
||||||
error:
|
|
||||||
… while evaluating an attribute name
|
|
||||||
|
|
||||||
error: value is a set while a string was expected
|
|
||||||
```
|
|
||||||
|
|
||||||
Now, the error message displays where the problematic value was found:
|
|
||||||
|
|
||||||
```
|
|
||||||
error:
|
|
||||||
… while evaluating an attribute name
|
|
||||||
|
|
||||||
at bad.nix:4:11:
|
|
||||||
|
|
||||||
3| key = {};
|
|
||||||
4| in attr.${key}
|
|
||||||
| ^
|
|
||||||
5|
|
|
||||||
|
|
||||||
error: expected a string but found a set: { }
|
|
||||||
```
|
|
|
@ -1,8 +0,0 @@
|
||||||
---
|
|
||||||
synopsis: Include phase reporting in log file for ssh-ng builds
|
|
||||||
prs: 9280
|
|
||||||
credits: r-vdp
|
|
||||||
category: Fixes
|
|
||||||
---
|
|
||||||
|
|
||||||
Store phase information of remote builds run via `ssh-ng` remotes in the local log file, matching logging behavior of local builds.
|
|
|
@ -1,9 +0,0 @@
|
||||||
---
|
|
||||||
synopsis: Fix `ssh-ng://` remotes not respecting `--substitute-on-destination`
|
|
||||||
prs: 9600
|
|
||||||
credits: SharzyL
|
|
||||||
category: Fixes
|
|
||||||
---
|
|
||||||
|
|
||||||
`nix copy ssh-ng://` now respects `--substitute-on-destination`, as does `nix-copy-closure` and other commands that operate on remote `ssh-ng` stores.
|
|
||||||
Previously this was always set by `builders-use-substitutes` setting.
|
|
|
@ -1,35 +0,0 @@
|
||||||
---
|
|
||||||
synopsis: Some stack overflow segfaults are fixed
|
|
||||||
issues: 9616
|
|
||||||
prs: 9617
|
|
||||||
cls: 205
|
|
||||||
category: Improvements
|
|
||||||
credits: [9999years, horrors]
|
|
||||||
---
|
|
||||||
|
|
||||||
The number of nested function calls has been restricted, to detect and report
|
|
||||||
infinite function call recursions. The default maximum call depth is 10,000 and
|
|
||||||
can be set with [the `max-call-depth`
|
|
||||||
option](@docroot@/command-ref/conf-file.md#conf-max-call-depth).
|
|
||||||
|
|
||||||
This fixes segfaults or the following unhelpful error message in many cases:
|
|
||||||
|
|
||||||
error: stack overflow (possible infinite recursion)
|
|
||||||
|
|
||||||
Before:
|
|
||||||
|
|
||||||
```
|
|
||||||
$ nix-instantiate --eval --expr '(x: x x) (x: x x)'
|
|
||||||
Segmentation fault: 11
|
|
||||||
```
|
|
||||||
|
|
||||||
After:
|
|
||||||
|
|
||||||
```
|
|
||||||
$ nix-instantiate --eval --expr '(x: x x) (x: x x)'
|
|
||||||
error: stack overflow
|
|
||||||
|
|
||||||
at «string»:1:14:
|
|
||||||
1| (x: x x) (x: x x)
|
|
||||||
| ^
|
|
||||||
```
|
|
|
@ -1,8 +0,0 @@
|
||||||
---
|
|
||||||
synopsis: add `--store-path` argument to `nix upgrade-nix`, to manually specify the Nix to upgrade to
|
|
||||||
cls: 953
|
|
||||||
credits: Qyriad
|
|
||||||
category: Features
|
|
||||||
---
|
|
||||||
|
|
||||||
`nix upgrade-nix` by default downloads a manifest to find the new Nix version to upgrade to, but now you can specify `--store-path` to upgrade Nix to an arbitrary version from the Nix store.
|
|
|
@ -1,10 +0,0 @@
|
||||||
---
|
|
||||||
synopsis: using `nix profile` on `/nix/var/nix/profiles/default` no longer breaks `nix upgrade-nix`
|
|
||||||
cls: 952
|
|
||||||
credits: Qyriad
|
|
||||||
category: Fixes
|
|
||||||
---
|
|
||||||
|
|
||||||
On non-NixOS, Nix is conventionally installed into a `nix-env` style profile at /nix/var/nix/profiles/default.
|
|
||||||
Like any `nix-env` profile, using `nix profile` on it automatically migrates it to a `nix profile` style profile, which is incompatible with `nix-env`.
|
|
||||||
`nix upgrade-nix` previously relied solely on `nix-env` to do the upgrade, but now will work fine with either kind of profile.
|
|
|
@ -1,10 +0,0 @@
|
||||||
---
|
|
||||||
synopsis: Upstart scripts removed
|
|
||||||
cls: 574
|
|
||||||
category: Packaging
|
|
||||||
credits: jade
|
|
||||||
---
|
|
||||||
|
|
||||||
Upstart scripts have been removed from Lix, since Upstart is obsolete and has
|
|
||||||
not been shipped by any major distributions for many years. If these are
|
|
||||||
necessary to your use case, please back port them to your packaging.
|
|
|
@ -1,9 +0,0 @@
|
||||||
---
|
|
||||||
synopsis: Warn about ignored client settings
|
|
||||||
cls: 1026
|
|
||||||
credits: jade
|
|
||||||
category: Improvements
|
|
||||||
---
|
|
||||||
|
|
||||||
Emit a warning for every client-provided setting the daemon ignores because the requesting client is not run by a trusted user.
|
|
||||||
Previously this was only a debug message.
|
|
|
@ -1,34 +0,0 @@
|
||||||
---
|
|
||||||
synopsis: Better error reporting for `with` expressions
|
|
||||||
prs: 9658
|
|
||||||
cls: 207
|
|
||||||
credits: horrors
|
|
||||||
category: Improvements
|
|
||||||
---
|
|
||||||
|
|
||||||
`with` expressions using non-attrset values to resolve variables are now reported with proper positions.
|
|
||||||
|
|
||||||
Previously an incorrect `with` expression would report no position at all, making it hard to determine where the error originated:
|
|
||||||
|
|
||||||
```
|
|
||||||
nix-repl> with 1; a
|
|
||||||
error:
|
|
||||||
… <borked>
|
|
||||||
|
|
||||||
at «none»:0: (source not available)
|
|
||||||
|
|
||||||
error: value is an integer while a set was expected
|
|
||||||
```
|
|
||||||
|
|
||||||
Now position information is preserved and reported as with most other errors:
|
|
||||||
|
|
||||||
```
|
|
||||||
nix-repl> with 1; a
|
|
||||||
error:
|
|
||||||
… while evaluating the first subexpression of a with expression
|
|
||||||
at «string»:1:1:
|
|
||||||
1| with 1; a
|
|
||||||
| ^
|
|
||||||
|
|
||||||
error: expected a set but found an integer: 1
|
|
||||||
```
|
|
|
@ -196,53 +196,55 @@
|
||||||
- [C++ style guide](contributing/cxx.md)
|
- [C++ style guide](contributing/cxx.md)
|
||||||
- [Release Notes](release-notes/release-notes.md)
|
- [Release Notes](release-notes/release-notes.md)
|
||||||
- [Upcoming release](release-notes/rl-next.md)
|
- [Upcoming release](release-notes/rl-next.md)
|
||||||
- [Release 2.18 (2023-09-20)](release-notes/rl-2.18.md)
|
<!-- RELENG-AUTO-INSERTION-MARKER (see releng/release_notes.py) -->
|
||||||
- [Release 2.17 (2023-07-24)](release-notes/rl-2.17.md)
|
- [Lix 2.90 (FIXME date)](release-notes/rl-2.90.md)
|
||||||
- [Release 2.16 (2023-05-31)](release-notes/rl-2.16.md)
|
- [Nix 2.18 (2023-09-20)](release-notes/rl-2.18.md)
|
||||||
- [Release 2.15 (2023-04-11)](release-notes/rl-2.15.md)
|
- [Nix 2.17 (2023-07-24)](release-notes/rl-2.17.md)
|
||||||
- [Release 2.14 (2023-02-28)](release-notes/rl-2.14.md)
|
- [Nix 2.16 (2023-05-31)](release-notes/rl-2.16.md)
|
||||||
- [Release 2.13 (2023-01-17)](release-notes/rl-2.13.md)
|
- [Nix 2.15 (2023-04-11)](release-notes/rl-2.15.md)
|
||||||
- [Release 2.12 (2022-12-06)](release-notes/rl-2.12.md)
|
- [Nix 2.14 (2023-02-28)](release-notes/rl-2.14.md)
|
||||||
- [Release 2.11 (2022-08-25)](release-notes/rl-2.11.md)
|
- [Nix 2.13 (2023-01-17)](release-notes/rl-2.13.md)
|
||||||
- [Release 2.10 (2022-07-11)](release-notes/rl-2.10.md)
|
- [Nix 2.12 (2022-12-06)](release-notes/rl-2.12.md)
|
||||||
- [Release 2.9 (2022-05-30)](release-notes/rl-2.9.md)
|
- [Nix 2.11 (2022-08-25)](release-notes/rl-2.11.md)
|
||||||
- [Release 2.8 (2022-04-19)](release-notes/rl-2.8.md)
|
- [Nix 2.10 (2022-07-11)](release-notes/rl-2.10.md)
|
||||||
- [Release 2.7 (2022-03-07)](release-notes/rl-2.7.md)
|
- [Nix 2.9 (2022-05-30)](release-notes/rl-2.9.md)
|
||||||
- [Release 2.6 (2022-01-24)](release-notes/rl-2.6.md)
|
- [Nix 2.8 (2022-04-19)](release-notes/rl-2.8.md)
|
||||||
- [Release 2.5 (2021-12-13)](release-notes/rl-2.5.md)
|
- [Nix 2.7 (2022-03-07)](release-notes/rl-2.7.md)
|
||||||
- [Release 2.4 (2021-11-01)](release-notes/rl-2.4.md)
|
- [Nix 2.6 (2022-01-24)](release-notes/rl-2.6.md)
|
||||||
- [Release 2.3 (2019-09-04)](release-notes/rl-2.3.md)
|
- [Nix 2.5 (2021-12-13)](release-notes/rl-2.5.md)
|
||||||
- [Release 2.2 (2019-01-11)](release-notes/rl-2.2.md)
|
- [Nix 2.4 (2021-11-01)](release-notes/rl-2.4.md)
|
||||||
- [Release 2.1 (2018-09-02)](release-notes/rl-2.1.md)
|
- [Nix 2.3 (2019-09-04)](release-notes/rl-2.3.md)
|
||||||
- [Release 2.0 (2018-02-22)](release-notes/rl-2.0.md)
|
- [Nix 2.2 (2019-01-11)](release-notes/rl-2.2.md)
|
||||||
- [Release 1.11.10 (2017-06-12)](release-notes/rl-1.11.10.md)
|
- [Nix 2.1 (2018-09-02)](release-notes/rl-2.1.md)
|
||||||
- [Release 1.11 (2016-01-19)](release-notes/rl-1.11.md)
|
- [Nix 2.0 (2018-02-22)](release-notes/rl-2.0.md)
|
||||||
- [Release 1.10 (2015-09-03)](release-notes/rl-1.10.md)
|
- [Nix 1.11.10 (2017-06-12)](release-notes/rl-1.11.10.md)
|
||||||
- [Release 1.9 (2015-06-12)](release-notes/rl-1.9.md)
|
- [Nix 1.11 (2016-01-19)](release-notes/rl-1.11.md)
|
||||||
- [Release 1.8 (2014-12-14)](release-notes/rl-1.8.md)
|
- [Nix 1.10 (2015-09-03)](release-notes/rl-1.10.md)
|
||||||
- [Release 1.7 (2014-04-11)](release-notes/rl-1.7.md)
|
- [Nix 1.9 (2015-06-12)](release-notes/rl-1.9.md)
|
||||||
- [Release 1.6.1 (2013-10-28)](release-notes/rl-1.6.1.md)
|
- [Nix 1.8 (2014-12-14)](release-notes/rl-1.8.md)
|
||||||
- [Release 1.6 (2013-09-10)](release-notes/rl-1.6.md)
|
- [Nix 1.7 (2014-04-11)](release-notes/rl-1.7.md)
|
||||||
- [Release 1.5.2 (2013-05-13)](release-notes/rl-1.5.2.md)
|
- [Nix 1.6.1 (2013-10-28)](release-notes/rl-1.6.1.md)
|
||||||
- [Release 1.5 (2013-02-27)](release-notes/rl-1.5.md)
|
- [Nix 1.6 (2013-09-10)](release-notes/rl-1.6.md)
|
||||||
- [Release 1.4 (2013-02-26)](release-notes/rl-1.4.md)
|
- [Nix 1.5.2 (2013-05-13)](release-notes/rl-1.5.2.md)
|
||||||
- [Release 1.3 (2013-01-04)](release-notes/rl-1.3.md)
|
- [Nix 1.5 (2013-02-27)](release-notes/rl-1.5.md)
|
||||||
- [Release 1.2 (2012-12-06)](release-notes/rl-1.2.md)
|
- [Nix 1.4 (2013-02-26)](release-notes/rl-1.4.md)
|
||||||
- [Release 1.1 (2012-07-18)](release-notes/rl-1.1.md)
|
- [Nix 1.3 (2013-01-04)](release-notes/rl-1.3.md)
|
||||||
- [Release 1.0 (2012-05-11)](release-notes/rl-1.0.md)
|
- [Nix 1.2 (2012-12-06)](release-notes/rl-1.2.md)
|
||||||
- [Release 0.16 (2010-08-17)](release-notes/rl-0.16.md)
|
- [Nix 1.1 (2012-07-18)](release-notes/rl-1.1.md)
|
||||||
- [Release 0.15 (2010-03-17)](release-notes/rl-0.15.md)
|
- [Nix 1.0 (2012-05-11)](release-notes/rl-1.0.md)
|
||||||
- [Release 0.14 (2010-02-04)](release-notes/rl-0.14.md)
|
- [Nix 0.16 (2010-08-17)](release-notes/rl-0.16.md)
|
||||||
- [Release 0.13 (2009-11-05)](release-notes/rl-0.13.md)
|
- [Nix 0.15 (2010-03-17)](release-notes/rl-0.15.md)
|
||||||
- [Release 0.12 (2008-11-20)](release-notes/rl-0.12.md)
|
- [Nix 0.14 (2010-02-04)](release-notes/rl-0.14.md)
|
||||||
- [Release 0.11 (2007-12-31)](release-notes/rl-0.11.md)
|
- [Nix 0.13 (2009-11-05)](release-notes/rl-0.13.md)
|
||||||
- [Release 0.10.1 (2006-10-11)](release-notes/rl-0.10.1.md)
|
- [Nix 0.12 (2008-11-20)](release-notes/rl-0.12.md)
|
||||||
- [Release 0.10 (2006-10-06)](release-notes/rl-0.10.md)
|
- [Nix 0.11 (2007-12-31)](release-notes/rl-0.11.md)
|
||||||
- [Release 0.9.2 (2005-09-21)](release-notes/rl-0.9.2.md)
|
- [Nix 0.10.1 (2006-10-11)](release-notes/rl-0.10.1.md)
|
||||||
- [Release 0.9.1 (2005-09-20)](release-notes/rl-0.9.1.md)
|
- [Nix 0.10 (2006-10-06)](release-notes/rl-0.10.md)
|
||||||
- [Release 0.9 (2005-09-16)](release-notes/rl-0.9.md)
|
- [Nix 0.9.2 (2005-09-21)](release-notes/rl-0.9.2.md)
|
||||||
- [Release 0.8.1 (2005-04-13)](release-notes/rl-0.8.1.md)
|
- [Nix 0.9.1 (2005-09-20)](release-notes/rl-0.9.1.md)
|
||||||
- [Release 0.8 (2005-04-11)](release-notes/rl-0.8.md)
|
- [Nix 0.9 (2005-09-16)](release-notes/rl-0.9.md)
|
||||||
- [Release 0.7 (2005-01-12)](release-notes/rl-0.7.md)
|
- [Nix 0.8.1 (2005-04-13)](release-notes/rl-0.8.1.md)
|
||||||
- [Release 0.6 (2004-11-14)](release-notes/rl-0.6.md)
|
- [Nix 0.8 (2005-04-11)](release-notes/rl-0.8.md)
|
||||||
- [Release 0.5 and earlier](release-notes/rl-0.5.md)
|
- [Nix 0.7 (2005-01-12)](release-notes/rl-0.7.md)
|
||||||
|
- [Nix 0.6 (2004-11-14)](release-notes/rl-0.6.md)
|
||||||
|
- [Nix 0.5 and earlier](release-notes/rl-0.5.md)
|
||||||
|
|
|
@ -78,6 +78,15 @@ Most commands in Lix accept the following command-line options:
|
||||||
|
|
||||||
Display the raw logs, with the progress bar at the bottom.
|
Display the raw logs, with the progress bar at the bottom.
|
||||||
|
|
||||||
|
- `multiline`
|
||||||
|
|
||||||
|
Display a progress bar during the builds and in the lines below that one line per activity.
|
||||||
|
|
||||||
|
|
||||||
|
- `multiline-with-logs`
|
||||||
|
|
||||||
|
Displayes the raw logs, with a progress bar and activities each in a new line at the bottom.
|
||||||
|
|
||||||
- <span id="opt-no-build-output">[`--no-build-output`](#opt-no-build-output)</span> / `-Q`
|
- <span id="opt-no-build-output">[`--no-build-output`](#opt-no-build-output)</span> / `-Q`
|
||||||
|
|
||||||
By default, output written by builders to standard output and standard error is echoed to the Lix command's standard error.
|
By default, output written by builders to standard output and standard error is echoed to the Lix command's standard error.
|
||||||
|
|
929
doc/manual/src/release-notes/rl-2.90.md
Normal file
929
doc/manual/src/release-notes/rl-2.90.md
Normal file
|
@ -0,0 +1,929 @@
|
||||||
|
# Lix 2.90 "Vanilla Ice Cream" (FIXME date)
|
||||||
|
|
||||||
|
|
||||||
|
# Lix 2.90.0 (FIXME date)
|
||||||
|
|
||||||
|
## Breaking Changes
|
||||||
|
- Deprecate the online flake registries and vendor the default registry [fj#183](https://git.lix.systems/lix-project/lix/issues/183) [fj#110](https://git.lix.systems/lix-project/lix/issues/110) [fj#116](https://git.lix.systems/lix-project/lix/issues/116) [#8953](https://github.com/NixOS/nix/issues/8953) [#9087](https://github.com/NixOS/nix/issues/9087) [cl/1127](https://gerrit.lix.systems/c/lix/+/1127)
|
||||||
|
|
||||||
|
The online flake registry [https://channels.nixos.org/flake-registry.json](https://channels.nixos.org/flake-registry.json) is not pinned in any way,
|
||||||
|
and the targets of the indirections can both update or change entirely at any
|
||||||
|
point. Furthermore, it is refetched on every use of a flake reference, even if
|
||||||
|
there is a local flake reference, and even if you are offline (which breaks).
|
||||||
|
|
||||||
|
For now, we deprecate the (any) online flake registry, and vendor a copy of the
|
||||||
|
current online flake registry. This makes it work offline, and ensures that
|
||||||
|
it won't change in the future.
|
||||||
|
|
||||||
|
Many thanks to [julia](https://git.lix.systems/midnightveil) for this.
|
||||||
|
- Enforce syscall filtering and no-new-privileges on Linux [cl/1063](https://gerrit.lix.systems/c/lix/+/1063)
|
||||||
|
|
||||||
|
In order to improve consistency of the build environment, system call filtering and no-new-privileges are now unconditionally enabled on Linux.
|
||||||
|
The `filter-syscalls` and `allow-new-privileges` options which could be used to disable these features under some circumstances have been removed.
|
||||||
|
|
||||||
|
In order to support building on architectures without libseccomp support, the option to disable syscall filtering at build time remains.
|
||||||
|
However, other uses of this option are heavily discouraged, since it would reduce the security of the sandbox substantially.
|
||||||
|
|
||||||
|
Many thanks to [alois31](https://git.lix.systems/alois31) for this.
|
||||||
|
- Overhaul `nix flake update` and `nix flake lock` UX [#8817](https://github.com/NixOS/nix/pull/8817)
|
||||||
|
|
||||||
|
The interface for creating and updating lock files has been overhauled:
|
||||||
|
|
||||||
|
- [`nix flake lock`](@docroot@/command-ref/new-cli/nix3-flake-lock.md) only creates lock files and adds missing inputs now.
|
||||||
|
It will *never* update existing inputs.
|
||||||
|
|
||||||
|
- [`nix flake update`](@docroot@/command-ref/new-cli/nix3-flake-update.md) does the same, but *will* update inputs.
|
||||||
|
- Passing no arguments will update all inputs of the current flake, just like it already did.
|
||||||
|
- Passing input names as arguments will ensure only those are updated. This replaces the functionality of `nix flake lock --update-input`
|
||||||
|
- To operate on a flake outside the current directory, you must now pass `--flake path/to/flake`.
|
||||||
|
|
||||||
|
- The flake-specific flags `--recreate-lock-file` and `--update-input` have been removed from all commands operating on installables.
|
||||||
|
They are superceded by `nix flake update`.
|
||||||
|
|
||||||
|
Many thanks to [iFreilicht](https://github.com/iFreilicht), [Lunaphied](https://git.lix.systems/Lunaphied), and [Théophane Hufschmitt](https://github.com/thufschmitt) for this.
|
||||||
|
- `nix profile` now allows referring to elements by human-readable name, and no longer accepts indices [#8678](https://github.com/NixOS/nix/pull/8678) [cl/978](https://gerrit.lix.systems/c/lix/+/978) [cl/980](https://gerrit.lix.systems/c/lix/+/980)
|
||||||
|
|
||||||
|
[`nix profile`](@docroot@/command-ref/new-cli/nix3-profile.md) now uses names to refer to installed packages when running [`list`](@docroot@/command-ref/new-cli/nix3-profile-list.md), [`remove`](@docroot@/command-ref/new-cli/nix3-profile-remove.md) or [`upgrade`](@docroot@/command-ref/new-cli/nix3-profile-upgrade.md) as opposed to indices. Indices have been removed. Profile element names are generated when a package is installed and remain the same until the package is removed.
|
||||||
|
|
||||||
|
**Warning**: The `manifest.nix` file used to record the contents of profiles has changed. Lix will automatically upgrade profiles to the new version when you modify the profile. After that, the profile can no longer be used by older versions of Lix.
|
||||||
|
|
||||||
|
Many thanks to [iFreilicht](https://github.com/iFreilicht), [Qyriad](https://git.lix.systems/Qyriad), and [Eelco Dolstra](https://github.com/edolstra) for this.
|
||||||
|
- `builtins.nixVersion` and `builtins.langVersion` return fixed values [cl/558](https://gerrit.lix.systems/c/lix/+/558) [cl/1144](https://gerrit.lix.systems/c/lix/+/1144)
|
||||||
|
|
||||||
|
`builtins.nixVersion` now returns a fixed value `"2.18.3-lix"`.
|
||||||
|
|
||||||
|
`builtins.langVersion` returns a fixed value `6`, matching CppNix 2.18.
|
||||||
|
|
||||||
|
This prevents feature detection assuming that features that exist in Nix
|
||||||
|
post-Lix-branch-off might exist, even though the Lix version is greater than
|
||||||
|
the Nix version.
|
||||||
|
|
||||||
|
In the future, check for builtins for feature detection. If a feature cannot be
|
||||||
|
detected by *those* means, please file a Lix bug.
|
||||||
|
|
||||||
|
Many thanks to [jade](https://git.lix.systems/jade) for this.
|
||||||
|
- Rename all the libraries nixexpr, nixstore, etc to lixexpr, lixstore, etc
|
||||||
|
|
||||||
|
The Lix C++ API libraries have had the following changes:
|
||||||
|
- Includes moved from `include/nix/` to `include/lix/`
|
||||||
|
- `pkg-config` files renamed from `nix-expr` to `lix-expr` and so on.
|
||||||
|
- Libraries renamed from `libnixexpr.so` to `liblixexpr.so` and so on.
|
||||||
|
|
||||||
|
There are other changes between Nix 2.18 and Lix, since these APIs are not
|
||||||
|
stable. However, this change in particular is a deliberate compatibility break
|
||||||
|
to force downstreams linking to Lix to specifically handle Lix and avoid Lix
|
||||||
|
accidentally getting ensnared in compatibility code for newer CppNix.
|
||||||
|
|
||||||
|
Migration path:
|
||||||
|
|
||||||
|
- expr.hh -> lix/libexpr/expr.hh
|
||||||
|
- nix/config.h -> lix/config.h
|
||||||
|
|
||||||
|
To apply this migration automatically, remove all `<nix/>` from includes, so `#include <nix/expr.hh>` -> `#include <expr.hh>`.
|
||||||
|
Then, the correct paths will be resolved from the tangled mess, and the clang-tidy automated fix will work.
|
||||||
|
|
||||||
|
Then run the following for out of tree projects (header filter is set to only fix instances in headers in `../src` relative to the compiler's working directory, as would be the case in nix-eval-jobs or other things built with meson, e.g.):
|
||||||
|
|
||||||
|
```console
|
||||||
|
lix_root=$HOME/lix
|
||||||
|
(cd $lix_root/clang-tidy && nix develop -c 'meson setup build && ninja -C build')
|
||||||
|
run-clang-tidy -checks='-*,lix-fixincludes' -load=$lix_root/clang-tidy/build/liblix-clang-tidy.so -p build/ -header-filter '\.\./src/.*\.h' -fix src
|
||||||
|
```
|
||||||
|
|
||||||
|
Many thanks to [jade](https://git.lix.systems/jade) for this.
|
||||||
|
|
||||||
|
## Features
|
||||||
|
- Experimental REPL support for documentation comments using `:doc` [cl/564](https://gerrit.lix.systems/c/lix/+/564)
|
||||||
|
|
||||||
|
Using `:doc` in the REPL now supports showing documentation comments when defined on a function.
|
||||||
|
|
||||||
|
Previously this was only able to document builtins, however it now will show comments defined on a lambda as well.
|
||||||
|
|
||||||
|
This support is experimental and relies on an embedded version of [nix-doc](https://github.com/lf-/nix-doc).
|
||||||
|
|
||||||
|
The logic also supports limited Markdown formatting of doccomments and should easily support any [RFC 145](https://github.com/NixOS/rfcs/blob/master/rfcs/0145-doc-strings.md)
|
||||||
|
compatible documentation comments in addition to simple commented documentation.
|
||||||
|
|
||||||
|
Many thanks to [Lunaphied](https://git.lix.systems/Lunaphied) and [jade](https://git.lix.systems/jade) for this.
|
||||||
|
- Add `repl-overlays` option [#10203](https://github.com/NixOS/nix/pull/10203) [cl/504](https://gerrit.lix.systems/c/lix/+/504)
|
||||||
|
|
||||||
|
A `repl-overlays` option has been added, which specifies files that can overlay
|
||||||
|
and modify the top-level bindings in `nix repl`. For example, with the
|
||||||
|
following contents in `~/.config/nix/repl.nix`:
|
||||||
|
|
||||||
|
```nix
|
||||||
|
info: final: prev: let
|
||||||
|
optionalAttrs = predicate: attrs:
|
||||||
|
if predicate
|
||||||
|
then attrs
|
||||||
|
else {};
|
||||||
|
in
|
||||||
|
optionalAttrs (prev ? legacyPackages && prev.legacyPackages ? ${info.currentSystem})
|
||||||
|
{
|
||||||
|
pkgs = prev.legacyPackages.${info.currentSystem};
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
We can run `nix repl` and use `pkgs` to refer to `legacyPackages.${currentSystem}`:
|
||||||
|
|
||||||
|
```ShellSession
|
||||||
|
$ nix repl --repl-overlays ~/.config/nix/repl.nix nixpkgs
|
||||||
|
Lix 2.90.0
|
||||||
|
Type :? for help.
|
||||||
|
Loading installable 'flake:nixpkgs#'...
|
||||||
|
Added 5 variables.
|
||||||
|
Loading 'repl-overlays'...
|
||||||
|
Added 6 variables.
|
||||||
|
nix-repl> pkgs.bash
|
||||||
|
«derivation /nix/store/g08b5vkwwh0j8ic9rkmd8mpj878rk62z-bash-5.2p26.drv»
|
||||||
|
```
|
||||||
|
|
||||||
|
Many thanks to [wiggles](https://git.lix.systems/rbt) for this.
|
||||||
|
- Add a builtin `addDrvOutputDependencies` [#7910](https://github.com/NixOS/nix/issues/7910) [#9216](https://github.com/NixOS/nix/pull/9216)
|
||||||
|
|
||||||
|
This builtin allows taking a `drvPath`-like string and turning it into a string
|
||||||
|
with context such that, when it lands in a derivation, it will create
|
||||||
|
dependencies on *all the outputs* in its closure (!). Although `drvPath` does this
|
||||||
|
today, this builtin starts forming a path to migrate to making `drvPath` have a
|
||||||
|
more normal and less surprising string context behaviour (see linked issue and
|
||||||
|
PR for more details).
|
||||||
|
|
||||||
|
Many thanks to [John Ericson](https://github.com/ericson2314) and [eldritch horrors](https://git.lix.systems/pennae) for this.
|
||||||
|
- Enter the `--debugger` when `builtins.trace` is called if `debugger-on-trace` is set [#9914](https://github.com/NixOS/nix/pull/9914)
|
||||||
|
|
||||||
|
If the `debugger-on-trace` option is set and `--debugger` is given,
|
||||||
|
`builtins.trace` calls will behave similarly to `builtins.break` and will enter
|
||||||
|
the debug REPL. This is useful for determining where warnings are being emitted
|
||||||
|
from.
|
||||||
|
|
||||||
|
Many thanks to [wiggles](https://git.lix.systems/rbt) for this.
|
||||||
|
- Add an option `enable-core-dumps` that enables core dumps from builds [cl/1088](https://gerrit.lix.systems/c/lix/+/1088)
|
||||||
|
|
||||||
|
In the past, Lix disabled core dumps by setting the soft `RLIMIT_CORE` to 0
|
||||||
|
unconditionally. Although this rlimit could be altered from the builder since
|
||||||
|
it is just the soft limit, this was kind of annoying to do. By passing
|
||||||
|
`--option enable-core-dumps true` to an offending build, one can now cause the
|
||||||
|
core dumps to be handled by the system in the normal way (winding up in
|
||||||
|
`coredumpctl`, say, on Linux).
|
||||||
|
|
||||||
|
Many thanks to [julia](https://git.lix.systems/midnightveil) for this.
|
||||||
|
- Add new `eval-system` setting [#4093](https://github.com/NixOS/nix/pull/4093)
|
||||||
|
|
||||||
|
Add a new `eval-system` option.
|
||||||
|
Unlike `system`, it just overrides the value of `builtins.currentSystem`.
|
||||||
|
This is more useful than overriding `system`, because you can build these derivations on remote builders which can work on the given system.
|
||||||
|
In contrast, `system` also effects scheduling which will cause Lix to build those derivations locally even if that doesn't make sense.
|
||||||
|
|
||||||
|
`eval-system` only takes effect if it is non-empty.
|
||||||
|
If empty (the default) `system` is used as before, so there is no breakage.
|
||||||
|
|
||||||
|
Many thanks to [matthewbauer](https://github.com/matthewbauer) and [eldritch horrors](https://git.lix.systems/pennae) for this.
|
||||||
|
- add `--store-path` argument to `nix upgrade-nix`, to manually specify the Nix to upgrade to [cl/953](https://gerrit.lix.systems/c/lix/+/953)
|
||||||
|
|
||||||
|
`nix upgrade-nix` by default downloads a manifest to find the new Nix version to upgrade to, but now you can specify `--store-path` to upgrade Nix to an arbitrary version from the Nix store.
|
||||||
|
|
||||||
|
Many thanks to [Qyriad](https://git.lix.systems/Qyriad) for this.
|
||||||
|
|
||||||
|
## Improvements
|
||||||
|
- `nix flake check` logs the checks [#8882](https://github.com/NixOS/nix/issues/8882) [#8893](https://github.com/NixOS/nix/pull/8893) [cl/259](https://gerrit.lix.systems/c/lix/+/259) [cl/260](https://gerrit.lix.systems/c/lix/+/260) [cl/261](https://gerrit.lix.systems/c/lix/+/261) [cl/262](https://gerrit.lix.systems/c/lix/+/262)
|
||||||
|
|
||||||
|
`nix flake check` now logs the checks it runs and the derivations it evaluates:
|
||||||
|
|
||||||
|
```
|
||||||
|
$ nix flake check -v
|
||||||
|
evaluating flake...
|
||||||
|
checking flake output 'checks'...
|
||||||
|
checking derivation 'checks.aarch64-darwin.ghciwatch-tests'...
|
||||||
|
derivation evaluated to /nix/store/nh7dlvsrhds4cxl91mvgj4h5cbq6skmq-ghciwatch-test-0.3.0.drv
|
||||||
|
checking derivation 'checks.aarch64-darwin.ghciwatch-clippy'...
|
||||||
|
derivation evaluated to /nix/store/9cb5a6wmp6kf6hidqw9wphidvb8bshym-ghciwatch-clippy-0.3.0.drv
|
||||||
|
checking derivation 'checks.aarch64-darwin.ghciwatch-doc'...
|
||||||
|
derivation evaluated to /nix/store/8brdd3jbawfszpbs7vdpsrhy80as1il8-ghciwatch-doc-0.3.0.drv
|
||||||
|
checking derivation 'checks.aarch64-darwin.ghciwatch-fmt'...
|
||||||
|
derivation evaluated to /nix/store/wjhs0l1njl5pyji53xlmfjrlya0wmz8p-ghciwatch-fmt-0.3.0.drv
|
||||||
|
checking derivation 'checks.aarch64-darwin.ghciwatch-audit'...
|
||||||
|
derivation evaluated to /nix/store/z0mps8dyj2ds7c0fn0819y5h5611033z-ghciwatch-audit-0.3.0.drv
|
||||||
|
checking flake output 'packages'...
|
||||||
|
checking derivation 'packages.aarch64-darwin.default'...
|
||||||
|
derivation evaluated to /nix/store/41abbdyglw5x9vcsvd89xan3ydjf8d7r-ghciwatch-0.3.0.drv
|
||||||
|
checking flake output 'apps'...
|
||||||
|
checking flake output 'devShells'...
|
||||||
|
checking derivation 'devShells.aarch64-darwin.default'...
|
||||||
|
derivation evaluated to /nix/store/bc935gz7dylzmcpdb5cczr8gngv8pmdb-nix-shell.drv
|
||||||
|
running 5 flake checks...
|
||||||
|
warning: The check omitted these incompatible systems: aarch64-linux, x86_64-darwin, x86_64-linux
|
||||||
|
Use '--all-systems' to check all.
|
||||||
|
```
|
||||||
|
|
||||||
|
Many thanks to [wiggles](https://git.lix.systems/rbt), [Raito Bezarius](https://git.lix.systems/raito), and [eldritch horrors](https://git.lix.systems/pennae) for this.
|
||||||
|
- Add an option `always-allow-substitutes` to ignore `allowSubstitutes` in derivations [#8047](https://github.com/NixOS/nix/pull/8047)
|
||||||
|
|
||||||
|
You can set this setting to force a system to always allow substituting even
|
||||||
|
trivial derivations like `pkgs.writeText`. This is useful for
|
||||||
|
[`nix-fast-build --skip-cached`][skip-cached] and similar to be able to also
|
||||||
|
ignore trivial derivations.
|
||||||
|
|
||||||
|
[skip-cached]: https://github.com/Mic92/nix-fast-build?tab=readme-ov-file#avoiding-redundant-package-downloads
|
||||||
|
|
||||||
|
Many thanks to [lovesegfault](https://github.com/lovesegfault) and [eldritch horrors](https://git.lix.systems/pennae) for this.
|
||||||
|
- Concise error printing in `nix repl` [#9928](https://github.com/NixOS/nix/pull/9928) [cl/811](https://gerrit.lix.systems/c/lix/+/811)
|
||||||
|
|
||||||
|
Previously, if an element of a list or attribute set threw an error while
|
||||||
|
evaluating, `nix repl` would print the entire error (including source location
|
||||||
|
information) inline. This output was clumsy and difficult to parse:
|
||||||
|
|
||||||
|
```
|
||||||
|
nix-repl> { err = builtins.throw "uh oh!"; }
|
||||||
|
{ err = «error:
|
||||||
|
… while calling the 'throw' builtin
|
||||||
|
at «string»:1:9:
|
||||||
|
1| { err = builtins.throw "uh oh!"; }
|
||||||
|
| ^
|
||||||
|
|
||||||
|
error: uh oh!»; }
|
||||||
|
```
|
||||||
|
|
||||||
|
Now, only the error message is displayed, making the output much more readable.
|
||||||
|
```
|
||||||
|
nix-repl> { err = builtins.throw "uh oh!"; }
|
||||||
|
{ err = «error: uh oh!»; }
|
||||||
|
```
|
||||||
|
|
||||||
|
However, if the whole expression being evaluated throws an error, source
|
||||||
|
locations and (if applicable) a stack trace are printed, just like you'd expect:
|
||||||
|
|
||||||
|
```
|
||||||
|
nix-repl> builtins.throw "uh oh!"
|
||||||
|
error:
|
||||||
|
… while calling the 'throw' builtin
|
||||||
|
at «string»:1:1:
|
||||||
|
1| builtins.throw "uh oh!"
|
||||||
|
| ^
|
||||||
|
|
||||||
|
error: uh oh!
|
||||||
|
```
|
||||||
|
|
||||||
|
Many thanks to [wiggles](https://git.lix.systems/rbt) for this.
|
||||||
|
- Show all FOD errors with `nix build --keep-going` [cl/1108](https://gerrit.lix.systems/c/lix/+/1108)
|
||||||
|
|
||||||
|
`nix build --keep-going` now behaves consistently with `nix-build --keep-going`. This means
|
||||||
|
that if e.g. multiple FODs fail to build, all hash mismatches are displayed.
|
||||||
|
|
||||||
|
Many thanks to [ma27](https://git.lix.systems/ma27) for this.
|
||||||
|
- Duplicate attribute reports are more accurate [cl/557](https://gerrit.lix.systems/c/lix/+/557)
|
||||||
|
|
||||||
|
Duplicate attribute errors are now more accurate, showing the path at which an error was detected rather than the full, possibly longer, path that caused the error.
|
||||||
|
Error reports are now
|
||||||
|
```ShellSession
|
||||||
|
$ nix eval --expr '{ a.b = 1; a.b.c.d = 1; }'
|
||||||
|
error: attribute 'a.b' already defined at «string»:1:3
|
||||||
|
at «string»:1:12:
|
||||||
|
1| { a.b = 1; a.b.c.d = 1;
|
||||||
|
| ^
|
||||||
|
```
|
||||||
|
instead of
|
||||||
|
```ShellSession
|
||||||
|
$ nix eval --expr '{ a.b = 1; a.b.c.d = 1; }'
|
||||||
|
error: attribute 'a.b.c.d' already defined at «string»:1:3
|
||||||
|
at «string»:1:12:
|
||||||
|
1| { a.b = 1; a.b.c.d = 1;
|
||||||
|
| ^
|
||||||
|
```
|
||||||
|
|
||||||
|
Many thanks to [eldritch horrors](https://git.lix.systems/pennae) for this.
|
||||||
|
- Reduce eval memory usage and wall time [#9658](https://github.com/NixOS/nix/pull/9658) [cl/207](https://gerrit.lix.systems/c/lix/+/207)
|
||||||
|
|
||||||
|
Reduce the size of the `Env` struct used in the evaluator by a pointer, or 8 bytes on most modern machines.
|
||||||
|
This reduces memory usage during eval by around 2% and wall time by around 3%.
|
||||||
|
|
||||||
|
Many thanks to [eldritch horrors](https://git.lix.systems/pennae) for this.
|
||||||
|
- Warn on unknown settings anywhere in the command line [#10701](https://github.com/NixOS/nix/pull/10701)
|
||||||
|
|
||||||
|
All `nix` commands will now properly warn when an unknown option is specified anywhere in the command line.
|
||||||
|
|
||||||
|
Before:
|
||||||
|
|
||||||
|
```console
|
||||||
|
$ nix-instantiate --option foobar baz --expr '{}'
|
||||||
|
warning: unknown setting 'foobar'
|
||||||
|
$ nix-instantiate '{}' --option foobar baz --expr
|
||||||
|
$ nix eval --expr '{}' --option foobar baz
|
||||||
|
{ }
|
||||||
|
```
|
||||||
|
|
||||||
|
After:
|
||||||
|
|
||||||
|
```console
|
||||||
|
$ nix-instantiate --option foobar baz --expr '{}'
|
||||||
|
warning: unknown setting 'foobar'
|
||||||
|
$ nix-instantiate '{}' --option foobar baz --expr
|
||||||
|
warning: unknown setting 'foobar'
|
||||||
|
$ nix eval --expr '{}' --option foobar baz
|
||||||
|
warning: unknown setting 'foobar'
|
||||||
|
{ }
|
||||||
|
```
|
||||||
|
|
||||||
|
Many thanks to [Cole Helbling](https://github.com/cole-h) for this.
|
||||||
|
- Nested debuggers are no longer supported [#9920](https://github.com/NixOS/nix/pull/9920)
|
||||||
|
|
||||||
|
Previously, evaluating an expression that throws an error in the debugger would
|
||||||
|
enter a second, nested debugger:
|
||||||
|
|
||||||
|
```
|
||||||
|
nix-repl> builtins.throw "what"
|
||||||
|
error: what
|
||||||
|
|
||||||
|
|
||||||
|
Starting REPL to allow you to inspect the current state of the evaluator.
|
||||||
|
|
||||||
|
Welcome to Nix 2.18.1. Type :? for help.
|
||||||
|
|
||||||
|
nix-repl>
|
||||||
|
```
|
||||||
|
|
||||||
|
Now, it just prints the error message like `nix repl`:
|
||||||
|
|
||||||
|
```
|
||||||
|
nix-repl> builtins.throw "what"
|
||||||
|
error:
|
||||||
|
… while calling the 'throw' builtin
|
||||||
|
at «string»:1:1:
|
||||||
|
1| builtins.throw "what"
|
||||||
|
| ^
|
||||||
|
|
||||||
|
error: what
|
||||||
|
```
|
||||||
|
|
||||||
|
Many thanks to [wiggles](https://git.lix.systems/rbt) for this.
|
||||||
|
- Find GC roots using libproc on Darwin [cl/723](https://gerrit.lix.systems/c/lix/+/723)
|
||||||
|
|
||||||
|
Previously, the garbage collector found runtime roots on Darwin by shelling out to `lsof -n -w -F n` then parsing the result. The version of `lsof` packaged in Nixpkgs is very slow on Darwin, so Lix now uses `libproc` directly to speed up GC root discovery, in some tests taking 250ms now instead of 40s.
|
||||||
|
|
||||||
|
Many thanks to [Artemis Tosini](https://git.lix.systems/artemist) for this.
|
||||||
|
- Increase default stack size on macOS [#9860](https://github.com/NixOS/nix/pull/9860)
|
||||||
|
|
||||||
|
Increase the default stack size on macOS to the same value as on Linux, subject to system restrictions to maximum stack size.
|
||||||
|
This should reduce the number of stack overflow crashes on macOS when evaluating Nix code with deep call stacks.
|
||||||
|
|
||||||
|
Many thanks to [wiggles](https://git.lix.systems/rbt) for this.
|
||||||
|
- Show more log context for failed builds [#9670](https://github.com/NixOS/nix/pull/9670)
|
||||||
|
|
||||||
|
Show 25 lines of log tail instead of 10 for failed builds.
|
||||||
|
This increases the chances of having useful information in the shown logs.
|
||||||
|
|
||||||
|
Many thanks to [DavHau](https://github.com/DavHau) for this.
|
||||||
|
- rename 'nix show-config' to 'nix config show' [#7672](https://github.com/NixOS/nix/issues/7672) [#9477](https://github.com/NixOS/nix/pull/9477) [cl/993](https://gerrit.lix.systems/c/lix/+/993)
|
||||||
|
|
||||||
|
`nix show-config` was renamed to `nix config show` to be more consistent with the rest of the command-line interface.
|
||||||
|
|
||||||
|
Running `nix show-config` will now print a deprecation warning saying to use `nix config show` instead.
|
||||||
|
|
||||||
|
Many thanks to [Théophane Hufschmitt](https://github.com/thufschmitt) and [ma27](https://git.lix.systems/ma27) for this.
|
||||||
|
- Print derivation paths in `nix eval` [cl/446](https://gerrit.lix.systems/c/lix/+/446)
|
||||||
|
|
||||||
|
`nix eval` previously printed derivations as attribute sets, so commands that print derivations (e.g. `nix eval nixpkgs#bash`) would infinitely loop and segfault.
|
||||||
|
It now prints the `.drv` path the derivation generates instead.
|
||||||
|
|
||||||
|
Many thanks to [wiggles](https://git.lix.systems/rbt) for this.
|
||||||
|
- Add an option `--unpack` to unpack archives in `nix store prefetch-file` [#9805](https://github.com/NixOS/nix/pull/9805) [cl/224](https://gerrit.lix.systems/c/lix/+/224)
|
||||||
|
|
||||||
|
It is now possible to fetch an archive then NAR-hash it (as in, hash it in the
|
||||||
|
same manner as `builtins.fetchTarball` or fixed-output derivations with
|
||||||
|
recursive hash type) in one command.
|
||||||
|
|
||||||
|
Example:
|
||||||
|
|
||||||
|
```
|
||||||
|
~ » nix store prefetch-file --name source --unpack https://git.lix.systems/lix-project/lix/archive/2.90-beta.1.tar.gz
|
||||||
|
Downloaded 'https://git.lix.systems/lix-project/lix/archive/2.90-beta.1.tar.gz' to '/nix/store/yvfqnq52ryjc3janw02ziv7kr6gd0cs1-source' (hash 'sha256-REWlo2RYHfJkxnmZTEJu3Cd/2VM+wjjpPy7Xi4BdDTQ=').
|
||||||
|
```
|
||||||
|
|
||||||
|
Many thanks to [yshui](https://github.com/yshui) and [eldritch horrors](https://git.lix.systems/pennae) for this.
|
||||||
|
- REPL printing improvements [#9931](https://github.com/NixOS/nix/pull/9931) [#10208](https://github.com/NixOS/nix/pull/10208) [cl/375](https://gerrit.lix.systems/c/lix/+/375) [cl/492](https://gerrit.lix.systems/c/lix/+/492)
|
||||||
|
|
||||||
|
The REPL printer has been improved to do the following:
|
||||||
|
- If a string is passed to `:print`, it is printed literally to the screen
|
||||||
|
- Structures will be printed as multiple lines when necessary
|
||||||
|
|
||||||
|
Before:
|
||||||
|
|
||||||
|
```
|
||||||
|
nix-repl> { attrs = { a = { b = { c = { }; }; }; }; list = [ 1 ]; list' = [ 1 2 3 ]; }
|
||||||
|
{ attrs = { ... }; list = [ ... ]; list' = [ ... ]; }
|
||||||
|
|
||||||
|
nix-repl> :p { attrs = { a = { b = { c = { }; }; }; }; list = [ 1 ]; list' = [ 1 2 3 ]; }
|
||||||
|
{ attrs = { a = { b = { c = { }; }; }; }; list = [ 1 ]; list' = [ 1 2 3 ]; }
|
||||||
|
|
||||||
|
nix-repl> :p "meow"
|
||||||
|
"meow"
|
||||||
|
```
|
||||||
|
|
||||||
|
After:
|
||||||
|
|
||||||
|
```
|
||||||
|
nix-repl> { attrs = { a = { b = { c = { }; }; }; }; list = [ 1 ]; list' = [ 1 2 3 ]; }
|
||||||
|
{
|
||||||
|
attrs = { ... };
|
||||||
|
list = [ ... ];
|
||||||
|
list' = [ ... ];
|
||||||
|
}
|
||||||
|
|
||||||
|
nix-repl> :p { attrs = { a = { b = { c = { }; }; }; }; list = [ 1 ]; list' = [ 1 2 3 ]; }
|
||||||
|
{
|
||||||
|
attrs = {
|
||||||
|
a = {
|
||||||
|
b = {
|
||||||
|
c = { };
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
list = [ 1 ];
|
||||||
|
list' = [
|
||||||
|
1
|
||||||
|
2
|
||||||
|
3
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
nix-repl> :p "meow"
|
||||||
|
meow
|
||||||
|
```
|
||||||
|
|
||||||
|
Many thanks to [wiggles](https://git.lix.systems/rbt) and [eldritch horrors](https://git.lix.systems/pennae) for this.
|
||||||
|
- Coercion errors include the failing value [#561](https://github.com/NixOS/nix/issues/561) [#9754](https://github.com/NixOS/nix/pull/9754)
|
||||||
|
|
||||||
|
The `error: cannot coerce a <TYPE> to a string` message now includes the value
|
||||||
|
which caused the error.
|
||||||
|
|
||||||
|
Before:
|
||||||
|
|
||||||
|
```
|
||||||
|
error: cannot coerce a set to a string
|
||||||
|
```
|
||||||
|
|
||||||
|
After:
|
||||||
|
|
||||||
|
```
|
||||||
|
error: cannot coerce a set to a string: { aesSupport = «thunk»;
|
||||||
|
avx2Support = «thunk»; avx512Support = «thunk»; avxSupport = «thunk»;
|
||||||
|
canExecute = «thunk»; config = «thunk»; darwinArch = «thunk»; darwinMinVersion
|
||||||
|
= «thunk»; darwinMinVersionVariable = «thunk»; darwinPlatform = «thunk»; «84
|
||||||
|
attributes elided»}
|
||||||
|
```
|
||||||
|
|
||||||
|
Many thanks to [wiggles](https://git.lix.systems/rbt) and [eldritch horrors](https://git.lix.systems/pennae) for this.
|
||||||
|
- New-cli flake commands that expect derivations now print the failing value and its type [cl/1177](https://gerrit.lix.systems/c/lix/+/1177)
|
||||||
|
|
||||||
|
In errors like `flake output attribute 'legacyPackages.x86_64-linux.lib' is not a derivation or path`, the message now includes the failing value and type.
|
||||||
|
|
||||||
|
Before:
|
||||||
|
|
||||||
|
```
|
||||||
|
error: flake output attribute 'nixosConfigurations.yuki.config' is not a derivation or path
|
||||||
|
````
|
||||||
|
|
||||||
|
After:
|
||||||
|
|
||||||
|
```
|
||||||
|
error: expected flake output attribute 'nixosConfigurations.yuki.config' to be a derivation or path but found a set: { appstream = «thunk»; assertions = «thunk»; boot = { bcache = «thunk»; binfmt = «thunk»; binfmtMiscRegistrations = «thunk»; blacklistedKernelModules = «thunk»; bootMount = «thunk»; bootspec = «thunk»; cleanTmpDir = «thunk»; consoleLogLevel = «thunk»; «43 attributes elided» }; «48 attributes elided» }
|
||||||
|
```
|
||||||
|
|
||||||
|
Many thanks to [Qyriad](https://git.lix.systems/Qyriad) for this.
|
||||||
|
- Type errors include the failing value [#561](https://github.com/NixOS/nix/issues/561) [#9753](https://github.com/NixOS/nix/pull/9753)
|
||||||
|
|
||||||
|
In errors like `value is an integer while a list was expected`, the message now
|
||||||
|
includes the failing value.
|
||||||
|
|
||||||
|
Before:
|
||||||
|
|
||||||
|
```
|
||||||
|
error: value is a set while a string was expected
|
||||||
|
```
|
||||||
|
|
||||||
|
After:
|
||||||
|
|
||||||
|
```
|
||||||
|
error: expected a string but found a set: { ghc810 = «thunk»;
|
||||||
|
ghc8102Binary = «thunk»; ghc8107 = «thunk»; ghc8107Binary = «thunk»;
|
||||||
|
ghc865Binary = «thunk»; ghc90 = «thunk»; ghc902 = «thunk»; ghc92 = «thunk»;
|
||||||
|
ghc924Binary = «thunk»; ghc925 = «thunk»; «17 attributes elided»}
|
||||||
|
```
|
||||||
|
|
||||||
|
Many thanks to [wiggles](https://git.lix.systems/rbt) and [eldritch horrors](https://git.lix.systems/pennae) for this.
|
||||||
|
- Visual clutter in `--debugger` is reduced [#9919](https://github.com/NixOS/nix/pull/9919)
|
||||||
|
|
||||||
|
Before:
|
||||||
|
```
|
||||||
|
info: breakpoint reached
|
||||||
|
|
||||||
|
|
||||||
|
Starting REPL to allow you to inspect the current state of the evaluator.
|
||||||
|
|
||||||
|
Welcome to Nix 2.20.0pre20231222_dirty. Type :? for help.
|
||||||
|
|
||||||
|
nix-repl> :continue
|
||||||
|
error: uh oh
|
||||||
|
|
||||||
|
|
||||||
|
Starting REPL to allow you to inspect the current state of the evaluator.
|
||||||
|
|
||||||
|
Welcome to Nix 2.20.0pre20231222_dirty. Type :? for help.
|
||||||
|
|
||||||
|
nix-repl>
|
||||||
|
```
|
||||||
|
|
||||||
|
After:
|
||||||
|
|
||||||
|
```
|
||||||
|
info: breakpoint reached
|
||||||
|
|
||||||
|
Nix 2.20.0pre20231222_dirty debugger
|
||||||
|
Type :? for help.
|
||||||
|
nix-repl> :continue
|
||||||
|
error: uh oh
|
||||||
|
|
||||||
|
nix-repl>
|
||||||
|
```
|
||||||
|
|
||||||
|
Many thanks to [wiggles](https://git.lix.systems/rbt) and [eldritch horrors](https://git.lix.systems/pennae) for this.
|
||||||
|
- REPL now supports CTRL+Z to suspend
|
||||||
|
|
||||||
|
Editline is now built with SIGTSTP support, so now typing CTRL+Z in the REPL will suspend the REPL and allow it to be resumed later or backgrounded.
|
||||||
|
|
||||||
|
Many thanks to [Qyriad](https://git.lix.systems/Qyriad) for this.
|
||||||
|
- Allow single quotes in nix-shell shebangs [#8470](https://github.com/NixOS/nix/pull/8470)
|
||||||
|
|
||||||
|
Example:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
#! /usr/bin/env nix-shell
|
||||||
|
#! nix-shell -i bash --packages 'terraform.withPlugins (plugins: [ plugins.openstack ])'
|
||||||
|
```
|
||||||
|
|
||||||
|
Many thanks to [ncfavier](https://github.com/ncfavier) and [eldritch horrors](https://git.lix.systems/pennae) for this.
|
||||||
|
- reintroduce shortened `-E` form for `--expr` to new CLI [cl/605](https://gerrit.lix.systems/c/lix/+/605)
|
||||||
|
|
||||||
|
In the old CLI, it was possible to supply a shorter `-E` flag instead of fully
|
||||||
|
specifying `--expr` every time you wished to provide an expression that would
|
||||||
|
be evaluated to produce the given command's input. This was retained for the
|
||||||
|
`--file` flag when the new CLI utilities were written with `-f`, but `-E` was
|
||||||
|
dropped.
|
||||||
|
|
||||||
|
We now restore the `-E` short form for better UX. This is most useful for
|
||||||
|
`nix eval` but most any command that takes an Installable argument should benefit
|
||||||
|
from it as well.
|
||||||
|
|
||||||
|
Many thanks to [Lunaphied](https://git.lix.systems/Lunaphied) for this.
|
||||||
|
- Source locations are printed more consistently in errors [#561](https://github.com/NixOS/nix/issues/561) [#9555](https://github.com/NixOS/nix/pull/9555)
|
||||||
|
|
||||||
|
Source location information is now included in error messages more
|
||||||
|
consistently. Given this code:
|
||||||
|
|
||||||
|
```nix
|
||||||
|
let
|
||||||
|
attr = {foo = "bar";};
|
||||||
|
key = {};
|
||||||
|
in
|
||||||
|
attr.${key}
|
||||||
|
```
|
||||||
|
|
||||||
|
Previously, Nix would show this unhelpful message when attempting to evaluate
|
||||||
|
it:
|
||||||
|
|
||||||
|
```
|
||||||
|
error:
|
||||||
|
… while evaluating an attribute name
|
||||||
|
|
||||||
|
error: value is a set while a string was expected
|
||||||
|
```
|
||||||
|
|
||||||
|
Now, the error message displays where the problematic value was found:
|
||||||
|
|
||||||
|
```
|
||||||
|
error:
|
||||||
|
… while evaluating an attribute name
|
||||||
|
|
||||||
|
at bad.nix:4:11:
|
||||||
|
|
||||||
|
3| key = {};
|
||||||
|
4| in attr.${key}
|
||||||
|
| ^
|
||||||
|
5|
|
||||||
|
|
||||||
|
error: expected a string but found a set: { }
|
||||||
|
```
|
||||||
|
|
||||||
|
Many thanks to [wiggles](https://git.lix.systems/rbt) and [eldritch horrors](https://git.lix.systems/pennae) for this.
|
||||||
|
- Some stack overflow segfaults are fixed [#9616](https://github.com/NixOS/nix/issues/9616) [#9617](https://github.com/NixOS/nix/pull/9617) [cl/205](https://gerrit.lix.systems/c/lix/+/205)
|
||||||
|
|
||||||
|
The number of nested function calls has been restricted, to detect and report
|
||||||
|
infinite function call recursions. The default maximum call depth is 10,000 and
|
||||||
|
can be set with [the `max-call-depth`
|
||||||
|
option](@docroot@/command-ref/conf-file.md#conf-max-call-depth).
|
||||||
|
|
||||||
|
This fixes segfaults or the following unhelpful error message in many cases:
|
||||||
|
|
||||||
|
error: stack overflow (possible infinite recursion)
|
||||||
|
|
||||||
|
Before:
|
||||||
|
|
||||||
|
```
|
||||||
|
$ nix-instantiate --eval --expr '(x: x x) (x: x x)'
|
||||||
|
Segmentation fault: 11
|
||||||
|
```
|
||||||
|
|
||||||
|
After:
|
||||||
|
|
||||||
|
```
|
||||||
|
$ nix-instantiate --eval --expr '(x: x x) (x: x x)'
|
||||||
|
error: stack overflow
|
||||||
|
|
||||||
|
at «string»:1:14:
|
||||||
|
1| (x: x x) (x: x x)
|
||||||
|
| ^
|
||||||
|
```
|
||||||
|
|
||||||
|
Many thanks to [wiggles](https://git.lix.systems/rbt) and [eldritch horrors](https://git.lix.systems/pennae) for this.
|
||||||
|
- Warn about ignored client settings [cl/1026](https://gerrit.lix.systems/c/lix/+/1026)
|
||||||
|
|
||||||
|
Emit a warning for every client-provided setting the daemon ignores because the requesting client is not run by a trusted user.
|
||||||
|
Previously this was only a debug message.
|
||||||
|
|
||||||
|
Many thanks to [jade](https://git.lix.systems/jade) for this.
|
||||||
|
- Better error reporting for `with` expressions [#9658](https://github.com/NixOS/nix/pull/9658) [cl/207](https://gerrit.lix.systems/c/lix/+/207)
|
||||||
|
|
||||||
|
`with` expressions using non-attrset values to resolve variables are now reported with proper positions.
|
||||||
|
|
||||||
|
Previously an incorrect `with` expression would report no position at all, making it hard to determine where the error originated:
|
||||||
|
|
||||||
|
```
|
||||||
|
nix-repl> with 1; a
|
||||||
|
error:
|
||||||
|
… <borked>
|
||||||
|
|
||||||
|
at «none»:0: (source not available)
|
||||||
|
|
||||||
|
error: value is an integer while a set was expected
|
||||||
|
```
|
||||||
|
|
||||||
|
Now position information is preserved and reported as with most other errors:
|
||||||
|
|
||||||
|
```
|
||||||
|
nix-repl> with 1; a
|
||||||
|
error:
|
||||||
|
… while evaluating the first subexpression of a with expression
|
||||||
|
at «string»:1:1:
|
||||||
|
1| with 1; a
|
||||||
|
| ^
|
||||||
|
|
||||||
|
error: expected a set but found an integer: 1
|
||||||
|
```
|
||||||
|
|
||||||
|
Many thanks to [eldritch horrors](https://git.lix.systems/pennae) for this.
|
||||||
|
|
||||||
|
## Fixes
|
||||||
|
- Fix nested flake input `follows` [#6621](https://github.com/NixOS/nix/pull/6621) [cl/994](https://gerrit.lix.systems/c/lix/+/994)
|
||||||
|
|
||||||
|
Previously nested-input overrides were ignored; that is, the following did not
|
||||||
|
override anything, in spite of the `nix3-flake` manual documenting it working:
|
||||||
|
|
||||||
|
```
|
||||||
|
{
|
||||||
|
inputs = {
|
||||||
|
foo.url = "github:bar/foo";
|
||||||
|
foo.inputs.bar.inputs.nixpkgs = "nixpkgs";
|
||||||
|
};
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
This is useful to avoid the 1000 instances of nixpkgs problem without having
|
||||||
|
each flake in the dependency tree to expose all of its transitive dependencies
|
||||||
|
for modification.
|
||||||
|
|
||||||
|
Many thanks to [Kha](https://github.com/Kha) and [ma27](https://git.lix.systems/ma27) for this.
|
||||||
|
- Fix CVE-2024-27297 (GHSA-2ffj-w4mj-pg37) [cl/266](https://gerrit.lix.systems/c/lix/+/266)
|
||||||
|
|
||||||
|
Since Lix fixed-output derivations run in the host network namespace (which we
|
||||||
|
wish to change in the future, see
|
||||||
|
[lix#285](https://git.lix.systems/lix-project/lix/issues/285)), they may open
|
||||||
|
abstract-namespace Unix sockets to each other and to programs on the host. Lix
|
||||||
|
contained a now-fixed time-of-check/time-of-use vulnerability where one
|
||||||
|
derivation could send writable handles to files in their final location in the
|
||||||
|
store to another over an abstract-namespace Unix socket, exit, then the other
|
||||||
|
derivation could wait for Lix to hash the paths and overwrite them.
|
||||||
|
|
||||||
|
The impact of this vulnerability is that two malicious fixed-output derivations
|
||||||
|
could create a poisoned path for the sources to Bash or similarly important
|
||||||
|
software containing a backdoor, leading to local privilege execution.
|
||||||
|
|
||||||
|
CppNix advisory: https://github.com/NixOS/nix/security/advisories/GHSA-2ffj-w4mj-pg37
|
||||||
|
|
||||||
|
Many thanks to [puck](https://git.lix.systems/puck), [jade](https://git.lix.systems/jade), [Théophane Hufschmitt](https://github.com/thufschmitt), [Tom Bereknyei](https://github.com/tomberek), and [Valentin Gagarin](https://github.com/fricklerhandwerk) for this.
|
||||||
|
- `--debugger` can now access bindings from `let` expressions [#8827](https://github.com/NixOS/nix/issues/8827) [#9918](https://github.com/NixOS/nix/pull/9918)
|
||||||
|
|
||||||
|
Breakpoints and errors in the bindings of a `let` expression can now access
|
||||||
|
those bindings in the debugger. Previously, only the body of `let` expressions
|
||||||
|
could access those bindings.
|
||||||
|
|
||||||
|
Many thanks to [wiggles](https://git.lix.systems/rbt) for this.
|
||||||
|
- Fix handling of truncated `.drv` files. [#9673](https://github.com/NixOS/nix/pull/9673)
|
||||||
|
|
||||||
|
Previously a `.drv` that was truncated in the middle of a string would case nix to enter an infinite loop, eventually exhausting all memory and crashing.
|
||||||
|
|
||||||
|
Many thanks to [eldritch horrors](https://git.lix.systems/pennae) for this.
|
||||||
|
- The `--debugger` will start more reliably in `let` expressions and function calls [#6649](https://github.com/NixOS/nix/issues/6649) [#9917](https://github.com/NixOS/nix/pull/9917)
|
||||||
|
|
||||||
|
Previously, if you attempted to evaluate this file with the debugger:
|
||||||
|
|
||||||
|
```nix
|
||||||
|
let
|
||||||
|
a = builtins.trace "before inner break" (
|
||||||
|
builtins.break "hello"
|
||||||
|
);
|
||||||
|
b = builtins.trace "before outer break" (
|
||||||
|
builtins.break a
|
||||||
|
);
|
||||||
|
in
|
||||||
|
b
|
||||||
|
```
|
||||||
|
|
||||||
|
Lix would correctly enter the debugger at `builtins.break a`, but if you asked
|
||||||
|
it to `:continue`, it would skip over the `builtins.break "hello"` expression
|
||||||
|
entirely.
|
||||||
|
|
||||||
|
Now, Lix will correctly enter the debugger at both breakpoints.
|
||||||
|
|
||||||
|
Many thanks to [wiggles](https://git.lix.systems/rbt) and [eldritch horrors](https://git.lix.systems/pennae) for this.
|
||||||
|
- Creating setuid/setgid binaries with fchmodat2 is now prohibited by the build sandbox [#10501](https://github.com/NixOS/nix/pull/10501)
|
||||||
|
|
||||||
|
The build sandbox blocks any attempt to create setuid/setgid binaries, but didn't check
|
||||||
|
for the use of the `fchmodat2` syscall which was introduced in Linux 6.6 and is used by
|
||||||
|
glibc >=2.39. This is fixed now.
|
||||||
|
|
||||||
|
Many thanks to [ma27](https://git.lix.systems/ma27) for this.
|
||||||
|
- consistent order of lambda formals in printed expressions [#9874](https://github.com/NixOS/nix/pull/9874)
|
||||||
|
|
||||||
|
Always print lambda formals in lexicographic order rather than the internal, creation-time based symbol order.
|
||||||
|
This makes printed formals independent of the context they appear in.
|
||||||
|
|
||||||
|
Many thanks to [eldritch horrors](https://git.lix.systems/pennae) for this.
|
||||||
|
- fix duplicate attribute error positions for `inherit` [#9874](https://github.com/NixOS/nix/pull/9874)
|
||||||
|
|
||||||
|
When an inherit caused a duplicate attribute error, the position of the error was not reported correctly, placing the error with the inherit itself or at the start of the bindings block instead of the offending attribute name.
|
||||||
|
|
||||||
|
Many thanks to [eldritch horrors](https://git.lix.systems/pennae) for this.
|
||||||
|
- `inherit (x) ...` evaluates `x` only once [#9847](https://github.com/NixOS/nix/pull/9847)
|
||||||
|
|
||||||
|
`inherit (x) a b ...` now evaluates the expression `x` only once for all inherited attributes rather than once for each inherited attribute.
|
||||||
|
This does not usually have a measurable impact, but side-effects (such as `builtins.trace`) would be duplicated and expensive expressions (such as derivations) could cause a measurable slowdown.
|
||||||
|
|
||||||
|
Many thanks to [eldritch horrors](https://git.lix.systems/pennae) for this.
|
||||||
|
- Store paths are allowed to start with `.` [#912](https://github.com/NixOS/nix/issues/912) [#9867](https://github.com/NixOS/nix/pull/9867) [#9091](https://github.com/NixOS/nix/pull/9091) [#9095](https://github.com/NixOS/nix/pull/9095) [#9120](https://github.com/NixOS/nix/pull/9120) [#9121](https://github.com/NixOS/nix/pull/9121) [#9122](https://github.com/NixOS/nix/pull/9122) [#9130](https://github.com/NixOS/nix/pull/9130) [#9219](https://github.com/NixOS/nix/pull/9219) [#9224](https://github.com/NixOS/nix/pull/9224)
|
||||||
|
|
||||||
|
Leading periods were allowed by accident in Nix 2.4. The Nix team has considered this to be a bug, but this behavior has since been relied on by users, leading to unnecessary difficulties.
|
||||||
|
From now on, leading periods are officially, definitively supported. The names `.` and `..` are disallowed, as well as those starting with `.-` or `..-`.
|
||||||
|
|
||||||
|
Nix versions that denied leading periods are documented [in the issue](https://github.com/NixOS/nix/issues/912#issuecomment-1919583286).
|
||||||
|
|
||||||
|
Many thanks to [Robert Hensing](https://github.com/roberth) and [eldritch horrors](https://git.lix.systems/pennae) for this.
|
||||||
|
- Fix `nix-env --query --drv-path --json` [#9257](https://github.com/NixOS/nix/pull/9257)
|
||||||
|
|
||||||
|
Fixed a bug where `nix-env --query` ignored `--drv-path` when `--json` was set.
|
||||||
|
|
||||||
|
Many thanks to [Artturin](https://github.com/Artturin) and [eldritch horrors](https://git.lix.systems/pennae) for this.
|
||||||
|
- re-evaluate cached evaluation errors [cl/771](https://gerrit.lix.systems/c/lix/+/771)
|
||||||
|
|
||||||
|
"cached failure of [expr]" errors have been removed: expressions already in the
|
||||||
|
eval cache as a failure will now simply be re-evaluated, removing the need to
|
||||||
|
set `--no-eval-cache` or similar to see the error.
|
||||||
|
|
||||||
|
Many thanks to [Qyriad](https://git.lix.systems/Qyriad) for this.
|
||||||
|
- Interrupting builds in the REPL works more than once [cl/1097](https://gerrit.lix.systems/c/lix/+/1097)
|
||||||
|
|
||||||
|
Builds in the REPL can be interrupted by pressing Ctrl+C.
|
||||||
|
Previously, this only worked once per REPL session; further attempts would be ignored.
|
||||||
|
This issue is now fixed, so that builds can be canceled consistently.
|
||||||
|
|
||||||
|
Many thanks to [alois31](https://git.lix.systems/alois31) for this.
|
||||||
|
- In the debugger, `while evaluating the attribute` errors now include position information [#9915](https://github.com/NixOS/nix/pull/9915)
|
||||||
|
|
||||||
|
Before:
|
||||||
|
|
||||||
|
```
|
||||||
|
0: while evaluating the attribute 'python311.pythonForBuild.pkgs'
|
||||||
|
0x600001522598
|
||||||
|
```
|
||||||
|
|
||||||
|
After:
|
||||||
|
|
||||||
|
```
|
||||||
|
0: while evaluating the attribute 'python311.pythonForBuild.pkgs'
|
||||||
|
/nix/store/hg65h51xnp74ikahns9hyf3py5mlbbqq-source/overrides/default.nix:132:27
|
||||||
|
|
||||||
|
131|
|
||||||
|
132| bootstrappingBase = pkgs.${self.python.pythonAttr}.pythonForBuild.pkgs;
|
||||||
|
| ^
|
||||||
|
133| in
|
||||||
|
```
|
||||||
|
|
||||||
|
Many thanks to [wiggles](https://git.lix.systems/rbt) for this.
|
||||||
|
- Include phase reporting in log file for ssh-ng builds [#9280](https://github.com/NixOS/nix/pull/9280)
|
||||||
|
|
||||||
|
Store phase information of remote builds run via `ssh-ng` remotes in the local log file, matching logging behavior of local builds.
|
||||||
|
|
||||||
|
Many thanks to [r-vdp](https://github.com/r-vdp) for this.
|
||||||
|
- Fix `ssh-ng://` remotes not respecting `--substitute-on-destination` [#9600](https://github.com/NixOS/nix/pull/9600)
|
||||||
|
|
||||||
|
`nix copy ssh-ng://` now respects `--substitute-on-destination`, as does `nix-copy-closure` and other commands that operate on remote `ssh-ng` stores.
|
||||||
|
Previously this was always set by `builders-use-substitutes` setting.
|
||||||
|
|
||||||
|
Many thanks to [SharzyL](https://github.com/SharzyL) for this.
|
||||||
|
- using `nix profile` on `/nix/var/nix/profiles/default` no longer breaks `nix upgrade-nix` [cl/952](https://gerrit.lix.systems/c/lix/+/952)
|
||||||
|
|
||||||
|
On non-NixOS, Nix is conventionally installed into a `nix-env` style profile at /nix/var/nix/profiles/default.
|
||||||
|
Like any `nix-env` profile, using `nix profile` on it automatically migrates it to a `nix profile` style profile, which is incompatible with `nix-env`.
|
||||||
|
`nix upgrade-nix` previously relied solely on `nix-env` to do the upgrade, but now will work fine with either kind of profile.
|
||||||
|
|
||||||
|
Many thanks to [Qyriad](https://git.lix.systems/Qyriad) for this.
|
||||||
|
|
||||||
|
## Packaging
|
||||||
|
- Lix turns more internal bugs into crashes [cl/797](https://gerrit.lix.systems/c/lix/+/797) [cl/626](https://gerrit.lix.systems/c/lix/+/626)
|
||||||
|
|
||||||
|
Lix now enables build options such as trapping on signed overflow and enabling
|
||||||
|
libstdc++ assertions by default. These may find new bugs in Lix, which will
|
||||||
|
present themselves as Lix processes aborting, potentially without an error
|
||||||
|
message.
|
||||||
|
|
||||||
|
If Lix processes abort on your machine, this is a bug. Please file a bug,
|
||||||
|
ideally with the core dump (or information from it).
|
||||||
|
|
||||||
|
On Linux, run `coredumpctl list`, find the crashed process's PID at
|
||||||
|
the bottom of the list, then run `coredumpctl info THE-PID`. You can then paste
|
||||||
|
the output into a bug report.
|
||||||
|
|
||||||
|
On macOS, open the Console app from Applications/Utilities, select Crash
|
||||||
|
Reports, select the crash report in question. Right click on it, select Open In
|
||||||
|
Finder, then include that file in your bug report. [See the Apple
|
||||||
|
documentation][apple-crashreport] for more details.
|
||||||
|
|
||||||
|
[apple-crashreport]: https://developer.apple.com/documentation/xcode/acquiring-crash-reports-and-diagnostic-logs#Locate-crash-reports-and-memory-logs-on-the-device
|
||||||
|
|
||||||
|
Many thanks to [jade](https://git.lix.systems/jade) for this.
|
||||||
|
- Stop vendoring toml11 [cl/675](https://gerrit.lix.systems/c/lix/+/675)
|
||||||
|
|
||||||
|
We don't apply any patches to it, and vendoring it locks users into
|
||||||
|
bugs (it hasn't been updated since its introduction in late 2021).
|
||||||
|
|
||||||
|
Many thanks to [winter](https://git.lix.systems/winter) for this.
|
||||||
|
- Lix is built with meson [cl/580](https://gerrit.lix.systems/c/lix/+/580) [cl/627](https://gerrit.lix.systems/c/lix/+/627) [cl/628](https://gerrit.lix.systems/c/lix/+/628) [cl/707](https://gerrit.lix.systems/c/lix/+/707) [cl/711](https://gerrit.lix.systems/c/lix/+/711) [cl/712](https://gerrit.lix.systems/c/lix/+/712) [cl/719](https://gerrit.lix.systems/c/lix/+/719)
|
||||||
|
|
||||||
|
Lix is built exclusively with the meson build system thanks to a huge team-wide
|
||||||
|
effort, and the legacy `make`/`autoconf` based build system has been removed
|
||||||
|
altogether. This improves maintainability of Lix, enables things like saving
|
||||||
|
20% of compile times with precompiled headers, and generally makes the build
|
||||||
|
less able to produce obscure incremental compilation bugs.
|
||||||
|
|
||||||
|
Non-Nix-based downstream packaging needs rewriting accordingly.
|
||||||
|
|
||||||
|
Many thanks to [Qyriad](https://git.lix.systems/Qyriad), [eldritch horrors](https://git.lix.systems/pennae), [jade](https://git.lix.systems/jade), [wiggles](https://git.lix.systems/rbt), and [winter](https://git.lix.systems/winter) for this.
|
||||||
|
- Upstart scripts removed [cl/574](https://gerrit.lix.systems/c/lix/+/574)
|
||||||
|
|
||||||
|
Upstart scripts have been removed from Lix, since Upstart is obsolete and has
|
||||||
|
not been shipped by any major distributions for many years. If these are
|
||||||
|
necessary to your use case, please back port them to your packaging.
|
||||||
|
|
||||||
|
Many thanks to [jade](https://git.lix.systems/jade) for this.
|
||||||
|
|
||||||
|
## Development
|
||||||
|
- Clang build timing analysis [cl/587](https://gerrit.lix.systems/c/lix/+/587)
|
||||||
|
|
||||||
|
We now have Clang build profiling available, which generates Chrome
|
||||||
|
tracing files for each compilation unit. To enable it, run `meson configure
|
||||||
|
build -Dprofile-build=enabled` in a Clang stdenv (`nix develop
|
||||||
|
.#native-clangStdenvPackages`) then rerun the compilation.
|
||||||
|
|
||||||
|
If you want to make the build go faster, do a clang build with meson, then run
|
||||||
|
`maintainers/buildtime_report.sh build`, then contemplate how to improve the
|
||||||
|
build time.
|
||||||
|
|
||||||
|
You can also look at individual object files' traces in
|
||||||
|
<https://ui.perfetto.dev>.
|
||||||
|
|
||||||
|
See [the wiki page][improving-build-times-wiki] for more details on how to do
|
||||||
|
this.
|
||||||
|
|
||||||
|
[improving-build-times-wiki]: https://wiki.lix.systems/link/8#bkmrk-page-title
|
||||||
|
|
||||||
|
## Miscellany
|
||||||
|
- Disallow empty search regex in `nix search` [#9481](https://github.com/NixOS/nix/pull/9481)
|
||||||
|
|
||||||
|
[`nix search`](@docroot@/command-ref/new-cli/nix3-search.md) now requires a search regex to be passed. To show all packages, use `^`.
|
||||||
|
|
||||||
|
Many thanks to [iFreilicht](https://github.com/iFreilicht) and [eldritch horrors](https://git.lix.systems/pennae) for this.
|
||||||
|
- `nix repl` history is saved more reliably [cl/1164](https://gerrit.lix.systems/c/lix/+/1164)
|
||||||
|
|
||||||
|
`nix repl` now saves its history file after each line, rather than at the end
|
||||||
|
of the session; ensuring that it will remember what you typed even after it
|
||||||
|
crashes.
|
||||||
|
|
||||||
|
Many thanks to [puck](https://git.lix.systems/puck) for this.
|
|
@ -164,6 +164,7 @@
|
||||||
nixUnstable = prev.nixUnstable;
|
nixUnstable = prev.nixUnstable;
|
||||||
|
|
||||||
check-headers = final.buildPackages.callPackage ./maintainers/check-headers.nix { };
|
check-headers = final.buildPackages.callPackage ./maintainers/check-headers.nix { };
|
||||||
|
check-syscalls = final.buildPackages.callPackage ./maintainers/check-syscalls.nix { };
|
||||||
|
|
||||||
default-busybox-sandbox-shell = final.busybox.override {
|
default-busybox-sandbox-shell = final.busybox.override {
|
||||||
useMusl = true;
|
useMusl = true;
|
||||||
|
@ -233,6 +234,11 @@
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
|
# Completion tests for the Nix REPL.
|
||||||
|
repl-completion = forAllSystems (
|
||||||
|
system: nixpkgsFor.${system}.native.callPackage ./tests/repl-completion.nix { }
|
||||||
|
);
|
||||||
|
|
||||||
# Perl bindings for various platforms.
|
# Perl bindings for various platforms.
|
||||||
perlBindings = forAllSystems (system: nixpkgsFor.${system}.native.nix.passthru.perl-bindings);
|
perlBindings = forAllSystems (system: nixpkgsFor.${system}.native.nix.passthru.perl-bindings);
|
||||||
|
|
||||||
|
@ -330,6 +336,7 @@
|
||||||
rl-next = self.hydraJobs.rl-next.${system}.user;
|
rl-next = self.hydraJobs.rl-next.${system}.user;
|
||||||
# Will be empty attr set on i686-linux, and filtered out by forAvailableSystems.
|
# Will be empty attr set on i686-linux, and filtered out by forAvailableSystems.
|
||||||
pre-commit = self.hydraJobs.pre-commit.${system};
|
pre-commit = self.hydraJobs.pre-commit.${system};
|
||||||
|
repl-completion = self.hydraJobs.repl-completion.${system};
|
||||||
}
|
}
|
||||||
// (lib.optionalAttrs (builtins.elem system linux64BitSystems)) {
|
// (lib.optionalAttrs (builtins.elem system linux64BitSystems)) {
|
||||||
dockerImage = self.hydraJobs.dockerImage.${system};
|
dockerImage = self.hydraJobs.dockerImage.${system};
|
||||||
|
|
16
maintainers/check-syscalls.nix
Normal file
16
maintainers/check-syscalls.nix
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
{
|
||||||
|
runCommandNoCC,
|
||||||
|
lib,
|
||||||
|
libseccomp,
|
||||||
|
writeShellScriptBin,
|
||||||
|
}:
|
||||||
|
let
|
||||||
|
syscalls-csv = runCommandNoCC "syscalls.csv" { } ''
|
||||||
|
echo ${lib.escapeShellArg libseccomp.src}
|
||||||
|
tar -xf ${lib.escapeShellArg libseccomp.src} --strip-components=2 ${libseccomp.name}/src/syscalls.csv
|
||||||
|
mv syscalls.csv "$out"
|
||||||
|
'';
|
||||||
|
in
|
||||||
|
writeShellScriptBin "check-syscalls" ''
|
||||||
|
${./check-syscalls.sh} ${syscalls-csv}
|
||||||
|
''
|
7
maintainers/check-syscalls.sh
Executable file
7
maintainers/check-syscalls.sh
Executable file
|
@ -0,0 +1,7 @@
|
||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
set -e
|
||||||
|
|
||||||
|
diff -u <(awk < src/libstore/build/local-derivation-goal.cc '/BEGIN extract-syscalls/ { extracting = 1; next }
|
||||||
|
match($0, /allowSyscall\(ctx, SCMP_SYS\(([^)]*)\)\);|\/\/ skip ([^ ]*)/, result) { print result[1] result[2] }
|
||||||
|
/END extract-syscalls/ { extracting = 0; next }') <(tail -n+2 "$1" | cut -d, -f 1)
|
|
@ -1,179 +0,0 @@
|
||||||
#!/usr/bin/env nix-shell
|
|
||||||
#!nix-shell -i bash ../shell.nix -I nixpkgs=channel:nixos-unstable-small
|
|
||||||
# ^^^^^^^
|
|
||||||
# Only used for bash. shell.nix goes to the flake.
|
|
||||||
|
|
||||||
# --- CONFIGURATION ---
|
|
||||||
|
|
||||||
# This does double duty for
|
|
||||||
# - including rl-next
|
|
||||||
# - marking where to insert new links (right after)
|
|
||||||
SUMMARY_MARKER_LINE='release-notes/rl-next.md'
|
|
||||||
|
|
||||||
# --- LIB ---
|
|
||||||
|
|
||||||
log() {
|
|
||||||
echo 1>&2 "release-notes:" "$@"
|
|
||||||
}
|
|
||||||
logcmd() {
|
|
||||||
local cmd="$1"
|
|
||||||
shift
|
|
||||||
logcmd2 "$cmd" "${*@Q}" "$cmd" "$@"
|
|
||||||
}
|
|
||||||
logcmd2() {
|
|
||||||
local fakecmd="$1"
|
|
||||||
local fakeargs="$2"
|
|
||||||
shift
|
|
||||||
shift
|
|
||||||
printf 1>&2 "release-notes: \033[34;1m$fakecmd\033[0m "
|
|
||||||
echo "$fakeargs" 1>&2
|
|
||||||
"$@"
|
|
||||||
}
|
|
||||||
die() {
|
|
||||||
# ANSI red
|
|
||||||
printf 1>&2 "release-notes: \033[31;1merror:\033[0m"
|
|
||||||
echo 1>&2 "" "$@"
|
|
||||||
exit 1
|
|
||||||
}
|
|
||||||
confirm() {
|
|
||||||
local answer
|
|
||||||
echo 1>&2 "$@" "[y/n]"
|
|
||||||
read -r answer
|
|
||||||
case "$answer" in
|
|
||||||
y|Y|yes|Yes|YES)
|
|
||||||
return 0
|
|
||||||
;;
|
|
||||||
n|N|no|No|NO)
|
|
||||||
return 1
|
|
||||||
;;
|
|
||||||
*)
|
|
||||||
echo 1>&2 "please answer y or n"
|
|
||||||
confirm "$@"
|
|
||||||
;;
|
|
||||||
esac
|
|
||||||
}
|
|
||||||
report_done() {
|
|
||||||
logcmd2 "git" "show" git -c pager.show=false show
|
|
||||||
printf 1>&2 "release-notes: \033[32;1mdone\033[0m\n"
|
|
||||||
}
|
|
||||||
|
|
||||||
# --- PARSE ARGS ---
|
|
||||||
|
|
||||||
if [[ $# -gt 0 ]]; then
|
|
||||||
die "Release notes takes no arguments, but make sure to set VERSION."
|
|
||||||
fi
|
|
||||||
|
|
||||||
# --- CHECKS ---
|
|
||||||
|
|
||||||
if [[ ! -e flake.nix ]] || [[ ! -e .git ]]; then
|
|
||||||
die "must run in repo root"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
# repo must be clean
|
|
||||||
if ! git diff --quiet; then
|
|
||||||
die "repo is dirty, please commit or stash changes"
|
|
||||||
fi
|
|
||||||
|
|
||||||
if ! git diff --quiet --cached; then
|
|
||||||
die "repo has staged changes, please commit or stash them"
|
|
||||||
fi
|
|
||||||
|
|
||||||
if ! grep -F "$SUMMARY_MARKER_LINE" doc/manual/src/SUMMARY.md >/dev/null; then
|
|
||||||
# would have been nice to catch this early, but won't be worth the extra infra
|
|
||||||
die "SUMMARY.md is missing the marker line '$SUMMARY_MARKER_LINE', which would be used for inserting a new release notes page. Please fix the script."
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [[ ! -n "${VERSION:-}" ]]; then
|
|
||||||
die "please set the VERSION environment variable before invoking this script"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
# version_major_minor: MAJOR.MINOR
|
|
||||||
# version_full: MAJOR.MINOR.PATCH
|
|
||||||
# IS_PATCH: true if this is a patch release; append instead of create
|
|
||||||
if grep -E '^[0-9]+\.[0-9]+$' <<< "$VERSION" >/dev/null; then
|
|
||||||
log 'is minor'
|
|
||||||
IS_PATCH=false
|
|
||||||
version_full="$VERSION.0"
|
|
||||||
version_major_minor="$VERSION"
|
|
||||||
elif grep -E '^[0-9]+\.[0-9]+\.0$' <<< "$VERSION" >/dev/null; then
|
|
||||||
log 'is minor (.0)'
|
|
||||||
IS_PATCH=false
|
|
||||||
version_full="$VERSION"
|
|
||||||
version_major_minor="$(echo "$VERSION" | sed -e 's/\.0$//')"
|
|
||||||
elif grep -E '^[0-9]+\.[0-9]+\.[0-9]+$' <<< "$VERSION" >/dev/null; then
|
|
||||||
log 'is patch'
|
|
||||||
IS_PATCH=true
|
|
||||||
version_full="$VERSION"
|
|
||||||
version_major_minor="$(echo "$VERSION" | sed -e 's/\.[0-9]*$//')"
|
|
||||||
else
|
|
||||||
die "VERSION must be MAJOR.MINOR[.PATCH], where each is a number, e.g. 2.20 or 2.20.1 (VERSION was set to $VERSION)"
|
|
||||||
fi
|
|
||||||
|
|
||||||
unset VERSION
|
|
||||||
|
|
||||||
log "version_major_minor=$version_major_minor"
|
|
||||||
log "version_full=$version_full"
|
|
||||||
log "IS_PATCH=$IS_PATCH"
|
|
||||||
|
|
||||||
basename=rl-${version_major_minor}.md
|
|
||||||
file=doc/manual/src/release-notes/$basename
|
|
||||||
|
|
||||||
if ! $IS_PATCH; then
|
|
||||||
if [[ -e $file ]]; then
|
|
||||||
die "release notes file $file already exists. If you'd like to make a minor release, pass a patch version, e.g. 2.20.1"
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
|
|
||||||
# --- DEFAULTS ---
|
|
||||||
|
|
||||||
if [[ ! -n "${DATE:-}" ]]; then
|
|
||||||
DATE="$(date +%Y-%m-%d)"
|
|
||||||
log "DATE not set, using $DATE"
|
|
||||||
fi
|
|
||||||
|
|
||||||
case "$DATE" in
|
|
||||||
[0-9][0-9][0-9][0-9]-[0-9][0-9]-[0-9][0-9])
|
|
||||||
;;
|
|
||||||
*)
|
|
||||||
die "DATE must be YYYY-MM-DD, e.g. 2021-12-31 (DATE was set to $DATE)"
|
|
||||||
;;
|
|
||||||
esac
|
|
||||||
|
|
||||||
# --- DO THE WORK ---
|
|
||||||
|
|
||||||
# menu
|
|
||||||
title="Release $version_major_minor ($DATE)"
|
|
||||||
# section on page
|
|
||||||
section_title="Release $version_full ($DATE)"
|
|
||||||
|
|
||||||
(
|
|
||||||
# TODO add minor number, and append?
|
|
||||||
echo "# $section_title"
|
|
||||||
echo
|
|
||||||
build-release-notes --change-authors doc/manual/change-authors.yml doc/manual/rl-next
|
|
||||||
) | tee -a $file
|
|
||||||
|
|
||||||
log "Wrote $file"
|
|
||||||
|
|
||||||
if ! $IS_PATCH; then
|
|
||||||
NEW_SUMMARY_LINE=" - [$title](release-notes/$basename)"
|
|
||||||
|
|
||||||
# find the marker line, insert new link after it
|
|
||||||
escaped_marker="$(echo "$SUMMARY_MARKER_LINE" | sed -e 's/\//\\\//g' -e 's/ /\\ /g')"
|
|
||||||
escaped_line="$(echo "$NEW_SUMMARY_LINE" | sed -e 's/\//\\\//g' -e 's/ /\\ /g')"
|
|
||||||
logcmd sed -i -e "/$escaped_marker/a $escaped_line" doc/manual/src/SUMMARY.md
|
|
||||||
fi
|
|
||||||
|
|
||||||
for f in doc/manual/rl-next/*.md; do
|
|
||||||
if [[ config != "$(basename $f)" ]]; then
|
|
||||||
logcmd git rm $f
|
|
||||||
fi
|
|
||||||
done
|
|
||||||
|
|
||||||
logcmd git add $file doc/manual/src/SUMMARY.md
|
|
||||||
logcmd git status
|
|
||||||
logcmd git commit -m "release notes: $version_full"
|
|
||||||
|
|
||||||
report_done
|
|
25
meson.build
25
meson.build
|
@ -167,6 +167,7 @@ message('canonical Nix system name:', host_system)
|
||||||
|
|
||||||
is_linux = host_machine.system() == 'linux'
|
is_linux = host_machine.system() == 'linux'
|
||||||
is_darwin = host_machine.system() == 'darwin'
|
is_darwin = host_machine.system() == 'darwin'
|
||||||
|
is_freebsd = host_machine.system() == 'freebsd'
|
||||||
is_x64 = host_machine.cpu_family() == 'x86_64'
|
is_x64 = host_machine.cpu_family() == 'x86_64'
|
||||||
|
|
||||||
# Per-platform arguments that you should probably pass to shared_module() invocations.
|
# Per-platform arguments that you should probably pass to shared_module() invocations.
|
||||||
|
@ -222,7 +223,8 @@ brotli = [
|
||||||
|
|
||||||
openssl = dependency('libcrypto', 'openssl', required : true)
|
openssl = dependency('libcrypto', 'openssl', required : true)
|
||||||
|
|
||||||
aws_sdk = dependency('aws-cpp-sdk-core', required : false)
|
# FIXME: confirm we actually support such old versions of aws-sdk-cpp
|
||||||
|
aws_sdk = dependency('aws-cpp-sdk-core', required : false, version : '>=1.8')
|
||||||
aws_sdk_transfer = dependency('aws-cpp-sdk-transfer', required : aws_sdk.found(), fallback : ['aws_sdk', 'aws_cpp_sdk_transfer_dep'])
|
aws_sdk_transfer = dependency('aws-cpp-sdk-transfer', required : aws_sdk.found(), fallback : ['aws_sdk', 'aws_cpp_sdk_transfer_dep'])
|
||||||
if aws_sdk.found()
|
if aws_sdk.found()
|
||||||
# The AWS pkg-config adds -std=c++11.
|
# The AWS pkg-config adds -std=c++11.
|
||||||
|
@ -234,12 +236,6 @@ if aws_sdk.found()
|
||||||
links : true,
|
links : true,
|
||||||
sources : true,
|
sources : true,
|
||||||
)
|
)
|
||||||
s = aws_sdk.version().split('.')
|
|
||||||
configdata += {
|
|
||||||
'AWS_VERSION_MAJOR': s[0].to_int(),
|
|
||||||
'AWS_VERSION_MINOR': s[1].to_int(),
|
|
||||||
'AWS_VERSION_PATCH': s[2].to_int(),
|
|
||||||
}
|
|
||||||
aws_sdk_transfer = aws_sdk_transfer.partial_dependency(
|
aws_sdk_transfer = aws_sdk_transfer.partial_dependency(
|
||||||
compile_args : false,
|
compile_args : false,
|
||||||
includes : true,
|
includes : true,
|
||||||
|
@ -441,7 +437,9 @@ add_project_arguments(
|
||||||
language : 'cpp',
|
language : 'cpp',
|
||||||
)
|
)
|
||||||
|
|
||||||
if cxx.get_id() in ['gcc', 'clang']
|
# We turn off the production UBSan if the slower dev UBSan is requested, to
|
||||||
|
# give better diagnostics.
|
||||||
|
if cxx.get_id() in ['gcc', 'clang'] and 'undefined' not in get_option('b_sanitize')
|
||||||
# 2024-03-24: jade benchmarked the default sanitize reporting in clang and got
|
# 2024-03-24: jade benchmarked the default sanitize reporting in clang and got
|
||||||
# a regression of about 10% on hackage-packages.nix with clang. So we are trapping instead.
|
# a regression of about 10% on hackage-packages.nix with clang. So we are trapping instead.
|
||||||
#
|
#
|
||||||
|
@ -456,12 +454,23 @@ if cxx.get_id() in ['gcc', 'clang']
|
||||||
add_project_arguments(sanitize_args, language: 'cpp')
|
add_project_arguments(sanitize_args, language: 'cpp')
|
||||||
add_project_link_arguments(sanitize_args, language: 'cpp')
|
add_project_link_arguments(sanitize_args, language: 'cpp')
|
||||||
endif
|
endif
|
||||||
|
# Clang's default of -no-shared-libsan on Linux causes link errors; on macOS it defaults to shared.
|
||||||
|
# GCC defaults to shared libsan so is fine.
|
||||||
|
if cxx.get_id() == 'clang' and get_option('b_sanitize') != ''
|
||||||
|
add_project_link_arguments('-shared-libsan', language : 'cpp')
|
||||||
|
endif
|
||||||
|
|
||||||
add_project_link_arguments('-pthread', language : 'cpp')
|
add_project_link_arguments('-pthread', language : 'cpp')
|
||||||
if cxx.get_linker_id() in ['ld.bfd', 'ld.gold']
|
if cxx.get_linker_id() in ['ld.bfd', 'ld.gold']
|
||||||
add_project_link_arguments('-Wl,--no-copy-dt-needed-entries', language : 'cpp')
|
add_project_link_arguments('-Wl,--no-copy-dt-needed-entries', language : 'cpp')
|
||||||
endif
|
endif
|
||||||
|
|
||||||
|
if is_freebsd
|
||||||
|
# FreeBSD's `environ` is defined in `crt1.o`, not `libc.so`,
|
||||||
|
# so the linker thinks it's undefined
|
||||||
|
add_project_link_arguments('-Wl,-z,undefs', language: 'cpp')
|
||||||
|
endif
|
||||||
|
|
||||||
# Generate Chromium tracing files for each compiled file, which enables
|
# Generate Chromium tracing files for each compiled file, which enables
|
||||||
# maintainers/buildtime_report.sh BUILD-DIR to simply work in clang builds.
|
# maintainers/buildtime_report.sh BUILD-DIR to simply work in clang builds.
|
||||||
#
|
#
|
||||||
|
|
|
@ -402,6 +402,7 @@ stdenv.mkDerivation (finalAttrs: {
|
||||||
# Lix specific packages
|
# Lix specific packages
|
||||||
pre-commit-checks,
|
pre-commit-checks,
|
||||||
contribNotice,
|
contribNotice,
|
||||||
|
check-syscalls,
|
||||||
}:
|
}:
|
||||||
let
|
let
|
||||||
glibcFix = lib.optionalAttrs (buildPlatform.isLinux && glibcLocales != null) {
|
glibcFix = lib.optionalAttrs (buildPlatform.isLinux && glibcLocales != null) {
|
||||||
|
@ -455,6 +456,7 @@ stdenv.mkDerivation (finalAttrs: {
|
||||||
pythonEnv
|
pythonEnv
|
||||||
# docker image tool
|
# docker image tool
|
||||||
skopeo
|
skopeo
|
||||||
|
check-syscalls
|
||||||
just
|
just
|
||||||
nixfmt
|
nixfmt
|
||||||
# Load-bearing order. Must come before clang-unwrapped below, but after clang_tools above.
|
# Load-bearing order. Must come before clang-unwrapped below, but after clang_tools above.
|
||||||
|
|
|
@ -15,6 +15,7 @@ from . import version
|
||||||
from . import cli
|
from . import cli
|
||||||
from . import docker
|
from . import docker
|
||||||
from . import docker_assemble
|
from . import docker_assemble
|
||||||
|
from . import release_notes
|
||||||
from . import gitutils
|
from . import gitutils
|
||||||
|
|
||||||
|
|
||||||
|
@ -77,3 +78,4 @@ def reload():
|
||||||
importlib.reload(docker)
|
importlib.reload(docker)
|
||||||
importlib.reload(docker_assemble)
|
importlib.reload(docker_assemble)
|
||||||
importlib.reload(gitutils)
|
importlib.reload(gitutils)
|
||||||
|
importlib.reload(release_notes)
|
||||||
|
|
|
@ -6,12 +6,14 @@ from pathlib import Path
|
||||||
import tempfile
|
import tempfile
|
||||||
import hashlib
|
import hashlib
|
||||||
import datetime
|
import datetime
|
||||||
|
|
||||||
from . import environment
|
from . import environment
|
||||||
from .environment import RelengEnvironment
|
from .environment import RelengEnvironment
|
||||||
from . import keys
|
from . import keys
|
||||||
from . import docker
|
from . import docker
|
||||||
from .version import VERSION, RELEASE_NAME, MAJOR
|
from .version import VERSION, RELEASE_NAME, MAJOR
|
||||||
from .gitutils import verify_are_on_tag, git_preconditions
|
from .gitutils import verify_are_on_tag, git_preconditions
|
||||||
|
from . import release_notes
|
||||||
|
|
||||||
$RAISE_SUBPROC_ERROR = True
|
$RAISE_SUBPROC_ERROR = True
|
||||||
$XONSH_SHOW_TRACEBACK = True
|
$XONSH_SHOW_TRACEBACK = True
|
||||||
|
@ -104,17 +106,17 @@ def upload_drv_paths_and_outputs(env: RelengEnvironment, paths: list[str]):
|
||||||
raise subprocess.CalledProcessError(rv, proc.args)
|
raise subprocess.CalledProcessError(rv, proc.args)
|
||||||
|
|
||||||
|
|
||||||
def make_manifest(eval_result):
|
def make_manifest(builds_by_system):
|
||||||
manifest = {vs['system']: vs['outputs']['out'] for vs in eval_result}
|
|
||||||
def manifest_line(system, out):
|
def manifest_line(system, out):
|
||||||
return f' {system} = "{out}";'
|
return f' {system} = "{out}";'
|
||||||
|
|
||||||
manifest_text = textwrap.dedent("""\
|
manifest_text = textwrap.dedent("""\
|
||||||
# This file was generated by releng/create_release.xsh in Lix
|
# This file was generated by releng/create_release.xsh in Lix
|
||||||
{{
|
{{
|
||||||
{lines}
|
{lines}
|
||||||
}}
|
}}
|
||||||
""").format(lines='\n'.join(manifest_line(s, p) for (s, p) in manifest.items()))
|
""").format(lines='\n'.join(manifest_line(s, p) for (s, p) in builds_by_system.items()))
|
||||||
|
|
||||||
return manifest_text
|
return manifest_text
|
||||||
|
|
||||||
|
@ -140,6 +142,18 @@ def sha256_file(f: Path):
|
||||||
return hasher.hexdigest()
|
return hasher.hexdigest()
|
||||||
|
|
||||||
|
|
||||||
|
def extract_builds_by_system(eval_result):
|
||||||
|
# This could be a dictionary comprehension, but we want to be absolutely
|
||||||
|
# sure we don't have duplicates.
|
||||||
|
ret = {}
|
||||||
|
for attr in eval_result:
|
||||||
|
if attr['attrPath'][0] != 'build':
|
||||||
|
continue
|
||||||
|
assert attr['system'] not in ret
|
||||||
|
ret[attr['system']] = attr['outputs']['out']
|
||||||
|
return ret
|
||||||
|
|
||||||
|
|
||||||
def make_artifacts_dir(eval_result, d: Path):
|
def make_artifacts_dir(eval_result, d: Path):
|
||||||
d.mkdir(exist_ok=True, parents=True)
|
d.mkdir(exist_ok=True, parents=True)
|
||||||
version_dir = d / 'lix' / f'lix-{VERSION}'
|
version_dir = d / 'lix' / f'lix-{VERSION}'
|
||||||
|
@ -148,12 +162,14 @@ def make_artifacts_dir(eval_result, d: Path):
|
||||||
tarballs_drv = next(p for p in eval_result if p['attr'] == 'tarballs')
|
tarballs_drv = next(p for p in eval_result if p['attr'] == 'tarballs')
|
||||||
cp --no-preserve=mode -r @(tarballs_drv['outputs']['out'])/* @(version_dir)
|
cp --no-preserve=mode -r @(tarballs_drv['outputs']['out'])/* @(version_dir)
|
||||||
|
|
||||||
|
builds_by_system = extract_builds_by_system(eval_result)
|
||||||
|
|
||||||
# FIXME: upgrade-nix searches for manifest.nix at root, which is rather annoying
|
# FIXME: upgrade-nix searches for manifest.nix at root, which is rather annoying
|
||||||
with open(d / 'manifest.nix', 'w') as h:
|
with open(d / 'manifest.nix', 'w') as h:
|
||||||
h.write(make_manifest(eval_result))
|
h.write(make_manifest(builds_by_system))
|
||||||
|
|
||||||
with open(version_dir / 'manifest.nix', 'w') as h:
|
with open(version_dir / 'manifest.nix', 'w') as h:
|
||||||
h.write(make_manifest(eval_result))
|
h.write(make_manifest(builds_by_system))
|
||||||
|
|
||||||
print('[+] Make sources tarball')
|
print('[+] Make sources tarball')
|
||||||
|
|
||||||
|
@ -169,51 +185,7 @@ def make_artifacts_dir(eval_result, d: Path):
|
||||||
|
|
||||||
|
|
||||||
def prepare_release_notes():
|
def prepare_release_notes():
|
||||||
print('[+] Preparing release notes')
|
rl_path = release_notes.build_release_notes_to_file()
|
||||||
RELEASE_NOTES_PATH = Path('doc/manual/rl-next')
|
|
||||||
|
|
||||||
if RELEASE_NOTES_PATH.isdir():
|
|
||||||
notes_body = subprocess.check_output(['build-release-notes', '--change-authors', 'doc/manual/change-authors.yml', 'doc/manual/rl-next']).decode()
|
|
||||||
else:
|
|
||||||
# I guess nobody put release notes on their changes?
|
|
||||||
print('[-] Warning: seemingly missing any release notes, not worrying about it')
|
|
||||||
notes_body = ''
|
|
||||||
|
|
||||||
rl_path = Path(f'doc/manual/src/release-notes/rl-{MAJOR}.md')
|
|
||||||
|
|
||||||
existing_rl = ''
|
|
||||||
try:
|
|
||||||
with open(rl_path, 'r') as fh:
|
|
||||||
existing_rl = fh.read()
|
|
||||||
except FileNotFoundError:
|
|
||||||
pass
|
|
||||||
|
|
||||||
date = datetime.datetime.now().strftime('%Y-%m-%d')
|
|
||||||
|
|
||||||
minor_header = f'# Lix {VERSION} ({date})'
|
|
||||||
|
|
||||||
header = f'# Lix {MAJOR} "{RELEASE_NAME}"'
|
|
||||||
if existing_rl.startswith(header):
|
|
||||||
# strip the header off for minor releases
|
|
||||||
lines = existing_rl.splitlines()
|
|
||||||
header = lines[0]
|
|
||||||
existing_rl = '\n'.join(lines[1:])
|
|
||||||
else:
|
|
||||||
header += f' ({date})\n\n'
|
|
||||||
|
|
||||||
header += '\n' + minor_header + '\n'
|
|
||||||
|
|
||||||
notes = header
|
|
||||||
notes += notes_body
|
|
||||||
notes += "\n\n"
|
|
||||||
notes += existing_rl
|
|
||||||
|
|
||||||
# make pre-commit happy about one newline
|
|
||||||
notes = notes.rstrip()
|
|
||||||
notes += "\n"
|
|
||||||
|
|
||||||
with open(rl_path, 'w') as fh:
|
|
||||||
fh.write(notes)
|
|
||||||
|
|
||||||
commit_msg = textwrap.dedent("""\
|
commit_msg = textwrap.dedent("""\
|
||||||
release: release notes for {VERSION}
|
release: release notes for {VERSION}
|
||||||
|
@ -221,8 +193,8 @@ def prepare_release_notes():
|
||||||
{RELENG_MSG}
|
{RELENG_MSG}
|
||||||
""").format(VERSION=VERSION, RELENG_MSG=RELENG_MSG)
|
""").format(VERSION=VERSION, RELENG_MSG=RELENG_MSG)
|
||||||
|
|
||||||
git add @(rl_path)
|
git add @(rl_path) @(release_notes.SUMMARY)
|
||||||
git rm doc/manual/rl-next/*.md
|
git rm --ignore-unmatch 'doc/manual/rl-next/*.md'
|
||||||
|
|
||||||
git commit -m @(commit_msg)
|
git commit -m @(commit_msg)
|
||||||
|
|
||||||
|
|
81
releng/release_notes.py
Normal file
81
releng/release_notes.py
Normal file
|
@ -0,0 +1,81 @@
|
||||||
|
from pathlib import Path
|
||||||
|
import subprocess
|
||||||
|
import datetime
|
||||||
|
|
||||||
|
from .version import MAJOR, VERSION, RELEASE_NAME
|
||||||
|
|
||||||
|
MANUAL = Path('doc/manual')
|
||||||
|
RELEASE_NOTES_BASE = MANUAL / 'src/release-notes'
|
||||||
|
VERSION_RL = RELEASE_NOTES_BASE / f'rl-{MAJOR}.md'
|
||||||
|
SUMMARY = MANUAL / 'src/SUMMARY.md'
|
||||||
|
|
||||||
|
def add_to_summary(date: str):
|
||||||
|
# N.B: This kind of duplicates gitutils.is_maintenance_branch, but it's a more clear
|
||||||
|
# check that allows potentially releasing a -rc without release notes being
|
||||||
|
# moved, then in .0 actually move the release notes in place.
|
||||||
|
if VERSION_RL.exists():
|
||||||
|
return
|
||||||
|
|
||||||
|
MARKER = '<!-- RELENG-AUTO-INSERTION-MARKER'
|
||||||
|
|
||||||
|
new_lines = []
|
||||||
|
for line in SUMMARY.read_text().splitlines():
|
||||||
|
new_lines.append(line)
|
||||||
|
if MARKER in line:
|
||||||
|
indent, _, _ = line.partition(MARKER)
|
||||||
|
new_lines.append(f'{indent}- [Lix {MAJOR} ({date})](release-notes/rl-{MAJOR}.md)')
|
||||||
|
|
||||||
|
# make pre-commit happy about one newline
|
||||||
|
text = '\n'.join(new_lines).rstrip()
|
||||||
|
text += '\n'
|
||||||
|
SUMMARY.write_text(text)
|
||||||
|
|
||||||
|
def build_release_notes_to_file():
|
||||||
|
date = datetime.datetime.now().strftime('%Y-%m-%d')
|
||||||
|
add_to_summary(date)
|
||||||
|
|
||||||
|
print('[+] Preparing release notes')
|
||||||
|
RELEASE_NOTES_PATH = Path('doc/manual/rl-next')
|
||||||
|
|
||||||
|
if RELEASE_NOTES_PATH.is_dir():
|
||||||
|
notes_body = subprocess.check_output(['build-release-notes', '--change-authors', 'doc/manual/change-authors.yml', RELEASE_NOTES_PATH]).decode()
|
||||||
|
else:
|
||||||
|
# I guess nobody put release notes on their changes?
|
||||||
|
print('[-] Warning: seemingly missing any release notes, not worrying about it')
|
||||||
|
notes_body = ''
|
||||||
|
|
||||||
|
rl_path = Path(RELEASE_NOTES_BASE / f'rl-{MAJOR}.md')
|
||||||
|
|
||||||
|
existing_rl = ''
|
||||||
|
try:
|
||||||
|
with open(rl_path, 'r') as fh:
|
||||||
|
existing_rl = fh.read()
|
||||||
|
except FileNotFoundError:
|
||||||
|
pass
|
||||||
|
|
||||||
|
minor_header = f'# Lix {VERSION} ({date})'
|
||||||
|
|
||||||
|
header = f'# Lix {MAJOR} "{RELEASE_NAME}"'
|
||||||
|
if existing_rl.startswith(header):
|
||||||
|
# strip the header off for minor releases
|
||||||
|
lines = existing_rl.splitlines()
|
||||||
|
header = lines[0]
|
||||||
|
existing_rl = '\n'.join(lines[1:])
|
||||||
|
else:
|
||||||
|
header += f' ({date})\n\n'
|
||||||
|
|
||||||
|
header += '\n' + minor_header + '\n'
|
||||||
|
|
||||||
|
notes = header
|
||||||
|
notes += notes_body
|
||||||
|
notes += "\n\n"
|
||||||
|
notes += existing_rl
|
||||||
|
|
||||||
|
# make pre-commit happy about one newline
|
||||||
|
notes = notes.rstrip()
|
||||||
|
notes += "\n"
|
||||||
|
|
||||||
|
with open(rl_path, 'w') as fh:
|
||||||
|
fh.write(notes)
|
||||||
|
|
||||||
|
return rl_path
|
|
@ -214,7 +214,7 @@ void SourceExprCommand::completeInstallable(AddCompletions & completions, std::s
|
||||||
|
|
||||||
evalSettings.pureEval = false;
|
evalSettings.pureEval = false;
|
||||||
auto state = getEvalState();
|
auto state = getEvalState();
|
||||||
Expr *e = state->parseExprFromFile(
|
Expr & e = state->parseExprFromFile(
|
||||||
resolveExprPath(state->checkSourcePath(lookupFileArg(*state, *file)))
|
resolveExprPath(state->checkSourcePath(lookupFileArg(*state, *file)))
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -434,13 +434,13 @@ Installables SourceExprCommand::parseInstallables(
|
||||||
auto vFile = state->allocValue();
|
auto vFile = state->allocValue();
|
||||||
|
|
||||||
if (file == "-") {
|
if (file == "-") {
|
||||||
auto e = state->parseStdin();
|
auto & e = state->parseStdin();
|
||||||
state->eval(e, *vFile);
|
state->eval(e, *vFile);
|
||||||
}
|
}
|
||||||
else if (file)
|
else if (file)
|
||||||
state->evalFile(lookupFileArg(*state, *file), *vFile);
|
state->evalFile(lookupFileArg(*state, *file), *vFile);
|
||||||
else {
|
else {
|
||||||
auto e = state->parseExprFromString(*expr, state->rootPath(CanonPath::fromCwd()));
|
auto & e = state->parseExprFromString(*expr, state->rootPath(CanonPath::fromCwd()));
|
||||||
state->eval(e, *vFile);
|
state->eval(e, *vFile);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,9 @@
|
||||||
|
#include <editline.h>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <cstdlib>
|
#include <cstdlib>
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
#include <climits>
|
#include <climits>
|
||||||
|
#include <string_view>
|
||||||
|
|
||||||
#include "box_ptr.hh"
|
#include "box_ptr.hh"
|
||||||
#include "repl-interacter.hh"
|
#include "repl-interacter.hh"
|
||||||
|
@ -79,6 +81,8 @@ enum class ProcessLineResult {
|
||||||
PromptAgain,
|
PromptAgain,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
using namespace std::literals::string_view_literals;
|
||||||
|
|
||||||
struct NixRepl
|
struct NixRepl
|
||||||
: AbstractNixRepl
|
: AbstractNixRepl
|
||||||
, detail::ReplCompleterMixin
|
, detail::ReplCompleterMixin
|
||||||
|
@ -86,6 +90,35 @@ struct NixRepl
|
||||||
, gc
|
, gc
|
||||||
#endif
|
#endif
|
||||||
{
|
{
|
||||||
|
/* clang-format: off */
|
||||||
|
static constexpr std::array COMMANDS = {
|
||||||
|
"add"sv, "a"sv,
|
||||||
|
"load"sv, "l"sv,
|
||||||
|
"load-flake"sv, "lf"sv,
|
||||||
|
"reload"sv, "r"sv,
|
||||||
|
"edit"sv, "e"sv,
|
||||||
|
"t"sv,
|
||||||
|
"u"sv,
|
||||||
|
"b"sv,
|
||||||
|
"bl"sv,
|
||||||
|
"i"sv,
|
||||||
|
"sh"sv,
|
||||||
|
"log"sv,
|
||||||
|
"print"sv, "p"sv,
|
||||||
|
"quit"sv, "q"sv,
|
||||||
|
"doc"sv,
|
||||||
|
"te"sv,
|
||||||
|
};
|
||||||
|
|
||||||
|
static constexpr std::array DEBUG_COMMANDS = {
|
||||||
|
"env"sv,
|
||||||
|
"bt"sv, "backtrace"sv,
|
||||||
|
"st"sv,
|
||||||
|
"c"sv, "continue"sv,
|
||||||
|
"s"sv, "step"sv,
|
||||||
|
};
|
||||||
|
/* clang-format: on */
|
||||||
|
|
||||||
size_t debugTraceIndex;
|
size_t debugTraceIndex;
|
||||||
|
|
||||||
Strings loadedFiles;
|
Strings loadedFiles;
|
||||||
|
@ -122,7 +155,7 @@ struct NixRepl
|
||||||
void reloadFiles();
|
void reloadFiles();
|
||||||
void addAttrsToScope(Value & attrs);
|
void addAttrsToScope(Value & attrs);
|
||||||
void addVarToScope(const Symbol name, Value & v);
|
void addVarToScope(const Symbol name, Value & v);
|
||||||
Expr * parseString(std::string s);
|
Expr & parseString(std::string s);
|
||||||
void evalString(std::string s, Value & v);
|
void evalString(std::string s, Value & v);
|
||||||
void loadDebugTraceEnv(DebugTrace & dt);
|
void loadDebugTraceEnv(DebugTrace & dt);
|
||||||
|
|
||||||
|
@ -323,6 +356,36 @@ StringSet NixRepl::completePrefix(const std::string & prefix)
|
||||||
{
|
{
|
||||||
StringSet completions;
|
StringSet completions;
|
||||||
|
|
||||||
|
// We should only complete colon commands if there's a colon at the beginning,
|
||||||
|
// but editline (for... whatever reason) doesn't *give* us the colon in the
|
||||||
|
// completion callback. If the user types :rel<TAB>, `prefix` will only be `rel`.
|
||||||
|
// Luckily, editline provides a global variable for its current buffer, so we can
|
||||||
|
// check for the presence of a colon there.
|
||||||
|
if (rl_line_buffer != nullptr && rl_line_buffer[0] == ':') {
|
||||||
|
for (auto const & colonCmd : this->COMMANDS) {
|
||||||
|
if (colonCmd.starts_with(prefix)) {
|
||||||
|
completions.insert(std::string(colonCmd));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (state->debugRepl) {
|
||||||
|
for (auto const & colonCmd : this->DEBUG_COMMANDS) {
|
||||||
|
if (colonCmd.starts_with(prefix)) {
|
||||||
|
completions.insert(std::string(colonCmd));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// If there were : command completions, then we should only return those,
|
||||||
|
// because otherwise this is not valid Nix syntax.
|
||||||
|
// However if we didn't get any completions, then this could be something
|
||||||
|
// like `:b pkgs.hel<TAB>`, in which case we should do expression completion
|
||||||
|
// as normal.
|
||||||
|
if (!completions.empty()) {
|
||||||
|
return completions;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
size_t start = prefix.find_last_of(" \n\r\t(){}[]");
|
size_t start = prefix.find_last_of(" \n\r\t(){}[]");
|
||||||
std::string prev, cur;
|
std::string prev, cur;
|
||||||
if (start == std::string::npos) {
|
if (start == std::string::npos) {
|
||||||
|
@ -365,9 +428,9 @@ StringSet NixRepl::completePrefix(const std::string & prefix)
|
||||||
auto expr = cur.substr(0, dot);
|
auto expr = cur.substr(0, dot);
|
||||||
auto cur2 = cur.substr(dot + 1);
|
auto cur2 = cur.substr(dot + 1);
|
||||||
|
|
||||||
Expr * e = parseString(expr);
|
Expr & e = parseString(expr);
|
||||||
Value v;
|
Value v;
|
||||||
e->eval(*state, *env, v);
|
e.eval(*state, *env, v);
|
||||||
state->forceAttrs(v, noPos, "while evaluating an attrset for the purpose of completion (this error should not be displayed; file an issue?)");
|
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) {
|
for (auto & i : *v.attrs) {
|
||||||
|
@ -758,7 +821,7 @@ ProcessLineResult NixRepl::processLine(std::string line)
|
||||||
line[p + 1] != '=' &&
|
line[p + 1] != '=' &&
|
||||||
isVarName(name = removeWhitespace(line.substr(0, p))))
|
isVarName(name = removeWhitespace(line.substr(0, p))))
|
||||||
{
|
{
|
||||||
Expr * e = parseString(line.substr(p + 1));
|
Expr & e = parseString(line.substr(p + 1));
|
||||||
Value & v(*state->allocValue());
|
Value & v(*state->allocValue());
|
||||||
v.mkThunk(env, e);
|
v.mkThunk(env, e);
|
||||||
addVarToScope(state->symbols.create(name), v);
|
addVarToScope(state->symbols.create(name), v);
|
||||||
|
@ -883,7 +946,7 @@ Value * NixRepl::getReplOverlaysEvalFunction()
|
||||||
auto code =
|
auto code =
|
||||||
#include "repl-overlays.nix.gen.hh"
|
#include "repl-overlays.nix.gen.hh"
|
||||||
;
|
;
|
||||||
auto expr = state->parseExprFromString(
|
auto & expr = state->parseExprFromString(
|
||||||
code,
|
code,
|
||||||
SourcePath(evalReplInitFilesPath),
|
SourcePath(evalReplInitFilesPath),
|
||||||
state->staticBaseEnv
|
state->staticBaseEnv
|
||||||
|
@ -991,7 +1054,7 @@ Value * NixRepl::bindingsToAttrs()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
Expr * NixRepl::parseString(std::string s)
|
Expr & NixRepl::parseString(std::string s)
|
||||||
{
|
{
|
||||||
return state->parseExprFromString(std::move(s), state->rootPath(CanonPath::fromCwd()), staticEnv);
|
return state->parseExprFromString(std::move(s), state->rootPath(CanonPath::fromCwd()), staticEnv);
|
||||||
}
|
}
|
||||||
|
@ -999,16 +1062,16 @@ Expr * NixRepl::parseString(std::string s)
|
||||||
|
|
||||||
void NixRepl::evalString(std::string s, Value & v)
|
void NixRepl::evalString(std::string s, Value & v)
|
||||||
{
|
{
|
||||||
Expr * e = parseString(s);
|
Expr & e = parseString(s);
|
||||||
e->eval(*state, *env, v);
|
e.eval(*state, *env, v);
|
||||||
state->forceValue(v, v.determinePos(noPos));
|
state->forceValue(v, v.determinePos(noPos));
|
||||||
}
|
}
|
||||||
|
|
||||||
Value * NixRepl::evalFile(SourcePath & path)
|
Value * NixRepl::evalFile(SourcePath & path)
|
||||||
{
|
{
|
||||||
auto expr = state->parseExprFromFile(path, staticEnv);
|
auto & expr = state->parseExprFromFile(path, staticEnv);
|
||||||
Value * result(state->allocValue());
|
Value * result(state->allocValue());
|
||||||
expr->eval(*state, *env, *result);
|
expr.eval(*state, *env, *result);
|
||||||
state->forceValue(*result, result->determinePos(noPos));
|
state->forceValue(*result, result->determinePos(noPos));
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
|
@ -47,12 +47,16 @@ MakeError(MissingArgumentError, EvalError);
|
||||||
MakeError(RestrictedPathError, Error);
|
MakeError(RestrictedPathError, Error);
|
||||||
MakeError(InfiniteRecursionError, EvalError);
|
MakeError(InfiniteRecursionError, EvalError);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents an exception due to an invalid path; that is, it does not exist.
|
||||||
|
* It corresponds to `!Store::validPath()`.
|
||||||
|
*/
|
||||||
struct InvalidPathError : public EvalError
|
struct InvalidPathError : public EvalError
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
Path path;
|
Path path;
|
||||||
InvalidPathError(EvalState & state, const Path & path)
|
InvalidPathError(EvalState & state, const Path & path)
|
||||||
: EvalError(state, "path '%s' is not valid", path)
|
: EvalError(state, "path '%s' did not exist in the store during evaluation", path)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
|
@ -86,11 +86,11 @@ void EvalState::forceValue(Value & v, const PosIdx pos)
|
||||||
{
|
{
|
||||||
if (v.isThunk()) {
|
if (v.isThunk()) {
|
||||||
Env * env = v.thunk.env;
|
Env * env = v.thunk.env;
|
||||||
Expr * expr = v.thunk.expr;
|
Expr & expr = *v.thunk.expr;
|
||||||
try {
|
try {
|
||||||
v.mkBlackhole();
|
v.mkBlackhole();
|
||||||
//checkInterrupt();
|
//checkInterrupt();
|
||||||
expr->eval(*this, *env, v);
|
expr.eval(*this, *env, v);
|
||||||
} catch (...) {
|
} catch (...) {
|
||||||
v.mkThunk(env, expr);
|
v.mkThunk(env, expr);
|
||||||
tryFixupBlackHolePos(v, pos);
|
tryFixupBlackHolePos(v, pos);
|
||||||
|
|
|
@ -63,11 +63,9 @@ Strings EvalSettings::getDefaultNixPath()
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
if (!evalSettings.restrictEval && !evalSettings.pureEval) {
|
add(getNixDefExpr() + "/channels");
|
||||||
add(getNixDefExpr() + "/channels");
|
add(rootChannelsDir() + "/nixpkgs", "nixpkgs");
|
||||||
add(rootChannelsDir() + "/nixpkgs", "nixpkgs");
|
add(rootChannelsDir());
|
||||||
add(rootChannelsDir());
|
|
||||||
}
|
|
||||||
|
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,9 +15,21 @@ struct EvalSettings : Config
|
||||||
static std::string resolvePseudoUrl(std::string_view url);
|
static std::string resolvePseudoUrl(std::string_view url);
|
||||||
|
|
||||||
Setting<bool> enableNativeCode{this, false, "allow-unsafe-native-code-during-evaluation", R"(
|
Setting<bool> enableNativeCode{this, false, "allow-unsafe-native-code-during-evaluation", R"(
|
||||||
Whether builtin functions that allow executing native code should be enabled.
|
Enable built-in functions that allow executing native code.
|
||||||
|
|
||||||
In particular, this adds the `importNative` and `exec` builtins.
|
In particular, this adds:
|
||||||
|
- `builtins.importNative` *path* *symbol*
|
||||||
|
|
||||||
|
Runs function with *symbol* from a dynamic shared object (DSO) at *path*.
|
||||||
|
This may be used to add new builtins to the Nix language.
|
||||||
|
The procedure must have the following signature:
|
||||||
|
```cpp
|
||||||
|
extern "C" typedef void (*ValueInitialiser) (EvalState & state, Value & v);
|
||||||
|
```
|
||||||
|
|
||||||
|
- `builtins.exec` *arguments*
|
||||||
|
|
||||||
|
Execute a program, where *arguments* are specified as a list of strings, and parse its output as a Nix expression.
|
||||||
)"};
|
)"};
|
||||||
|
|
||||||
Setting<Strings> nixPath{
|
Setting<Strings> nixPath{
|
||||||
|
@ -63,8 +75,17 @@ struct EvalSettings : Config
|
||||||
R"(
|
R"(
|
||||||
Pure evaluation mode ensures that the result of Nix expressions is fully determined by explicitly declared inputs, and not influenced by external state:
|
Pure evaluation mode ensures that the result of Nix expressions is fully determined by explicitly declared inputs, and not influenced by external state:
|
||||||
|
|
||||||
- Restrict file system and network access to files specified by cryptographic hash
|
- File system and network access is restricted to accesses to immutable data only:
|
||||||
- Disable [`builtins.currentSystem`](@docroot@/language/builtin-constants.md#builtins-currentSystem) and [`builtins.currentTime`](@docroot@/language/builtin-constants.md#builtins-currentTime)
|
- Path literals relative to the home directory like `~/lix` are rejected at parse time.
|
||||||
|
- Access to absolute paths that did not result from Nix language evaluation is rejected when such paths are given as parameters to builtins like, for example, [`builtins.readFile`](@docroot@/language/builtins.md#builtins-readFile).
|
||||||
|
|
||||||
|
Access is nonetheless allowed to (absolute) paths in the Nix store that are returned by builtins like [`builtins.filterSource`](@docroot@/language/builtins.md#builtins-filterSource), [`builtins.fetchTarball`](@docroot@/language/builtins.md#builtins-fetchTarball) and similar.
|
||||||
|
- Impure fetches such as not specifying a commit ID for `builtins.fetchGit` or not specifying a hash for `builtins.fetchTarball` are rejected.
|
||||||
|
- In flakes, access to relative paths outside of the root of the flake's source tree (often, a git repository) is rejected.
|
||||||
|
- The evaluator ignores `NIX_PATH`, `-I` and the `nix-path` setting. Thus, [`builtins.nixPath`](@docroot@/language/builtin-constants.md#builtins-nixPath) is an empty list.
|
||||||
|
- The builtins [`builtins.currentSystem`](@docroot@/language/builtin-constants.md#builtins-currentSystem) and [`builtins.currentTime`](@docroot@/language/builtin-constants.md#builtins-currentTime) are absent from `builtins`.
|
||||||
|
- [`builtins.getEnv`](@docroot@/language/builtin-constants.md#builtins-currentSystem) always returns empty string for any variable.
|
||||||
|
- [`builtins.storePath`](@docroot@/language/builtins.md#builtins-storePath) throws an error (Lix may change this, tracking issue: <https://git.lix.systems/lix-project/lix/issues/402>)
|
||||||
)"
|
)"
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -86,6 +107,7 @@ struct EvalSettings : Config
|
||||||
allowed to access `https://github.com/NixOS/patchelf.git`.
|
allowed to access `https://github.com/NixOS/patchelf.git`.
|
||||||
)"};
|
)"};
|
||||||
|
|
||||||
|
|
||||||
Setting<bool> traceFunctionCalls{this, false, "trace-function-calls",
|
Setting<bool> traceFunctionCalls{this, false, "trace-function-calls",
|
||||||
R"(
|
R"(
|
||||||
If set to `true`, the Nix evaluator will trace every function call.
|
If set to `true`, the Nix evaluator will trace every function call.
|
||||||
|
|
|
@ -418,7 +418,7 @@ EvalState::EvalState(
|
||||||
}
|
}
|
||||||
|
|
||||||
if (evalSettings.restrictEval || evalSettings.pureEval) {
|
if (evalSettings.restrictEval || evalSettings.pureEval) {
|
||||||
allowedPaths = PathSet();
|
allowedPaths = std::optional(PathSet());
|
||||||
|
|
||||||
for (auto & i : searchPath.elements) {
|
for (auto & i : searchPath.elements) {
|
||||||
auto r = resolveSearchPathPath(i.path);
|
auto r = resolveSearchPathPath(i.path);
|
||||||
|
@ -950,14 +950,14 @@ void EvalState::mkList(Value & v, size_t size)
|
||||||
|
|
||||||
unsigned long nrThunks = 0;
|
unsigned long nrThunks = 0;
|
||||||
|
|
||||||
static inline void mkThunk(Value & v, Env & env, Expr * expr)
|
static inline void mkThunk(Value & v, Env & env, Expr & expr)
|
||||||
{
|
{
|
||||||
v.mkThunk(&env, expr);
|
v.mkThunk(&env, expr);
|
||||||
nrThunks++;
|
nrThunks++;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void EvalState::mkThunk_(Value & v, Expr * expr)
|
void EvalState::mkThunk_(Value & v, Expr & expr)
|
||||||
{
|
{
|
||||||
mkThunk(v, baseEnv, expr);
|
mkThunk(v, baseEnv, expr);
|
||||||
}
|
}
|
||||||
|
@ -1058,7 +1058,7 @@ void EvalState::mkSingleDerivedPathString(
|
||||||
Value * Expr::maybeThunk(EvalState & state, Env & env)
|
Value * Expr::maybeThunk(EvalState & state, Env & env)
|
||||||
{
|
{
|
||||||
Value * v = state.allocValue();
|
Value * v = state.allocValue();
|
||||||
mkThunk(*v, env, this);
|
mkThunk(*v, env, *this);
|
||||||
return v;
|
return v;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1122,7 +1122,7 @@ void EvalState::evalFile(const SourcePath & path_, Value & v, bool mustBeTrivial
|
||||||
e = j->second;
|
e = j->second;
|
||||||
|
|
||||||
if (!e)
|
if (!e)
|
||||||
e = parseExprFromFile(checkSourcePath(resolvedPath));
|
e = &parseExprFromFile(checkSourcePath(resolvedPath));
|
||||||
|
|
||||||
cacheFile(path, resolvedPath, e, v, mustBeTrivial);
|
cacheFile(path, resolvedPath, e, v, mustBeTrivial);
|
||||||
}
|
}
|
||||||
|
@ -1159,7 +1159,7 @@ void EvalState::cacheFile(
|
||||||
if (mustBeTrivial &&
|
if (mustBeTrivial &&
|
||||||
!(dynamic_cast<ExprAttrs *>(e)))
|
!(dynamic_cast<ExprAttrs *>(e)))
|
||||||
error<EvalError>("file '%s' must be an attribute set", path).debugThrow();
|
error<EvalError>("file '%s' must be an attribute set", path).debugThrow();
|
||||||
eval(e, v);
|
eval(*e, v);
|
||||||
} catch (Error & e) {
|
} catch (Error & e) {
|
||||||
addErrorTrace(e, "while evaluating the file '%1%':", resolvedPath.to_string());
|
addErrorTrace(e, "while evaluating the file '%1%':", resolvedPath.to_string());
|
||||||
throw;
|
throw;
|
||||||
|
@ -1170,23 +1170,23 @@ void EvalState::cacheFile(
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void EvalState::eval(Expr * e, Value & v)
|
void EvalState::eval(Expr & e, Value & v)
|
||||||
{
|
{
|
||||||
e->eval(*this, baseEnv, v);
|
e.eval(*this, baseEnv, v);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
inline bool EvalState::evalBool(Env & env, Expr * e, const PosIdx pos, std::string_view errorCtx)
|
inline bool EvalState::evalBool(Env & env, Expr & e, const PosIdx pos, std::string_view errorCtx)
|
||||||
{
|
{
|
||||||
try {
|
try {
|
||||||
Value v;
|
Value v;
|
||||||
e->eval(*this, env, v);
|
e.eval(*this, env, v);
|
||||||
if (v.type() != nBool)
|
if (v.type() != nBool)
|
||||||
error<TypeError>(
|
error<TypeError>(
|
||||||
"expected a Boolean but found %1%: %2%",
|
"expected a Boolean but found %1%: %2%",
|
||||||
showType(v),
|
showType(v),
|
||||||
ValuePrinter(*this, v, errorPrintOptions)
|
ValuePrinter(*this, v, errorPrintOptions)
|
||||||
).atPos(pos).withFrame(env, *e).debugThrow();
|
).atPos(pos).withFrame(env, e).debugThrow();
|
||||||
return v.boolean;
|
return v.boolean;
|
||||||
} catch (Error & e) {
|
} catch (Error & e) {
|
||||||
e.addTrace(positions[pos], errorCtx);
|
e.addTrace(positions[pos], errorCtx);
|
||||||
|
@ -1195,16 +1195,16 @@ inline bool EvalState::evalBool(Env & env, Expr * e, const PosIdx pos, std::stri
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
inline void EvalState::evalAttrs(Env & env, Expr * e, Value & v, const PosIdx pos, std::string_view errorCtx)
|
inline void EvalState::evalAttrs(Env & env, Expr & e, Value & v, const PosIdx pos, std::string_view errorCtx)
|
||||||
{
|
{
|
||||||
try {
|
try {
|
||||||
e->eval(*this, env, v);
|
e.eval(*this, env, v);
|
||||||
if (v.type() != nAttrs)
|
if (v.type() != nAttrs)
|
||||||
error<TypeError>(
|
error<TypeError>(
|
||||||
"expected a set but found %1%: %2%",
|
"expected a set but found %1%: %2%",
|
||||||
showType(v),
|
showType(v),
|
||||||
ValuePrinter(*this, v, errorPrintOptions)
|
ValuePrinter(*this, v, errorPrintOptions)
|
||||||
).withFrame(env, *e).debugThrow();
|
).withFrame(env, e).debugThrow();
|
||||||
} catch (Error & e) {
|
} catch (Error & e) {
|
||||||
e.addTrace(positions[pos], errorCtx);
|
e.addTrace(positions[pos], errorCtx);
|
||||||
throw;
|
throw;
|
||||||
|
@ -1247,7 +1247,7 @@ Env * ExprAttrs::buildInheritFromEnv(EvalState & state, Env & up)
|
||||||
inheritEnv.up = &up;
|
inheritEnv.up = &up;
|
||||||
|
|
||||||
Displacement displ = 0;
|
Displacement displ = 0;
|
||||||
for (auto from : *inheritFromExprs)
|
for (auto & from : *inheritFromExprs)
|
||||||
inheritEnv.values[displ++] = from->maybeThunk(state, up);
|
inheritEnv.values[displ++] = from->maybeThunk(state, up);
|
||||||
|
|
||||||
return &inheritEnv;
|
return &inheritEnv;
|
||||||
|
@ -1277,7 +1277,7 @@ void ExprAttrs::eval(EvalState & state, Env & env, Value & v)
|
||||||
Value * vAttr;
|
Value * vAttr;
|
||||||
if (hasOverrides && i.second.kind != AttrDef::Kind::Inherited) {
|
if (hasOverrides && i.second.kind != AttrDef::Kind::Inherited) {
|
||||||
vAttr = state.allocValue();
|
vAttr = state.allocValue();
|
||||||
mkThunk(*vAttr, *i.second.chooseByKind(&env2, &env, inheritEnv), i.second.e);
|
mkThunk(*vAttr, *i.second.chooseByKind(&env2, &env, inheritEnv), *i.second.e);
|
||||||
} else
|
} else
|
||||||
vAttr = i.second.e->maybeThunk(state, *i.second.chooseByKind(&env2, &env, inheritEnv));
|
vAttr = i.second.e->maybeThunk(state, *i.second.chooseByKind(&env2, &env, inheritEnv));
|
||||||
env2.values[displ++] = vAttr;
|
env2.values[displ++] = vAttr;
|
||||||
|
@ -1533,6 +1533,66 @@ public:
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/** Currently these each just take one, but maybe in the future we could have diagnostics
|
||||||
|
* for all unexpected and missing arguments?
|
||||||
|
*/
|
||||||
|
struct FormalsMatch
|
||||||
|
{
|
||||||
|
std::vector<Symbol> missing;
|
||||||
|
std::vector<Symbol> unexpected;
|
||||||
|
};
|
||||||
|
|
||||||
|
/** Matchup an attribute argument set to a lambda's formal arguments,
|
||||||
|
* or return what arguments were required but not given, or given but not allowed.
|
||||||
|
* (currently returns only one, for each).
|
||||||
|
*/
|
||||||
|
FormalsMatch matchupFormals(EvalState & state, Env & env, Displacement & displ, ExprLambda const & lambda, Bindings & attrs)
|
||||||
|
{
|
||||||
|
size_t attrsUsed = 0;
|
||||||
|
|
||||||
|
for (auto const & formal : lambda.formals->formals) {
|
||||||
|
|
||||||
|
// The attribute whose name matches the name of the formal we're matching up, if it exists.
|
||||||
|
Attr const * matchingArg = attrs.get(formal.name);
|
||||||
|
if (matchingArg) {
|
||||||
|
attrsUsed += 1;
|
||||||
|
env.values[displ] = matchingArg->value;
|
||||||
|
displ += 1;
|
||||||
|
|
||||||
|
// We're done here. Move on to the next formal.
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// The argument for this formal wasn't given.
|
||||||
|
// If the formal has a default, use it.
|
||||||
|
if (formal.def) {
|
||||||
|
env.values[displ] = formal.def->maybeThunk(state, env);
|
||||||
|
displ += 1;
|
||||||
|
} else {
|
||||||
|
// Otherwise, let our caller know what was missing.
|
||||||
|
return FormalsMatch{
|
||||||
|
.missing = {formal.name},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check for unexpected extra arguments.
|
||||||
|
if (!lambda.formals->ellipsis && attrsUsed != attrs.size()) {
|
||||||
|
// Return the first unexpected argument.
|
||||||
|
for (Attr const & attr : attrs) {
|
||||||
|
if (!lambda.formals->has(attr.name)) {
|
||||||
|
return FormalsMatch{
|
||||||
|
.unexpected = {attr.name},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
abort(); // unreachable.
|
||||||
|
}
|
||||||
|
|
||||||
|
return FormalsMatch{};
|
||||||
|
}
|
||||||
|
|
||||||
void EvalState::callFunction(Value & fun, size_t nrArgs, Value * * args, Value & vRes, const PosIdx pos)
|
void EvalState::callFunction(Value & fun, size_t nrArgs, Value * * args, Value & vRes, const PosIdx pos)
|
||||||
{
|
{
|
||||||
if (callDepth > evalSettings.maxCallDepth)
|
if (callDepth > evalSettings.maxCallDepth)
|
||||||
|
@ -1586,53 +1646,42 @@ void EvalState::callFunction(Value & fun, size_t nrArgs, Value * * args, Value &
|
||||||
if (lambda.arg)
|
if (lambda.arg)
|
||||||
env2.values[displ++] = args[0];
|
env2.values[displ++] = args[0];
|
||||||
|
|
||||||
/* For each formal argument, get the actual argument. If
|
///* For each formal argument, get the actual argument. If
|
||||||
there is no matching actual argument but the formal
|
// there is no matching actual argument but the formal
|
||||||
argument has a default, use the default. */
|
// argument has a default, use the default. */
|
||||||
size_t attrsUsed = 0;
|
auto const formalsMatch = matchupFormals(
|
||||||
for (auto & i : lambda.formals->formals) {
|
*this,
|
||||||
auto j = args[0]->attrs->get(i.name);
|
env2,
|
||||||
if (!j) {
|
displ,
|
||||||
if (!i.def) {
|
lambda,
|
||||||
error<TypeError>("function '%1%' called without required argument '%2%'",
|
*args[0]->attrs
|
||||||
(lambda.name ? std::string(symbols[lambda.name]) : "anonymous lambda"),
|
);
|
||||||
symbols[i.name])
|
for (auto const & missingArg : formalsMatch.missing) {
|
||||||
.atPos(lambda.pos)
|
auto const missing = symbols[missingArg];
|
||||||
.withTrace(pos, "from call site")
|
error<TypeError>("function '%s' called without required argument '%s'", lambda.getName(symbols), missing)
|
||||||
.withFrame(*fun.lambda.env, lambda)
|
.atPos(lambda.pos)
|
||||||
.debugThrow();
|
.withTrace(pos, "from call site")
|
||||||
}
|
.withFrame(*fun.lambda.env, lambda)
|
||||||
env2.values[displ++] = i.def->maybeThunk(*this, env2);
|
.debugThrow();
|
||||||
} else {
|
}
|
||||||
attrsUsed++;
|
for (auto const & unexpectedArg : formalsMatch.unexpected) {
|
||||||
env2.values[displ++] = j->value;
|
auto const unex = symbols[unexpectedArg];
|
||||||
|
std::set<std::string> formalNames;
|
||||||
|
for (auto const & formal : lambda.formals->formals) {
|
||||||
|
formalNames.insert(symbols[formal.name]);
|
||||||
}
|
}
|
||||||
|
auto sug = Suggestions::bestMatches(formalNames, unex);
|
||||||
|
error<TypeError>("function '%s' called with unexpected argument '%s'", lambda.getName(symbols), unex)
|
||||||
|
.atPos(lambda.pos)
|
||||||
|
.withTrace(pos, "from call site")
|
||||||
|
.withSuggestions(sug)
|
||||||
|
.withFrame(*fun.lambda.env, lambda)
|
||||||
|
.debugThrow();
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Check that each actual argument is listed as a formal
|
|
||||||
argument (unless the attribute match specifies a `...'). */
|
|
||||||
if (!lambda.formals->ellipsis && attrsUsed != args[0]->attrs->size()) {
|
|
||||||
/* Nope, so show the first unexpected argument to the
|
|
||||||
user. */
|
|
||||||
for (auto & i : *args[0]->attrs)
|
|
||||||
if (!lambda.formals->has(i.name)) {
|
|
||||||
std::set<std::string> formalNames;
|
|
||||||
for (auto & formal : lambda.formals->formals)
|
|
||||||
formalNames.insert(symbols[formal.name]);
|
|
||||||
auto suggestions = Suggestions::bestMatches(formalNames, symbols[i.name]);
|
|
||||||
error<TypeError>("function '%1%' called with unexpected argument '%2%'",
|
|
||||||
(lambda.name ? std::string(symbols[lambda.name]) : "anonymous lambda"),
|
|
||||||
symbols[i.name])
|
|
||||||
.atPos(lambda.pos)
|
|
||||||
.withTrace(pos, "from call site")
|
|
||||||
.withSuggestions(suggestions)
|
|
||||||
.withFrame(*fun.lambda.env, lambda)
|
|
||||||
.debugThrow();
|
|
||||||
}
|
|
||||||
abort(); // can't happen
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
nrFunctionCalls++;
|
nrFunctionCalls++;
|
||||||
if (countCalls) incrFunctionCall(&lambda);
|
if (countCalls) incrFunctionCall(&lambda);
|
||||||
|
|
||||||
|
@ -1642,9 +1691,7 @@ void EvalState::callFunction(Value & fun, size_t nrArgs, Value * * args, Value &
|
||||||
? makeDebugTraceStacker(
|
? makeDebugTraceStacker(
|
||||||
*this, *lambda.body, env2, positions[lambda.pos],
|
*this, *lambda.body, env2, positions[lambda.pos],
|
||||||
"while calling %s",
|
"while calling %s",
|
||||||
lambda.name
|
lambda.getQuotedName(symbols))
|
||||||
? concatStrings("'", symbols[lambda.name], "'")
|
|
||||||
: "anonymous lambda")
|
|
||||||
: nullptr;
|
: nullptr;
|
||||||
|
|
||||||
lambda.body->eval(*this, env2, vCur);
|
lambda.body->eval(*this, env2, vCur);
|
||||||
|
@ -1654,9 +1701,7 @@ void EvalState::callFunction(Value & fun, size_t nrArgs, Value * * args, Value &
|
||||||
e,
|
e,
|
||||||
lambda.pos,
|
lambda.pos,
|
||||||
"while calling %s",
|
"while calling %s",
|
||||||
lambda.name
|
lambda.getQuotedName(symbols));
|
||||||
? concatStrings("'", symbols[lambda.name], "'")
|
|
||||||
: "anonymous lambda");
|
|
||||||
if (pos) addErrorTrace(e, pos, "from call site");
|
if (pos) addErrorTrace(e, pos, "from call site");
|
||||||
}
|
}
|
||||||
throw;
|
throw;
|
||||||
|
@ -1872,13 +1917,13 @@ void ExprWith::eval(EvalState & state, Env & env, Value & v)
|
||||||
void ExprIf::eval(EvalState & state, Env & env, Value & v)
|
void ExprIf::eval(EvalState & state, Env & env, Value & v)
|
||||||
{
|
{
|
||||||
// We cheat in the parser, and pass the position of the condition as the position of the if itself.
|
// We cheat in the parser, and pass the position of the condition as the position of the if itself.
|
||||||
(state.evalBool(env, cond, pos, "while evaluating a branch condition") ? then : else_)->eval(state, env, v);
|
(state.evalBool(env, *cond, pos, "while evaluating a branch condition") ? *then : *else_).eval(state, env, v);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void ExprAssert::eval(EvalState & state, Env & env, Value & v)
|
void ExprAssert::eval(EvalState & state, Env & env, Value & v)
|
||||||
{
|
{
|
||||||
if (!state.evalBool(env, cond, pos, "in the condition of the assert statement")) {
|
if (!state.evalBool(env, *cond, pos, "in the condition of the assert statement")) {
|
||||||
std::ostringstream out;
|
std::ostringstream out;
|
||||||
cond->show(state.symbols, out);
|
cond->show(state.symbols, out);
|
||||||
state.error<AssertionError>("assertion '%1%' failed", out.str()).atPos(pos).withFrame(env, *this).debugThrow();
|
state.error<AssertionError>("assertion '%1%' failed", out.str()).atPos(pos).withFrame(env, *this).debugThrow();
|
||||||
|
@ -1889,7 +1934,7 @@ void ExprAssert::eval(EvalState & state, Env & env, Value & v)
|
||||||
|
|
||||||
void ExprOpNot::eval(EvalState & state, Env & env, Value & v)
|
void ExprOpNot::eval(EvalState & state, Env & env, Value & v)
|
||||||
{
|
{
|
||||||
v.mkBool(!state.evalBool(env, e, getPos(), "in the argument of the not operator")); // XXX: FIXME: !
|
v.mkBool(!state.evalBool(env, *e, getPos(), "in the argument of the not operator")); // XXX: FIXME: !
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -1911,27 +1956,27 @@ void ExprOpNEq::eval(EvalState & state, Env & env, Value & v)
|
||||||
|
|
||||||
void ExprOpAnd::eval(EvalState & state, Env & env, Value & v)
|
void ExprOpAnd::eval(EvalState & state, Env & env, Value & v)
|
||||||
{
|
{
|
||||||
v.mkBool(state.evalBool(env, e1, pos, "in the left operand of the AND (&&) operator") && state.evalBool(env, e2, pos, "in the right operand of the AND (&&) operator"));
|
v.mkBool(state.evalBool(env, *e1, pos, "in the left operand of the AND (&&) operator") && state.evalBool(env, *e2, pos, "in the right operand of the AND (&&) operator"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void ExprOpOr::eval(EvalState & state, Env & env, Value & v)
|
void ExprOpOr::eval(EvalState & state, Env & env, Value & v)
|
||||||
{
|
{
|
||||||
v.mkBool(state.evalBool(env, e1, pos, "in the left operand of the OR (||) operator") || state.evalBool(env, e2, pos, "in the right operand of the OR (||) operator"));
|
v.mkBool(state.evalBool(env, *e1, pos, "in the left operand of the OR (||) operator") || state.evalBool(env, *e2, pos, "in the right operand of the OR (||) operator"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void ExprOpImpl::eval(EvalState & state, Env & env, Value & v)
|
void ExprOpImpl::eval(EvalState & state, Env & env, Value & v)
|
||||||
{
|
{
|
||||||
v.mkBool(!state.evalBool(env, e1, pos, "in the left operand of the IMPL (->) operator") || state.evalBool(env, e2, pos, "in the right operand of the IMPL (->) operator"));
|
v.mkBool(!state.evalBool(env, *e1, pos, "in the left operand of the IMPL (->) operator") || state.evalBool(env, *e2, pos, "in the right operand of the IMPL (->) operator"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void ExprOpUpdate::eval(EvalState & state, Env & env, Value & v)
|
void ExprOpUpdate::eval(EvalState & state, Env & env, Value & v)
|
||||||
{
|
{
|
||||||
Value v1, v2;
|
Value v1, v2;
|
||||||
state.evalAttrs(env, e1, v1, pos, "in the left operand of the update (//) operator");
|
state.evalAttrs(env, *e1, v1, pos, "in the left operand of the update (//) operator");
|
||||||
state.evalAttrs(env, e2, v2, pos, "in the right operand of the update (//) operator");
|
state.evalAttrs(env, *e2, v2, pos, "in the right operand of the update (//) operator");
|
||||||
|
|
||||||
state.nrOpUpdates++;
|
state.nrOpUpdates++;
|
||||||
|
|
||||||
|
@ -2035,10 +2080,10 @@ void ExprConcatStrings::eval(EvalState & state, Env & env, Value & v)
|
||||||
};
|
};
|
||||||
|
|
||||||
// List of returned strings. References to these Values must NOT be persisted.
|
// List of returned strings. References to these Values must NOT be persisted.
|
||||||
SmallTemporaryValueVector<conservativeStackReservation> values(es->size());
|
SmallTemporaryValueVector<conservativeStackReservation> values(es.size());
|
||||||
Value * vTmpP = values.data();
|
Value * vTmpP = values.data();
|
||||||
|
|
||||||
for (auto & [i_pos, i] : *es) {
|
for (auto & [i_pos, i] : es) {
|
||||||
Value & vTmp = *vTmpP++;
|
Value & vTmp = *vTmpP++;
|
||||||
i->eval(state, env, vTmp);
|
i->eval(state, env, vTmp);
|
||||||
|
|
||||||
|
@ -2068,7 +2113,7 @@ void ExprConcatStrings::eval(EvalState & state, Env & env, Value & v)
|
||||||
} else
|
} else
|
||||||
state.error<EvalError>("cannot add %1% to a float", showType(vTmp)).atPos(i_pos).withFrame(env, *this).debugThrow();
|
state.error<EvalError>("cannot add %1% to a float", showType(vTmp)).atPos(i_pos).withFrame(env, *this).debugThrow();
|
||||||
} else {
|
} else {
|
||||||
if (s.empty()) s.reserve(es->size());
|
if (s.empty()) s.reserve(es.size());
|
||||||
/* skip canonization of first path, which would only be not
|
/* skip canonization of first path, which would only be not
|
||||||
canonized in the first place if it's coming from a ./${foo} type
|
canonized in the first place if it's coming from a ./${foo} type
|
||||||
path */
|
path */
|
||||||
|
@ -2752,39 +2797,39 @@ SourcePath resolveExprPath(SourcePath path)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
Expr * EvalState::parseExprFromFile(const SourcePath & path)
|
Expr & EvalState::parseExprFromFile(const SourcePath & path)
|
||||||
{
|
{
|
||||||
return parseExprFromFile(path, staticBaseEnv);
|
return parseExprFromFile(path, staticBaseEnv);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
Expr * EvalState::parseExprFromFile(const SourcePath & path, std::shared_ptr<StaticEnv> & staticEnv)
|
Expr & EvalState::parseExprFromFile(const SourcePath & path, std::shared_ptr<StaticEnv> & staticEnv)
|
||||||
{
|
{
|
||||||
auto buffer = path.readFile();
|
auto buffer = path.readFile();
|
||||||
// readFile hopefully have left some extra space for terminators
|
// readFile hopefully have left some extra space for terminators
|
||||||
buffer.append("\0\0", 2);
|
buffer.append("\0\0", 2);
|
||||||
return parse(buffer.data(), buffer.size(), Pos::Origin(path), path.parent(), staticEnv);
|
return *parse(buffer.data(), buffer.size(), Pos::Origin(path), path.parent(), staticEnv);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
Expr * EvalState::parseExprFromString(std::string s_, const SourcePath & basePath, std::shared_ptr<StaticEnv> & staticEnv)
|
Expr & EvalState::parseExprFromString(std::string s_, const SourcePath & basePath, std::shared_ptr<StaticEnv> & staticEnv)
|
||||||
{
|
{
|
||||||
// NOTE this method (and parseStdin) must take care to *fully copy* their input
|
// NOTE this method (and parseStdin) must take care to *fully copy* their input
|
||||||
// into their respective Pos::Origin until the parser stops overwriting its input
|
// into their respective Pos::Origin until the parser stops overwriting its input
|
||||||
// data.
|
// data.
|
||||||
auto s = make_ref<std::string>(s_);
|
auto s = make_ref<std::string>(s_);
|
||||||
s_.append("\0\0", 2);
|
s_.append("\0\0", 2);
|
||||||
return parse(s_.data(), s_.size(), Pos::String{.source = s}, basePath, staticEnv);
|
return *parse(s_.data(), s_.size(), Pos::String{.source = s}, basePath, staticEnv);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
Expr * EvalState::parseExprFromString(std::string s, const SourcePath & basePath)
|
Expr & EvalState::parseExprFromString(std::string s, const SourcePath & basePath)
|
||||||
{
|
{
|
||||||
return parseExprFromString(std::move(s), basePath, staticBaseEnv);
|
return parseExprFromString(std::move(s), basePath, staticBaseEnv);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
Expr * EvalState::parseStdin()
|
Expr & EvalState::parseStdin()
|
||||||
{
|
{
|
||||||
// NOTE this method (and parseExprFromString) must take care to *fully copy* their
|
// NOTE this method (and parseExprFromString) must take care to *fully copy* their
|
||||||
// input into their respective Pos::Origin until the parser stops overwriting its
|
// input into their respective Pos::Origin until the parser stops overwriting its
|
||||||
|
@ -2794,7 +2839,7 @@ Expr * EvalState::parseStdin()
|
||||||
// drainFD should have left some extra space for terminators
|
// drainFD should have left some extra space for terminators
|
||||||
auto s = make_ref<std::string>(buffer);
|
auto s = make_ref<std::string>(buffer);
|
||||||
buffer.append("\0\0", 2);
|
buffer.append("\0\0", 2);
|
||||||
return parse(buffer.data(), buffer.size(), Pos::Stdin{.source = s}, rootPath(CanonPath::fromCwd()), staticBaseEnv);
|
return *parse(buffer.data(), buffer.size(), Pos::Stdin{.source = s}, rootPath(CanonPath::fromCwd()), staticBaseEnv);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -340,16 +340,16 @@ public:
|
||||||
/**
|
/**
|
||||||
* Parse a Nix expression from the specified file.
|
* Parse a Nix expression from the specified file.
|
||||||
*/
|
*/
|
||||||
Expr * parseExprFromFile(const SourcePath & path);
|
Expr & parseExprFromFile(const SourcePath & path);
|
||||||
Expr * parseExprFromFile(const SourcePath & path, std::shared_ptr<StaticEnv> & staticEnv);
|
Expr & parseExprFromFile(const SourcePath & path, std::shared_ptr<StaticEnv> & staticEnv);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Parse a Nix expression from the specified string.
|
* Parse a Nix expression from the specified string.
|
||||||
*/
|
*/
|
||||||
Expr * parseExprFromString(std::string s, const SourcePath & basePath, std::shared_ptr<StaticEnv> & staticEnv);
|
Expr & parseExprFromString(std::string s, const SourcePath & basePath, std::shared_ptr<StaticEnv> & staticEnv);
|
||||||
Expr * parseExprFromString(std::string s, const SourcePath & basePath);
|
Expr & parseExprFromString(std::string s, const SourcePath & basePath);
|
||||||
|
|
||||||
Expr * parseStdin();
|
Expr & parseStdin();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Evaluate an expression read from the given file to normal
|
* Evaluate an expression read from the given file to normal
|
||||||
|
@ -390,15 +390,15 @@ public:
|
||||||
*
|
*
|
||||||
* @param [out] v The resulting is stored here.
|
* @param [out] v The resulting is stored here.
|
||||||
*/
|
*/
|
||||||
void eval(Expr * e, Value & v);
|
void eval(Expr & e, Value & v);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Evaluation the expression, then verify that it has the expected
|
* Evaluation the expression, then verify that it has the expected
|
||||||
* type.
|
* type.
|
||||||
*/
|
*/
|
||||||
inline bool evalBool(Env & env, Expr * e);
|
inline bool evalBool(Env & env, Expr & e);
|
||||||
inline bool evalBool(Env & env, Expr * e, const PosIdx pos, std::string_view errorCtx);
|
inline bool evalBool(Env & env, Expr & e, const PosIdx pos, std::string_view errorCtx);
|
||||||
inline void evalAttrs(Env & env, Expr * e, Value & v, const PosIdx pos, std::string_view errorCtx);
|
inline void evalAttrs(Env & env, Expr & e, Value & v, const PosIdx pos, std::string_view errorCtx);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* If `v` is a thunk, enter it and overwrite `v` with the result
|
* If `v` is a thunk, enter it and overwrite `v` with the result
|
||||||
|
@ -619,7 +619,7 @@ public:
|
||||||
}
|
}
|
||||||
|
|
||||||
void mkList(Value & v, size_t length);
|
void mkList(Value & v, size_t length);
|
||||||
void mkThunk_(Value & v, Expr * expr);
|
void mkThunk_(Value & v, Expr & expr);
|
||||||
void mkPos(Value & v, PosIdx pos);
|
void mkPos(Value & v, PosIdx pos);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -32,6 +32,12 @@
|
||||||
|
|
||||||
using namespace nix;
|
using namespace nix;
|
||||||
|
|
||||||
|
#define THROW(...) \
|
||||||
|
do { \
|
||||||
|
state->error.reset(new auto(__VA_ARGS__)); \
|
||||||
|
return YYerror; \
|
||||||
|
} while (0)
|
||||||
|
|
||||||
namespace nix {
|
namespace nix {
|
||||||
|
|
||||||
#define CUR_POS state->at(*yylloc)
|
#define CUR_POS state->at(*yylloc)
|
||||||
|
@ -135,20 +141,20 @@ or { return OR_KW; }
|
||||||
if (numMay.has_value()) {
|
if (numMay.has_value()) {
|
||||||
yylval->n = *numMay;
|
yylval->n = *numMay;
|
||||||
} else {
|
} else {
|
||||||
throw ParseError(ErrorInfo{
|
THROW(ParseError(ErrorInfo{
|
||||||
.msg = HintFmt("invalid integer '%1%'", yytext),
|
.msg = HintFmt("invalid integer '%1%'", yytext),
|
||||||
.pos = state->positions[CUR_POS],
|
.pos = state->positions[CUR_POS],
|
||||||
});
|
}));
|
||||||
}
|
}
|
||||||
return INT;
|
return INT;
|
||||||
}
|
}
|
||||||
{FLOAT} { errno = 0;
|
{FLOAT} { errno = 0;
|
||||||
yylval->nf = strtod(yytext, 0);
|
yylval->nf = strtod(yytext, 0);
|
||||||
if (errno != 0)
|
if (errno != 0)
|
||||||
throw ParseError(ErrorInfo{
|
THROW(ParseError(ErrorInfo{
|
||||||
.msg = HintFmt("invalid float '%1%'", yytext),
|
.msg = HintFmt("invalid float '%1%'", yytext),
|
||||||
.pos = state->positions[CUR_POS],
|
.pos = state->positions[CUR_POS],
|
||||||
});
|
}));
|
||||||
return FLOAT;
|
return FLOAT;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -274,10 +280,10 @@ or { return OR_KW; }
|
||||||
|
|
||||||
<INPATH_SLASH>{ANY} |
|
<INPATH_SLASH>{ANY} |
|
||||||
<INPATH_SLASH><<EOF>> {
|
<INPATH_SLASH><<EOF>> {
|
||||||
throw ParseError(ErrorInfo{
|
THROW(ParseError(ErrorInfo{
|
||||||
.msg = HintFmt("path has a trailing slash"),
|
.msg = HintFmt("path has a trailing slash"),
|
||||||
.pos = state->positions[CUR_POS],
|
.pos = state->positions[CUR_POS],
|
||||||
});
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
{SPATH} { yylval->path = {yytext, (size_t) yyleng}; return SPATH; }
|
{SPATH} { yylval->path = {yytext, (size_t) yyleng}; return SPATH; }
|
||||||
|
|
|
@ -79,7 +79,7 @@ void ExprAttrs::showBindings(const SymbolTable & symbols, std::ostream & str) co
|
||||||
return sa < sb;
|
return sa < sb;
|
||||||
});
|
});
|
||||||
std::vector<Symbol> inherits;
|
std::vector<Symbol> inherits;
|
||||||
std::map<ExprInheritFrom *, std::vector<Symbol>> inheritsFrom;
|
std::map<Displacement, std::vector<Symbol>> inheritsFrom;
|
||||||
for (auto & i : sorted) {
|
for (auto & i : sorted) {
|
||||||
switch (i->second.kind) {
|
switch (i->second.kind) {
|
||||||
case AttrDef::Kind::Plain:
|
case AttrDef::Kind::Plain:
|
||||||
|
@ -90,7 +90,7 @@ void ExprAttrs::showBindings(const SymbolTable & symbols, std::ostream & str) co
|
||||||
case AttrDef::Kind::InheritedFrom: {
|
case AttrDef::Kind::InheritedFrom: {
|
||||||
auto & select = dynamic_cast<ExprSelect &>(*i->second.e);
|
auto & select = dynamic_cast<ExprSelect &>(*i->second.e);
|
||||||
auto & from = dynamic_cast<ExprInheritFrom &>(*select.e);
|
auto & from = dynamic_cast<ExprInheritFrom &>(*select.e);
|
||||||
inheritsFrom[&from].push_back(i->first);
|
inheritsFrom[from.displ].push_back(i->first);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -102,7 +102,7 @@ void ExprAttrs::showBindings(const SymbolTable & symbols, std::ostream & str) co
|
||||||
}
|
}
|
||||||
for (const auto & [from, syms] : inheritsFrom) {
|
for (const auto & [from, syms] : inheritsFrom) {
|
||||||
str << "inherit (";
|
str << "inherit (";
|
||||||
(*inheritFromExprs)[from->displ]->show(symbols, str);
|
(*inheritFromExprs)[from]->show(symbols, str);
|
||||||
str << ")";
|
str << ")";
|
||||||
for (auto sym : syms) str << " " << symbols[sym];
|
for (auto sym : syms) str << " " << symbols[sym];
|
||||||
str << "; ";
|
str << "; ";
|
||||||
|
@ -151,7 +151,7 @@ void ExprLambda::show(const SymbolTable & symbols, std::ostream & str) const
|
||||||
// the natural Symbol ordering is by creation time, which can lead to the
|
// the natural Symbol ordering is by creation time, which can lead to the
|
||||||
// same expression being printed in two different ways depending on its
|
// same expression being printed in two different ways depending on its
|
||||||
// context. always use lexicographic ordering to avoid this.
|
// context. always use lexicographic ordering to avoid this.
|
||||||
for (auto & i : formals->lexicographicOrder(symbols)) {
|
for (const Formal & i : formals->lexicographicOrder(symbols)) {
|
||||||
if (first) first = false; else str << ", ";
|
if (first) first = false; else str << ", ";
|
||||||
str << symbols[i.name];
|
str << symbols[i.name];
|
||||||
if (i.def) {
|
if (i.def) {
|
||||||
|
@ -176,7 +176,7 @@ void ExprCall::show(const SymbolTable & symbols, std::ostream & str) const
|
||||||
{
|
{
|
||||||
str << '(';
|
str << '(';
|
||||||
fun->show(symbols, str);
|
fun->show(symbols, str);
|
||||||
for (auto e : args) {
|
for (auto & e : args) {
|
||||||
str << ' ';
|
str << ' ';
|
||||||
e->show(symbols, str);
|
e->show(symbols, str);
|
||||||
}
|
}
|
||||||
|
@ -231,7 +231,7 @@ void ExprConcatStrings::show(const SymbolTable & symbols, std::ostream & str) co
|
||||||
{
|
{
|
||||||
bool first = true;
|
bool first = true;
|
||||||
str << "(";
|
str << "(";
|
||||||
for (auto & i : *es) {
|
for (auto & i : es) {
|
||||||
if (first) first = false; else str << " + ";
|
if (first) first = false; else str << " + ";
|
||||||
i.second->show(symbols, str);
|
i.second->show(symbols, str);
|
||||||
}
|
}
|
||||||
|
@ -375,7 +375,7 @@ std::shared_ptr<const StaticEnv> ExprAttrs::bindInheritSources(
|
||||||
// not even *have* an expr that grabs anything from this env since it's fully
|
// not even *have* an expr that grabs anything from this env since it's fully
|
||||||
// invisible, but the evaluator does not allow for this yet.
|
// invisible, but the evaluator does not allow for this yet.
|
||||||
auto inner = std::make_shared<StaticEnv>(nullptr, env.get(), 0);
|
auto inner = std::make_shared<StaticEnv>(nullptr, env.get(), 0);
|
||||||
for (auto from : *inheritFromExprs)
|
for (auto & from : *inheritFromExprs)
|
||||||
from->bindVars(es, env);
|
from->bindVars(es, env);
|
||||||
|
|
||||||
return inner;
|
return inner;
|
||||||
|
@ -462,7 +462,7 @@ void ExprCall::bindVars(EvalState & es, const std::shared_ptr<const StaticEnv> &
|
||||||
es.exprEnvs.insert(std::make_pair(this, env));
|
es.exprEnvs.insert(std::make_pair(this, env));
|
||||||
|
|
||||||
fun->bindVars(es, env);
|
fun->bindVars(es, env);
|
||||||
for (auto e : args)
|
for (auto & e : args)
|
||||||
e->bindVars(es, env);
|
e->bindVars(es, env);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -547,7 +547,7 @@ void ExprConcatStrings::bindVars(EvalState & es, const std::shared_ptr<const Sta
|
||||||
if (es.debugRepl)
|
if (es.debugRepl)
|
||||||
es.exprEnvs.insert(std::make_pair(this, env));
|
es.exprEnvs.insert(std::make_pair(this, env));
|
||||||
|
|
||||||
for (auto & i : *this->es)
|
for (auto & i : this->es)
|
||||||
i.second->bindVars(es, env);
|
i.second->bindVars(es, env);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -28,9 +28,9 @@ struct StaticEnv;
|
||||||
struct AttrName
|
struct AttrName
|
||||||
{
|
{
|
||||||
Symbol symbol;
|
Symbol symbol;
|
||||||
Expr * expr;
|
std::unique_ptr<Expr> expr;
|
||||||
AttrName(Symbol s) : symbol(s) {};
|
AttrName(Symbol s) : symbol(s) {};
|
||||||
AttrName(Expr * e) : expr(e) {};
|
AttrName(std::unique_ptr<Expr> e) : expr(std::move(e)) {};
|
||||||
};
|
};
|
||||||
|
|
||||||
typedef std::vector<AttrName> AttrPath;
|
typedef std::vector<AttrName> AttrPath;
|
||||||
|
@ -42,12 +42,21 @@ std::string showAttrPath(const SymbolTable & symbols, const AttrPath & attrPath)
|
||||||
|
|
||||||
struct Expr
|
struct Expr
|
||||||
{
|
{
|
||||||
|
protected:
|
||||||
|
Expr(Expr &&) = default;
|
||||||
|
Expr & operator=(Expr &&) = default;
|
||||||
|
|
||||||
|
public:
|
||||||
struct AstSymbols {
|
struct AstSymbols {
|
||||||
Symbol sub, lessThan, mul, div, or_, findFile, nixPath, body;
|
Symbol sub, lessThan, mul, div, or_, findFile, nixPath, body;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
Expr() = default;
|
||||||
|
Expr(const Expr &) = delete;
|
||||||
|
Expr & operator=(const Expr &) = delete;
|
||||||
virtual ~Expr() { };
|
virtual ~Expr() { };
|
||||||
|
|
||||||
virtual void show(const SymbolTable & symbols, std::ostream & str) const;
|
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(EvalState & es, const std::shared_ptr<const StaticEnv> & env);
|
||||||
virtual void eval(EvalState & state, Env & env, Value & v);
|
virtual void eval(EvalState & state, Env & env, Value & v);
|
||||||
|
@ -148,19 +157,19 @@ struct ExprInheritFrom : ExprVar
|
||||||
struct ExprSelect : Expr
|
struct ExprSelect : Expr
|
||||||
{
|
{
|
||||||
PosIdx pos;
|
PosIdx pos;
|
||||||
Expr * e, * def;
|
std::unique_ptr<Expr> e, def;
|
||||||
AttrPath attrPath;
|
AttrPath attrPath;
|
||||||
ExprSelect(const PosIdx & pos, Expr * e, AttrPath attrPath, Expr * def) : pos(pos), e(e), def(def), attrPath(std::move(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, Expr * e, Symbol name) : pos(pos), e(e), def(0) { attrPath.push_back(AttrName(name)); };
|
ExprSelect(const PosIdx & pos, std::unique_ptr<Expr> e, Symbol name) : pos(pos), e(std::move(e)) { attrPath.push_back(AttrName(name)); };
|
||||||
PosIdx getPos() const override { return pos; }
|
PosIdx getPos() const override { return pos; }
|
||||||
COMMON_METHODS
|
COMMON_METHODS
|
||||||
};
|
};
|
||||||
|
|
||||||
struct ExprOpHasAttr : Expr
|
struct ExprOpHasAttr : Expr
|
||||||
{
|
{
|
||||||
Expr * e;
|
std::unique_ptr<Expr> e;
|
||||||
AttrPath attrPath;
|
AttrPath attrPath;
|
||||||
ExprOpHasAttr(Expr * e, AttrPath attrPath) : e(e), attrPath(std::move(attrPath)) { };
|
ExprOpHasAttr(std::unique_ptr<Expr> e, AttrPath attrPath) : e(std::move(e)), attrPath(std::move(attrPath)) { };
|
||||||
PosIdx getPos() const override { return e->getPos(); }
|
PosIdx getPos() const override { return e->getPos(); }
|
||||||
COMMON_METHODS
|
COMMON_METHODS
|
||||||
};
|
};
|
||||||
|
@ -180,11 +189,11 @@ struct ExprAttrs : Expr
|
||||||
};
|
};
|
||||||
|
|
||||||
Kind kind;
|
Kind kind;
|
||||||
Expr * e;
|
std::unique_ptr<Expr> e;
|
||||||
PosIdx pos;
|
PosIdx pos;
|
||||||
Displacement displ; // displacement
|
Displacement displ; // displacement
|
||||||
AttrDef(Expr * e, const PosIdx & pos, Kind kind = Kind::Plain)
|
AttrDef(std::unique_ptr<Expr> e, const PosIdx & pos, Kind kind = Kind::Plain)
|
||||||
: kind(kind), e(e), pos(pos) { };
|
: kind(kind), e(std::move(e)), pos(pos) { };
|
||||||
AttrDef() { };
|
AttrDef() { };
|
||||||
|
|
||||||
template<typename T>
|
template<typename T>
|
||||||
|
@ -203,12 +212,12 @@ struct ExprAttrs : Expr
|
||||||
};
|
};
|
||||||
typedef std::map<Symbol, AttrDef> AttrDefs;
|
typedef std::map<Symbol, AttrDef> AttrDefs;
|
||||||
AttrDefs attrs;
|
AttrDefs attrs;
|
||||||
std::unique_ptr<std::vector<Expr *>> inheritFromExprs;
|
std::unique_ptr<std::vector<std::unique_ptr<Expr>>> inheritFromExprs;
|
||||||
struct DynamicAttrDef {
|
struct DynamicAttrDef {
|
||||||
Expr * nameExpr, * valueExpr;
|
std::unique_ptr<Expr> nameExpr, valueExpr;
|
||||||
PosIdx pos;
|
PosIdx pos;
|
||||||
DynamicAttrDef(Expr * nameExpr, Expr * valueExpr, const PosIdx & pos)
|
DynamicAttrDef(std::unique_ptr<Expr> nameExpr, std::unique_ptr<Expr> valueExpr, const PosIdx & pos)
|
||||||
: nameExpr(nameExpr), valueExpr(valueExpr), pos(pos) { };
|
: nameExpr(std::move(nameExpr)), valueExpr(std::move(valueExpr)), pos(pos) { };
|
||||||
};
|
};
|
||||||
typedef std::vector<DynamicAttrDef> DynamicAttrDefs;
|
typedef std::vector<DynamicAttrDef> DynamicAttrDefs;
|
||||||
DynamicAttrDefs dynamicAttrs;
|
DynamicAttrDefs dynamicAttrs;
|
||||||
|
@ -225,7 +234,7 @@ struct ExprAttrs : Expr
|
||||||
|
|
||||||
struct ExprList : Expr
|
struct ExprList : Expr
|
||||||
{
|
{
|
||||||
std::vector<Expr *> elems;
|
std::vector<std::unique_ptr<Expr>> elems;
|
||||||
ExprList() { };
|
ExprList() { };
|
||||||
COMMON_METHODS
|
COMMON_METHODS
|
||||||
Value * maybeThunk(EvalState & state, Env & env) override;
|
Value * maybeThunk(EvalState & state, Env & env) override;
|
||||||
|
@ -240,7 +249,7 @@ struct Formal
|
||||||
{
|
{
|
||||||
PosIdx pos;
|
PosIdx pos;
|
||||||
Symbol name;
|
Symbol name;
|
||||||
Expr * def;
|
std::unique_ptr<Expr> def;
|
||||||
};
|
};
|
||||||
|
|
||||||
/** Attribute set destructuring in arguments of a lambda, if present */
|
/** Attribute set destructuring in arguments of a lambda, if present */
|
||||||
|
@ -257,9 +266,9 @@ struct Formals
|
||||||
return it != formals.end() && it->name == arg;
|
return it != formals.end() && it->name == arg;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<Formal> lexicographicOrder(const SymbolTable & symbols) const
|
std::vector<std::reference_wrapper<const Formal>> lexicographicOrder(const SymbolTable & symbols) const
|
||||||
{
|
{
|
||||||
std::vector<Formal> result(formals.begin(), formals.end());
|
std::vector<std::reference_wrapper<const Formal>> result(formals.begin(), formals.end());
|
||||||
std::sort(result.begin(), result.end(),
|
std::sort(result.begin(), result.end(),
|
||||||
[&] (const Formal & a, const Formal & b) {
|
[&] (const Formal & a, const Formal & b) {
|
||||||
std::string_view sa = symbols[a.name], sb = symbols[b.name];
|
std::string_view sa = symbols[a.name], sb = symbols[b.name];
|
||||||
|
@ -283,30 +292,55 @@ struct ExprLambda : Expr
|
||||||
Symbol arg;
|
Symbol arg;
|
||||||
/** Formals are present when the lambda destructures an attr set as
|
/** Formals are present when the lambda destructures an attr set as
|
||||||
* argument, with or without ellipsis */
|
* argument, with or without ellipsis */
|
||||||
Formals * formals;
|
std::unique_ptr<Formals> formals;
|
||||||
Expr * body;
|
std::unique_ptr<Expr> body;
|
||||||
ExprLambda(PosIdx pos, Symbol arg, Formals * formals, Expr * body)
|
ExprLambda(PosIdx pos, Symbol arg, std::unique_ptr<Formals> formals, std::unique_ptr<Expr> body)
|
||||||
: pos(pos), arg(arg), formals(formals), body(body)
|
: pos(pos), arg(arg), formals(std::move(formals)), body(std::move(body))
|
||||||
{
|
{
|
||||||
};
|
};
|
||||||
ExprLambda(PosIdx pos, Formals * formals, Expr * body)
|
ExprLambda(PosIdx pos, std::unique_ptr<Formals> formals, std::unique_ptr<Expr> body)
|
||||||
: pos(pos), formals(formals), body(body)
|
: pos(pos), formals(std::move(formals)), body(std::move(body))
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
void setName(Symbol name) override;
|
void setName(Symbol name) override;
|
||||||
std::string showNamePos(const EvalState & state) const;
|
std::string showNamePos(const EvalState & state) const;
|
||||||
inline bool hasFormals() const { return formals != nullptr; }
|
inline bool hasFormals() const { return formals != nullptr; }
|
||||||
PosIdx getPos() const override { return pos; }
|
PosIdx getPos() const override { return pos; }
|
||||||
|
|
||||||
|
/** Returns the name of the lambda,
|
||||||
|
* or "anonymous lambda" if it doesn't have one.
|
||||||
|
*/
|
||||||
|
inline std::string getName(SymbolTable const & symbols) const
|
||||||
|
{
|
||||||
|
if (this->name) {
|
||||||
|
return symbols[this->name];
|
||||||
|
}
|
||||||
|
|
||||||
|
return "anonymous lambda";
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Returns the name of the lambda in single quotes,
|
||||||
|
* or "anonymous lambda" if it doesn't have one.
|
||||||
|
*/
|
||||||
|
inline std::string getQuotedName(SymbolTable const & symbols) const
|
||||||
|
{
|
||||||
|
if (this->name) {
|
||||||
|
return concatStrings("'", symbols[this->name], "'");
|
||||||
|
}
|
||||||
|
|
||||||
|
return "anonymous lambda";
|
||||||
|
}
|
||||||
|
|
||||||
COMMON_METHODS
|
COMMON_METHODS
|
||||||
};
|
};
|
||||||
|
|
||||||
struct ExprCall : Expr
|
struct ExprCall : Expr
|
||||||
{
|
{
|
||||||
Expr * fun;
|
std::unique_ptr<Expr> fun;
|
||||||
std::vector<Expr *> args;
|
std::vector<std::unique_ptr<Expr>> args;
|
||||||
PosIdx pos;
|
PosIdx pos;
|
||||||
ExprCall(const PosIdx & pos, Expr * fun, std::vector<Expr *> && args)
|
ExprCall(const PosIdx & pos, std::unique_ptr<Expr> fun, std::vector<std::unique_ptr<Expr>> && args)
|
||||||
: fun(fun), args(args), pos(pos)
|
: fun(std::move(fun)), args(std::move(args)), pos(pos)
|
||||||
{ }
|
{ }
|
||||||
PosIdx getPos() const override { return pos; }
|
PosIdx getPos() const override { return pos; }
|
||||||
COMMON_METHODS
|
COMMON_METHODS
|
||||||
|
@ -314,19 +348,19 @@ struct ExprCall : Expr
|
||||||
|
|
||||||
struct ExprLet : Expr
|
struct ExprLet : Expr
|
||||||
{
|
{
|
||||||
ExprAttrs * attrs;
|
std::unique_ptr<ExprAttrs> attrs;
|
||||||
Expr * body;
|
std::unique_ptr<Expr> body;
|
||||||
ExprLet(ExprAttrs * attrs, Expr * body) : attrs(attrs), body(body) { };
|
ExprLet(std::unique_ptr<ExprAttrs> attrs, std::unique_ptr<Expr> body) : attrs(std::move(attrs)), body(std::move(body)) { };
|
||||||
COMMON_METHODS
|
COMMON_METHODS
|
||||||
};
|
};
|
||||||
|
|
||||||
struct ExprWith : Expr
|
struct ExprWith : Expr
|
||||||
{
|
{
|
||||||
PosIdx pos;
|
PosIdx pos;
|
||||||
Expr * attrs, * body;
|
std::unique_ptr<Expr> attrs, body;
|
||||||
size_t prevWith;
|
size_t prevWith;
|
||||||
ExprWith * parentWith;
|
ExprWith * parentWith;
|
||||||
ExprWith(const PosIdx & pos, Expr * attrs, Expr * body) : pos(pos), attrs(attrs), body(body) { };
|
ExprWith(const PosIdx & pos, std::unique_ptr<Expr> attrs, std::unique_ptr<Expr> body) : pos(pos), attrs(std::move(attrs)), body(std::move(body)) { };
|
||||||
PosIdx getPos() const override { return pos; }
|
PosIdx getPos() const override { return pos; }
|
||||||
COMMON_METHODS
|
COMMON_METHODS
|
||||||
};
|
};
|
||||||
|
@ -334,8 +368,8 @@ struct ExprWith : Expr
|
||||||
struct ExprIf : Expr
|
struct ExprIf : Expr
|
||||||
{
|
{
|
||||||
PosIdx pos;
|
PosIdx pos;
|
||||||
Expr * cond, * then, * else_;
|
std::unique_ptr<Expr> cond, then, else_;
|
||||||
ExprIf(const PosIdx & pos, Expr * cond, Expr * then, Expr * else_) : pos(pos), cond(cond), then(then), else_(else_) { };
|
ExprIf(const PosIdx & pos, std::unique_ptr<Expr> cond, std::unique_ptr<Expr> then, std::unique_ptr<Expr> else_) : pos(pos), cond(std::move(cond)), then(std::move(then)), else_(std::move(else_)) { };
|
||||||
PosIdx getPos() const override { return pos; }
|
PosIdx getPos() const override { return pos; }
|
||||||
COMMON_METHODS
|
COMMON_METHODS
|
||||||
};
|
};
|
||||||
|
@ -343,16 +377,16 @@ struct ExprIf : Expr
|
||||||
struct ExprAssert : Expr
|
struct ExprAssert : Expr
|
||||||
{
|
{
|
||||||
PosIdx pos;
|
PosIdx pos;
|
||||||
Expr * cond, * body;
|
std::unique_ptr<Expr> cond, body;
|
||||||
ExprAssert(const PosIdx & pos, Expr * cond, Expr * body) : pos(pos), cond(cond), body(body) { };
|
ExprAssert(const PosIdx & pos, std::unique_ptr<Expr> cond, std::unique_ptr<Expr> body) : pos(pos), cond(std::move(cond)), body(std::move(body)) { };
|
||||||
PosIdx getPos() const override { return pos; }
|
PosIdx getPos() const override { return pos; }
|
||||||
COMMON_METHODS
|
COMMON_METHODS
|
||||||
};
|
};
|
||||||
|
|
||||||
struct ExprOpNot : Expr
|
struct ExprOpNot : Expr
|
||||||
{
|
{
|
||||||
Expr * e;
|
std::unique_ptr<Expr> e;
|
||||||
ExprOpNot(Expr * e) : e(e) { };
|
ExprOpNot(std::unique_ptr<Expr> e) : e(std::move(e)) { };
|
||||||
PosIdx getPos() const override { return e->getPos(); }
|
PosIdx getPos() const override { return e->getPos(); }
|
||||||
COMMON_METHODS
|
COMMON_METHODS
|
||||||
};
|
};
|
||||||
|
@ -361,9 +395,9 @@ struct ExprOpNot : Expr
|
||||||
struct name : Expr \
|
struct name : Expr \
|
||||||
{ \
|
{ \
|
||||||
PosIdx pos; \
|
PosIdx pos; \
|
||||||
Expr * e1, * e2; \
|
std::unique_ptr<Expr> e1, e2; \
|
||||||
name(Expr * e1, Expr * e2) : e1(e1), e2(e2) { }; \
|
name(std::unique_ptr<Expr> e1, std::unique_ptr<Expr> e2) : e1(std::move(e1)), e2(std::move(e2)) { }; \
|
||||||
name(const PosIdx & pos, Expr * e1, Expr * e2) : pos(pos), e1(e1), e2(e2) { }; \
|
name(const PosIdx & pos, std::unique_ptr<Expr> e1, std::unique_ptr<Expr> e2) : pos(pos), e1(std::move(e1)), e2(std::move(e2)) { }; \
|
||||||
void show(const SymbolTable & symbols, std::ostream & str) const override \
|
void show(const SymbolTable & symbols, std::ostream & str) const override \
|
||||||
{ \
|
{ \
|
||||||
str << "("; e1->show(symbols, str); str << " " s " "; e2->show(symbols, str); str << ")"; \
|
str << "("; e1->show(symbols, str); str << " " s " "; e2->show(symbols, str); str << ")"; \
|
||||||
|
@ -388,9 +422,9 @@ struct ExprConcatStrings : Expr
|
||||||
{
|
{
|
||||||
PosIdx pos;
|
PosIdx pos;
|
||||||
bool forceString;
|
bool forceString;
|
||||||
std::vector<std::pair<PosIdx, Expr *>> * es;
|
std::vector<std::pair<PosIdx, std::unique_ptr<Expr>>> es;
|
||||||
ExprConcatStrings(const PosIdx & pos, bool forceString, std::vector<std::pair<PosIdx, Expr *>> * es)
|
ExprConcatStrings(const PosIdx & pos, bool forceString, std::vector<std::pair<PosIdx, std::unique_ptr<Expr>>> es)
|
||||||
: pos(pos), forceString(forceString), es(es) { };
|
: pos(pos), forceString(forceString), es(std::move(es)) { };
|
||||||
PosIdx getPos() const override { return pos; }
|
PosIdx getPos() const override { return pos; }
|
||||||
COMMON_METHODS
|
COMMON_METHODS
|
||||||
};
|
};
|
||||||
|
|
|
@ -45,34 +45,35 @@ struct ParserState
|
||||||
SourcePath basePath;
|
SourcePath basePath;
|
||||||
PosTable::Origin origin;
|
PosTable::Origin origin;
|
||||||
const Expr::AstSymbols & s;
|
const Expr::AstSymbols & s;
|
||||||
|
std::unique_ptr<Error> error;
|
||||||
|
|
||||||
void dupAttr(const AttrPath & attrPath, const PosIdx pos, const PosIdx prevPos);
|
[[nodiscard]] ParseError dupAttr(const AttrPath & attrPath, const PosIdx pos, const PosIdx prevPos);
|
||||||
void dupAttr(Symbol attr, const PosIdx pos, const PosIdx prevPos);
|
[[nodiscard]] ParseError dupAttr(Symbol attr, const PosIdx pos, const PosIdx prevPos);
|
||||||
void addAttr(ExprAttrs * attrs, AttrPath && attrPath, Expr * e, const PosIdx pos);
|
[[nodiscard]] std::optional<ParseError> addAttr(ExprAttrs * attrs, AttrPath && attrPath, std::unique_ptr<Expr> e, const PosIdx pos);
|
||||||
Formals * validateFormals(Formals * formals, PosIdx pos = noPos, Symbol arg = {});
|
[[nodiscard]] std::optional<ParseError> validateFormals(Formals * formals, PosIdx pos = noPos, Symbol arg = {});
|
||||||
Expr * stripIndentation(const PosIdx pos,
|
std::unique_ptr<Expr> stripIndentation(const PosIdx pos,
|
||||||
std::vector<std::pair<PosIdx, std::variant<Expr *, StringToken>>> && es);
|
std::vector<std::pair<PosIdx, std::variant<std::unique_ptr<Expr>, StringToken>>> && es);
|
||||||
PosIdx at(const ParserLocation & loc);
|
PosIdx at(const ParserLocation & loc);
|
||||||
};
|
};
|
||||||
|
|
||||||
inline void ParserState::dupAttr(const AttrPath & attrPath, const PosIdx pos, const PosIdx prevPos)
|
inline ParseError ParserState::dupAttr(const AttrPath & attrPath, const PosIdx pos, const PosIdx prevPos)
|
||||||
{
|
{
|
||||||
throw ParseError({
|
return ParseError({
|
||||||
.msg = HintFmt("attribute '%1%' already defined at %2%",
|
.msg = HintFmt("attribute '%1%' already defined at %2%",
|
||||||
showAttrPath(symbols, attrPath), positions[prevPos]),
|
showAttrPath(symbols, attrPath), positions[prevPos]),
|
||||||
.pos = positions[pos]
|
.pos = positions[pos]
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
inline void ParserState::dupAttr(Symbol attr, const PosIdx pos, const PosIdx prevPos)
|
inline ParseError ParserState::dupAttr(Symbol attr, const PosIdx pos, const PosIdx prevPos)
|
||||||
{
|
{
|
||||||
throw ParseError({
|
return ParseError({
|
||||||
.msg = HintFmt("attribute '%1%' already defined at %2%", symbols[attr], positions[prevPos]),
|
.msg = HintFmt("attribute '%1%' already defined at %2%", symbols[attr], positions[prevPos]),
|
||||||
.pos = positions[pos]
|
.pos = positions[pos]
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
inline void ParserState::addAttr(ExprAttrs * attrs, AttrPath && attrPath, Expr * e, const PosIdx pos)
|
inline std::optional<ParseError> ParserState::addAttr(ExprAttrs * attrs, AttrPath && attrPath, std::unique_ptr<Expr> e, const PosIdx pos)
|
||||||
{
|
{
|
||||||
AttrPath::iterator i;
|
AttrPath::iterator i;
|
||||||
// All attrpaths have at least one attr
|
// All attrpaths have at least one attr
|
||||||
|
@ -84,20 +85,25 @@ inline void ParserState::addAttr(ExprAttrs * attrs, AttrPath && attrPath, Expr *
|
||||||
ExprAttrs::AttrDefs::iterator j = attrs->attrs.find(i->symbol);
|
ExprAttrs::AttrDefs::iterator j = attrs->attrs.find(i->symbol);
|
||||||
if (j != attrs->attrs.end()) {
|
if (j != attrs->attrs.end()) {
|
||||||
if (j->second.kind != ExprAttrs::AttrDef::Kind::Inherited) {
|
if (j->second.kind != ExprAttrs::AttrDef::Kind::Inherited) {
|
||||||
ExprAttrs * attrs2 = dynamic_cast<ExprAttrs *>(j->second.e);
|
ExprAttrs * attrs2 = dynamic_cast<ExprAttrs *>(j->second.e.get());
|
||||||
if (!attrs2) dupAttr({attrPath.begin(), i + 1}, pos, j->second.pos);
|
if (!attrs2) {
|
||||||
|
attrPath.erase(i + 1, attrPath.end());
|
||||||
|
return dupAttr(attrPath, pos, j->second.pos);
|
||||||
|
}
|
||||||
attrs = attrs2;
|
attrs = attrs2;
|
||||||
} else
|
} else {
|
||||||
dupAttr({attrPath.begin(), i + 1}, pos, j->second.pos);
|
attrPath.erase(i + 1, attrPath.end());
|
||||||
|
return dupAttr(attrPath, pos, j->second.pos);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
ExprAttrs * nested = new ExprAttrs;
|
auto next = attrs->attrs.emplace(std::piecewise_construct,
|
||||||
attrs->attrs[i->symbol] = ExprAttrs::AttrDef(nested, pos);
|
std::tuple(i->symbol),
|
||||||
attrs = nested;
|
std::tuple(std::make_unique<ExprAttrs>(), pos));
|
||||||
|
attrs = static_cast<ExprAttrs *>(next.first->second.e.get());
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
ExprAttrs *nested = new ExprAttrs;
|
auto & next = attrs->dynamicAttrs.emplace_back(std::move(i->expr), std::make_unique<ExprAttrs>(), pos);
|
||||||
attrs->dynamicAttrs.push_back(ExprAttrs::DynamicAttrDef(i->expr, nested, pos));
|
attrs = static_cast<ExprAttrs *>(next.valueExpr.get());
|
||||||
attrs = nested;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Expr insertion.
|
// Expr insertion.
|
||||||
|
@ -109,41 +115,43 @@ inline void ParserState::addAttr(ExprAttrs * attrs, AttrPath && attrPath, Expr *
|
||||||
// e and the expr pointed by the attr path are two attribute sets,
|
// e and the expr pointed by the attr path are two attribute sets,
|
||||||
// we want to merge them.
|
// we want to merge them.
|
||||||
// Otherwise, throw an error.
|
// Otherwise, throw an error.
|
||||||
auto ae = dynamic_cast<ExprAttrs *>(e);
|
auto * ae = dynamic_cast<ExprAttrs *>(e.get());
|
||||||
auto jAttrs = dynamic_cast<ExprAttrs *>(j->second.e);
|
auto * jAttrs = dynamic_cast<ExprAttrs *>(j->second.e.get());
|
||||||
if (jAttrs && ae) {
|
if (jAttrs && ae) {
|
||||||
if (ae->inheritFromExprs && !jAttrs->inheritFromExprs)
|
if (ae->inheritFromExprs && !jAttrs->inheritFromExprs)
|
||||||
jAttrs->inheritFromExprs = std::make_unique<std::vector<Expr *>>();
|
jAttrs->inheritFromExprs = std::make_unique<std::vector<std::unique_ptr<Expr>>>();
|
||||||
for (auto & ad : ae->attrs) {
|
for (auto & ad : ae->attrs) {
|
||||||
auto j2 = jAttrs->attrs.find(ad.first);
|
auto j2 = jAttrs->attrs.find(ad.first);
|
||||||
if (j2 != jAttrs->attrs.end()) // Attr already defined in iAttrs, error.
|
if (j2 != jAttrs->attrs.end()) // Attr already defined in iAttrs, error.
|
||||||
dupAttr(ad.first, j2->second.pos, ad.second.pos);
|
return dupAttr(ad.first, j2->second.pos, ad.second.pos);
|
||||||
jAttrs->attrs.emplace(ad.first, ad.second);
|
|
||||||
if (ad.second.kind == ExprAttrs::AttrDef::Kind::InheritedFrom) {
|
if (ad.second.kind == ExprAttrs::AttrDef::Kind::InheritedFrom) {
|
||||||
auto & sel = dynamic_cast<ExprSelect &>(*ad.second.e);
|
auto & sel = dynamic_cast<ExprSelect &>(*ad.second.e);
|
||||||
auto & from = dynamic_cast<ExprInheritFrom &>(*sel.e);
|
auto & from = dynamic_cast<ExprInheritFrom &>(*sel.e);
|
||||||
from.displ += jAttrs->inheritFromExprs->size();
|
from.displ += jAttrs->inheritFromExprs->size();
|
||||||
}
|
}
|
||||||
|
jAttrs->attrs.emplace(ad.first, std::move(ad.second));
|
||||||
}
|
}
|
||||||
jAttrs->dynamicAttrs.insert(jAttrs->dynamicAttrs.end(), ae->dynamicAttrs.begin(), ae->dynamicAttrs.end());
|
std::ranges::move(ae->dynamicAttrs, std::back_inserter(jAttrs->dynamicAttrs));
|
||||||
if (ae->inheritFromExprs) {
|
if (ae->inheritFromExprs)
|
||||||
jAttrs->inheritFromExprs->insert(jAttrs->inheritFromExprs->end(),
|
std::ranges::move(*ae->inheritFromExprs, std::back_inserter(*jAttrs->inheritFromExprs));
|
||||||
ae->inheritFromExprs->begin(), ae->inheritFromExprs->end());
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
dupAttr(attrPath, pos, j->second.pos);
|
return dupAttr(attrPath, pos, j->second.pos);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// This attr path is not defined. Let's create it.
|
// This attr path is not defined. Let's create it.
|
||||||
attrs->attrs.emplace(i->symbol, ExprAttrs::AttrDef(e, pos));
|
|
||||||
e->setName(i->symbol);
|
e->setName(i->symbol);
|
||||||
|
attrs->attrs.emplace(std::piecewise_construct,
|
||||||
|
std::tuple(i->symbol),
|
||||||
|
std::tuple(std::move(e), pos));
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
attrs->dynamicAttrs.push_back(ExprAttrs::DynamicAttrDef(i->expr, e, pos));
|
attrs->dynamicAttrs.emplace_back(std::move(i->expr), std::move(e), pos);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
inline Formals * ParserState::validateFormals(Formals * formals, PosIdx pos, Symbol arg)
|
inline std::optional<ParseError> ParserState::validateFormals(Formals * formals, PosIdx pos, Symbol arg)
|
||||||
{
|
{
|
||||||
std::sort(formals->formals.begin(), formals->formals.end(),
|
std::sort(formals->formals.begin(), formals->formals.end(),
|
||||||
[] (const auto & a, const auto & b) {
|
[] (const auto & a, const auto & b) {
|
||||||
|
@ -158,24 +166,24 @@ inline Formals * ParserState::validateFormals(Formals * formals, PosIdx pos, Sym
|
||||||
duplicate = std::min(thisDup, duplicate.value_or(thisDup));
|
duplicate = std::min(thisDup, duplicate.value_or(thisDup));
|
||||||
}
|
}
|
||||||
if (duplicate)
|
if (duplicate)
|
||||||
throw ParseError({
|
return ParseError({
|
||||||
.msg = HintFmt("duplicate formal function argument '%1%'", symbols[duplicate->first]),
|
.msg = HintFmt("duplicate formal function argument '%1%'", symbols[duplicate->first]),
|
||||||
.pos = positions[duplicate->second]
|
.pos = positions[duplicate->second]
|
||||||
});
|
});
|
||||||
|
|
||||||
if (arg && formals->has(arg))
|
if (arg && formals->has(arg))
|
||||||
throw ParseError({
|
return ParseError({
|
||||||
.msg = HintFmt("duplicate formal function argument '%1%'", symbols[arg]),
|
.msg = HintFmt("duplicate formal function argument '%1%'", symbols[arg]),
|
||||||
.pos = positions[pos]
|
.pos = positions[pos]
|
||||||
});
|
});
|
||||||
|
|
||||||
return formals;
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
inline Expr * ParserState::stripIndentation(const PosIdx pos,
|
inline std::unique_ptr<Expr> ParserState::stripIndentation(const PosIdx pos,
|
||||||
std::vector<std::pair<PosIdx, std::variant<Expr *, StringToken>>> && es)
|
std::vector<std::pair<PosIdx, std::variant<std::unique_ptr<Expr>, StringToken>>> && es)
|
||||||
{
|
{
|
||||||
if (es.empty()) return new ExprString("");
|
if (es.empty()) return std::make_unique<ExprString>("");
|
||||||
|
|
||||||
/* Figure out the minimum indentation. Note that by design
|
/* Figure out the minimum indentation. Note that by design
|
||||||
whitespace-only final lines are not taken into account. (So
|
whitespace-only final lines are not taken into account. (So
|
||||||
|
@ -213,17 +221,17 @@ inline Expr * ParserState::stripIndentation(const PosIdx pos,
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Strip spaces from each line. */
|
/* Strip spaces from each line. */
|
||||||
auto * es2 = new std::vector<std::pair<PosIdx, Expr *>>;
|
std::vector<std::pair<PosIdx, std::unique_ptr<Expr>>> es2;
|
||||||
atStartOfLine = true;
|
atStartOfLine = true;
|
||||||
size_t curDropped = 0;
|
size_t curDropped = 0;
|
||||||
size_t n = es.size();
|
size_t n = es.size();
|
||||||
auto i = es.begin();
|
auto i = es.begin();
|
||||||
const auto trimExpr = [&] (Expr * e) {
|
const auto trimExpr = [&] (std::unique_ptr<Expr> e) {
|
||||||
atStartOfLine = false;
|
atStartOfLine = false;
|
||||||
curDropped = 0;
|
curDropped = 0;
|
||||||
es2->emplace_back(i->first, e);
|
es2.emplace_back(i->first, std::move(e));
|
||||||
};
|
};
|
||||||
const auto trimString = [&] (const StringToken & t) {
|
const auto trimString = [&] (const StringToken t) {
|
||||||
std::string s2;
|
std::string s2;
|
||||||
for (size_t j = 0; j < t.l; ++j) {
|
for (size_t j = 0; j < t.l; ++j) {
|
||||||
if (atStartOfLine) {
|
if (atStartOfLine) {
|
||||||
|
@ -253,19 +261,17 @@ inline Expr * ParserState::stripIndentation(const PosIdx pos,
|
||||||
s2 = std::string(s2, 0, p + 1);
|
s2 = std::string(s2, 0, p + 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
es2->emplace_back(i->first, new ExprString(std::move(s2)));
|
es2.emplace_back(i->first, std::make_unique<ExprString>(std::move(s2)));
|
||||||
};
|
};
|
||||||
for (; i != es.end(); ++i, --n) {
|
for (; i != es.end(); ++i, --n) {
|
||||||
std::visit(overloaded { trimExpr, trimString }, i->second);
|
std::visit(overloaded { trimExpr, trimString }, std::move(i->second));
|
||||||
}
|
}
|
||||||
|
|
||||||
/* If this is a single string, then don't do a concatenation. */
|
/* If this is a single string, then don't do a concatenation. */
|
||||||
if (es2->size() == 1 && dynamic_cast<ExprString *>((*es2)[0].second)) {
|
if (es2.size() == 1 && dynamic_cast<ExprString *>(es2[0].second.get())) {
|
||||||
auto *const result = (*es2)[0].second;
|
return std::move(es2[0].second);
|
||||||
delete es2;
|
|
||||||
return result;
|
|
||||||
}
|
}
|
||||||
return new ExprConcatStrings(pos, true, es2);
|
return std::make_unique<ExprConcatStrings>(pos, true, std::move(es2));
|
||||||
}
|
}
|
||||||
|
|
||||||
inline PosIdx ParserState::at(const ParserLocation & loc)
|
inline PosIdx ParserState::at(const ParserLocation & loc)
|
||||||
|
|
|
@ -59,6 +59,15 @@ using namespace nix;
|
||||||
|
|
||||||
#define CUR_POS state->at(*yylocp)
|
#define CUR_POS state->at(*yylocp)
|
||||||
|
|
||||||
|
// otherwise destructors cause compiler errors
|
||||||
|
#pragma GCC diagnostic ignored "-Wswitch-enum"
|
||||||
|
|
||||||
|
#define THROW(err, ...) \
|
||||||
|
do { \
|
||||||
|
state->error.reset(new auto(err)); \
|
||||||
|
[](auto... d) { (delete d, ...); }(__VA_ARGS__); \
|
||||||
|
YYABORT; \
|
||||||
|
} while (0)
|
||||||
|
|
||||||
void yyerror(YYLTYPE * loc, yyscan_t scanner, ParserState * state, const char * error)
|
void yyerror(YYLTYPE * loc, yyscan_t scanner, ParserState * state, const char * error)
|
||||||
{
|
{
|
||||||
|
@ -72,6 +81,21 @@ void yyerror(YYLTYPE * loc, yyscan_t scanner, ParserState * state, const char *
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
static std::unique_ptr<T> unp(T * e)
|
||||||
|
{
|
||||||
|
return std::unique_ptr<T>(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T = std::unique_ptr<nix::Expr>, typename... Args>
|
||||||
|
static std::vector<T> vec(Args && ... args)
|
||||||
|
{
|
||||||
|
std::vector<T> result;
|
||||||
|
result.reserve(sizeof...(Args));
|
||||||
|
(result.emplace_back(std::forward<Args>(args)), ...);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
%}
|
%}
|
||||||
|
|
||||||
|
@ -90,11 +114,22 @@ void yyerror(YYLTYPE * loc, yyscan_t scanner, ParserState * state, const char *
|
||||||
nix::StringToken str;
|
nix::StringToken str;
|
||||||
std::vector<nix::AttrName> * attrNames;
|
std::vector<nix::AttrName> * attrNames;
|
||||||
std::vector<std::pair<nix::AttrName, nix::PosIdx>> * inheritAttrs;
|
std::vector<std::pair<nix::AttrName, nix::PosIdx>> * inheritAttrs;
|
||||||
std::vector<std::pair<nix::PosIdx, nix::Expr *>> * string_parts;
|
std::vector<std::pair<nix::PosIdx, std::unique_ptr<nix::Expr>>> * string_parts;
|
||||||
std::vector<std::pair<nix::PosIdx, std::variant<nix::Expr *, nix::StringToken>>> * ind_string_parts;
|
std::vector<std::pair<nix::PosIdx, std::variant<std::unique_ptr<nix::Expr>, nix::StringToken>>> * ind_string_parts;
|
||||||
}
|
}
|
||||||
|
|
||||||
%type <e> start expr expr_function expr_if expr_op
|
%destructor { delete $$; } <e>
|
||||||
|
%destructor { delete $$; } <list>
|
||||||
|
%destructor { delete $$; } <attrs>
|
||||||
|
%destructor { delete $$; } <formals>
|
||||||
|
%destructor { delete $$; } <formal>
|
||||||
|
%destructor { delete $$; } <attrNames>
|
||||||
|
%destructor { delete $$; } <inheritAttrs>
|
||||||
|
%destructor { delete $$; } <string_parts>
|
||||||
|
%destructor { delete $$; } <ind_string_parts>
|
||||||
|
|
||||||
|
%type <e> start
|
||||||
|
%type <e> expr expr_function expr_if expr_op
|
||||||
%type <e> expr_select expr_simple expr_app
|
%type <e> expr_select expr_simple expr_app
|
||||||
%type <list> expr_list
|
%type <list> expr_list
|
||||||
%type <attrs> binds
|
%type <attrs> binds
|
||||||
|
@ -132,88 +167,92 @@ void yyerror(YYLTYPE * loc, yyscan_t scanner, ParserState * state, const char *
|
||||||
|
|
||||||
%%
|
%%
|
||||||
|
|
||||||
start: expr { state->result = $1; };
|
start: expr { state->result = $1; $$ = 0; };
|
||||||
|
|
||||||
expr: expr_function;
|
expr: expr_function;
|
||||||
|
|
||||||
expr_function
|
expr_function
|
||||||
: ID ':' expr_function
|
: ID ':' expr_function
|
||||||
{ $$ = new ExprLambda(CUR_POS, state->symbols.create($1), 0, $3); }
|
{ $$ = new ExprLambda(CUR_POS, state->symbols.create($1), nullptr, unp($3)); }
|
||||||
| '{' formals '}' ':' expr_function
|
| '{' formals '}' ':' expr_function
|
||||||
{ $$ = new ExprLambda(CUR_POS, state->validateFormals($2), $5); }
|
{ if (auto e = state->validateFormals($2)) THROW(*e);
|
||||||
|
$$ = new ExprLambda(CUR_POS, unp($2), unp($5));
|
||||||
|
}
|
||||||
| '{' formals '}' '@' ID ':' expr_function
|
| '{' formals '}' '@' ID ':' expr_function
|
||||||
{
|
{
|
||||||
auto arg = state->symbols.create($5);
|
auto arg = state->symbols.create($5);
|
||||||
$$ = new ExprLambda(CUR_POS, arg, state->validateFormals($2, CUR_POS, arg), $7);
|
if (auto e = state->validateFormals($2, CUR_POS, arg)) THROW(*e, $2, $7);
|
||||||
|
$$ = new ExprLambda(CUR_POS, arg, unp($2), unp($7));
|
||||||
}
|
}
|
||||||
| ID '@' '{' formals '}' ':' expr_function
|
| ID '@' '{' formals '}' ':' expr_function
|
||||||
{
|
{
|
||||||
auto arg = state->symbols.create($1);
|
auto arg = state->symbols.create($1);
|
||||||
$$ = new ExprLambda(CUR_POS, arg, state->validateFormals($4, CUR_POS, arg), $7);
|
if (auto e = state->validateFormals($4, CUR_POS, arg)) THROW(*e, $4, $7);
|
||||||
|
$$ = new ExprLambda(CUR_POS, arg, unp($4), unp($7));
|
||||||
}
|
}
|
||||||
| ASSERT expr ';' expr_function
|
| ASSERT expr ';' expr_function
|
||||||
{ $$ = new ExprAssert(CUR_POS, $2, $4); }
|
{ $$ = new ExprAssert(CUR_POS, unp($2), unp($4)); }
|
||||||
| WITH expr ';' expr_function
|
| WITH expr ';' expr_function
|
||||||
{ $$ = new ExprWith(CUR_POS, $2, $4); }
|
{ $$ = new ExprWith(CUR_POS, unp($2), unp($4)); }
|
||||||
| LET binds IN expr_function
|
| LET binds IN expr_function
|
||||||
{ if (!$2->dynamicAttrs.empty())
|
{ if (!$2->dynamicAttrs.empty())
|
||||||
throw ParseError({
|
THROW(ParseError({
|
||||||
.msg = HintFmt("dynamic attributes not allowed in let"),
|
.msg = HintFmt("dynamic attributes not allowed in let"),
|
||||||
.pos = state->positions[CUR_POS]
|
.pos = state->positions[CUR_POS]
|
||||||
});
|
}), $2, $4);
|
||||||
$$ = new ExprLet($2, $4);
|
$$ = new ExprLet(unp($2), unp($4));
|
||||||
}
|
}
|
||||||
| expr_if
|
| expr_if
|
||||||
;
|
;
|
||||||
|
|
||||||
expr_if
|
expr_if
|
||||||
: IF expr THEN expr ELSE expr { $$ = new ExprIf(CUR_POS, $2, $4, $6); }
|
: IF expr THEN expr ELSE expr { $$ = new ExprIf(CUR_POS, unp($2), unp($4), unp($6)); }
|
||||||
| expr_op
|
| expr_op
|
||||||
;
|
;
|
||||||
|
|
||||||
expr_op
|
expr_op
|
||||||
: '!' expr_op %prec NOT { $$ = new ExprOpNot($2); }
|
: '!' expr_op %prec NOT { $$ = new ExprOpNot(unp($2)); }
|
||||||
| '-' expr_op %prec NEGATE { $$ = new ExprCall(CUR_POS, new ExprVar(state->s.sub), {new ExprInt(0), $2}); }
|
| '-' expr_op %prec NEGATE { $$ = new ExprCall(CUR_POS, std::make_unique<ExprVar>(state->s.sub), vec(std::make_unique<ExprInt>(0), unp($2))); }
|
||||||
| expr_op EQ expr_op { $$ = new ExprOpEq($1, $3); }
|
| expr_op EQ expr_op { $$ = new ExprOpEq(unp($1), unp($3)); }
|
||||||
| expr_op NEQ expr_op { $$ = new ExprOpNEq($1, $3); }
|
| expr_op NEQ expr_op { $$ = new ExprOpNEq(unp($1), unp($3)); }
|
||||||
| expr_op '<' expr_op { $$ = new ExprCall(state->at(@2), new ExprVar(state->s.lessThan), {$1, $3}); }
|
| expr_op '<' expr_op { $$ = new ExprCall(state->at(@2), std::make_unique<ExprVar>(state->s.lessThan), vec($1, $3)); }
|
||||||
| expr_op LEQ expr_op { $$ = new ExprOpNot(new ExprCall(state->at(@2), new ExprVar(state->s.lessThan), {$3, $1})); }
|
| expr_op LEQ expr_op { $$ = new ExprOpNot(std::make_unique<ExprCall>(state->at(@2), std::make_unique<ExprVar>(state->s.lessThan), vec($3, $1))); }
|
||||||
| expr_op '>' expr_op { $$ = new ExprCall(state->at(@2), new ExprVar(state->s.lessThan), {$3, $1}); }
|
| expr_op '>' expr_op { $$ = new ExprCall(state->at(@2), std::make_unique<ExprVar>(state->s.lessThan), vec($3, $1)); }
|
||||||
| expr_op GEQ expr_op { $$ = new ExprOpNot(new ExprCall(state->at(@2), new ExprVar(state->s.lessThan), {$1, $3})); }
|
| expr_op GEQ expr_op { $$ = new ExprOpNot(std::make_unique<ExprCall>(state->at(@2), std::make_unique<ExprVar>(state->s.lessThan), vec($1, $3))); }
|
||||||
| expr_op AND expr_op { $$ = new ExprOpAnd(state->at(@2), $1, $3); }
|
| expr_op AND expr_op { $$ = new ExprOpAnd(state->at(@2), unp($1), unp($3)); }
|
||||||
| expr_op OR expr_op { $$ = new ExprOpOr(state->at(@2), $1, $3); }
|
| expr_op OR expr_op { $$ = new ExprOpOr(state->at(@2), unp($1), unp($3)); }
|
||||||
| expr_op IMPL expr_op { $$ = new ExprOpImpl(state->at(@2), $1, $3); }
|
| expr_op IMPL expr_op { $$ = new ExprOpImpl(state->at(@2), unp($1), unp($3)); }
|
||||||
| expr_op UPDATE expr_op { $$ = new ExprOpUpdate(state->at(@2), $1, $3); }
|
| expr_op UPDATE expr_op { $$ = new ExprOpUpdate(state->at(@2), unp($1), unp($3)); }
|
||||||
| expr_op '?' attrpath { $$ = new ExprOpHasAttr($1, std::move(*$3)); delete $3; }
|
| expr_op '?' attrpath { $$ = new ExprOpHasAttr(unp($1), std::move(*$3)); delete $3; }
|
||||||
| expr_op '+' expr_op
|
| expr_op '+' expr_op
|
||||||
{ $$ = new ExprConcatStrings(state->at(@2), false, new std::vector<std::pair<PosIdx, Expr *> >({{state->at(@1), $1}, {state->at(@3), $3}})); }
|
{ $$ = new ExprConcatStrings(state->at(@2), false, vec<std::pair<PosIdx, std::unique_ptr<Expr>>>(std::pair(state->at(@1), unp($1)), std::pair(state->at(@3), unp($3)))); }
|
||||||
| expr_op '-' expr_op { $$ = new ExprCall(state->at(@2), new ExprVar(state->s.sub), {$1, $3}); }
|
| expr_op '-' expr_op { $$ = new ExprCall(state->at(@2), std::make_unique<ExprVar>(state->s.sub), vec($1, $3)); }
|
||||||
| expr_op '*' expr_op { $$ = new ExprCall(state->at(@2), new ExprVar(state->s.mul), {$1, $3}); }
|
| expr_op '*' expr_op { $$ = new ExprCall(state->at(@2), std::make_unique<ExprVar>(state->s.mul), vec($1, $3)); }
|
||||||
| expr_op '/' expr_op { $$ = new ExprCall(state->at(@2), new ExprVar(state->s.div), {$1, $3}); }
|
| expr_op '/' expr_op { $$ = new ExprCall(state->at(@2), std::make_unique<ExprVar>(state->s.div), vec($1, $3)); }
|
||||||
| expr_op CONCAT expr_op { $$ = new ExprOpConcatLists(state->at(@2), $1, $3); }
|
| expr_op CONCAT expr_op { $$ = new ExprOpConcatLists(state->at(@2), unp($1), unp($3)); }
|
||||||
| expr_app
|
| expr_app
|
||||||
;
|
;
|
||||||
|
|
||||||
expr_app
|
expr_app
|
||||||
: expr_app expr_select {
|
: expr_app expr_select {
|
||||||
if (auto e2 = dynamic_cast<ExprCall *>($1)) {
|
if (auto e2 = dynamic_cast<ExprCall *>($1)) {
|
||||||
e2->args.push_back($2);
|
e2->args.emplace_back($2);
|
||||||
$$ = $1;
|
$$ = $1;
|
||||||
} else
|
} else
|
||||||
$$ = new ExprCall(CUR_POS, $1, {$2});
|
$$ = new ExprCall(CUR_POS, unp($1), vec(unp($2)));
|
||||||
}
|
}
|
||||||
| expr_select
|
| expr_select
|
||||||
;
|
;
|
||||||
|
|
||||||
expr_select
|
expr_select
|
||||||
: expr_simple '.' attrpath
|
: expr_simple '.' attrpath
|
||||||
{ $$ = new ExprSelect(CUR_POS, $1, std::move(*$3), nullptr); delete $3; }
|
{ $$ = new ExprSelect(CUR_POS, unp($1), std::move(*$3), nullptr); delete $3; }
|
||||||
| expr_simple '.' attrpath OR_KW expr_select
|
| expr_simple '.' attrpath OR_KW expr_select
|
||||||
{ $$ = new ExprSelect(CUR_POS, $1, std::move(*$3), $5); delete $3; }
|
{ $$ = new ExprSelect(CUR_POS, unp($1), std::move(*$3), unp($5)); delete $3; }
|
||||||
| /* Backwards compatibility: because Nixpkgs has a rarely used
|
| /* Backwards compatibility: because Nixpkgs has a rarely used
|
||||||
function named ‘or’, allow stuff like ‘map or [...]’. */
|
function named ‘or’, allow stuff like ‘map or [...]’. */
|
||||||
expr_simple OR_KW
|
expr_simple OR_KW
|
||||||
{ $$ = new ExprCall(CUR_POS, $1, {new ExprVar(CUR_POS, state->s.or_)}); }
|
{ $$ = new ExprCall(CUR_POS, unp($1), vec(std::make_unique<ExprVar>(CUR_POS, state->s.or_))); }
|
||||||
| expr_simple
|
| expr_simple
|
||||||
;
|
;
|
||||||
|
|
||||||
|
@ -229,35 +268,36 @@ expr_simple
|
||||||
| FLOAT { $$ = new ExprFloat($1); }
|
| FLOAT { $$ = new ExprFloat($1); }
|
||||||
| '"' string_parts '"' { $$ = $2; }
|
| '"' string_parts '"' { $$ = $2; }
|
||||||
| IND_STRING_OPEN ind_string_parts IND_STRING_CLOSE {
|
| IND_STRING_OPEN ind_string_parts IND_STRING_CLOSE {
|
||||||
$$ = state->stripIndentation(CUR_POS, std::move(*$2));
|
$$ = state->stripIndentation(CUR_POS, std::move(*$2)).release();
|
||||||
delete $2;
|
delete $2;
|
||||||
}
|
}
|
||||||
| path_start PATH_END
|
| path_start PATH_END
|
||||||
| path_start string_parts_interpolated PATH_END {
|
| path_start string_parts_interpolated PATH_END {
|
||||||
$2->insert($2->begin(), {state->at(@1), $1});
|
$2->emplace($2->begin(), state->at(@1), $1);
|
||||||
$$ = new ExprConcatStrings(CUR_POS, false, $2);
|
$$ = new ExprConcatStrings(CUR_POS, false, std::move(*$2));
|
||||||
|
delete $2;
|
||||||
}
|
}
|
||||||
| SPATH {
|
| SPATH {
|
||||||
std::string path($1.p + 1, $1.l - 2);
|
std::string path($1.p + 1, $1.l - 2);
|
||||||
$$ = new ExprCall(CUR_POS,
|
$$ = new ExprCall(CUR_POS,
|
||||||
new ExprVar(state->s.findFile),
|
std::make_unique<ExprVar>(state->s.findFile),
|
||||||
{new ExprVar(state->s.nixPath),
|
vec(std::make_unique<ExprVar>(state->s.nixPath),
|
||||||
new ExprString(std::move(path))});
|
std::make_unique<ExprString>(std::move(path))));
|
||||||
}
|
}
|
||||||
| URI {
|
| URI {
|
||||||
static bool noURLLiterals = experimentalFeatureSettings.isEnabled(Xp::NoUrlLiterals);
|
static bool noURLLiterals = experimentalFeatureSettings.isEnabled(Xp::NoUrlLiterals);
|
||||||
if (noURLLiterals)
|
if (noURLLiterals)
|
||||||
throw ParseError({
|
THROW(ParseError({
|
||||||
.msg = HintFmt("URL literals are disabled"),
|
.msg = HintFmt("URL literals are disabled"),
|
||||||
.pos = state->positions[CUR_POS]
|
.pos = state->positions[CUR_POS]
|
||||||
});
|
}));
|
||||||
$$ = new ExprString(std::string($1));
|
$$ = new ExprString(std::string($1));
|
||||||
}
|
}
|
||||||
| '(' expr ')' { $$ = $2; }
|
| '(' expr ')' { $$ = $2; }
|
||||||
/* Let expressions `let {..., body = ...}' are just desugared
|
/* Let expressions `let {..., body = ...}' are just desugared
|
||||||
into `(rec {..., body = ...}).body'. */
|
into `(rec {..., body = ...}).body'. */
|
||||||
| LET '{' binds '}'
|
| LET '{' binds '}'
|
||||||
{ $3->recursive = true; $$ = new ExprSelect(noPos, $3, state->s.body); }
|
{ $3->recursive = true; $$ = new ExprSelect(noPos, unp($3), state->s.body); }
|
||||||
| REC '{' binds '}'
|
| REC '{' binds '}'
|
||||||
{ $3->recursive = true; $$ = $3; }
|
{ $3->recursive = true; $$ = $3; }
|
||||||
| '{' binds '}'
|
| '{' binds '}'
|
||||||
|
@ -267,7 +307,10 @@ expr_simple
|
||||||
|
|
||||||
string_parts
|
string_parts
|
||||||
: STR { $$ = new ExprString(std::string($1)); }
|
: STR { $$ = new ExprString(std::string($1)); }
|
||||||
| string_parts_interpolated { $$ = new ExprConcatStrings(CUR_POS, true, $1); }
|
| string_parts_interpolated
|
||||||
|
{ $$ = new ExprConcatStrings(CUR_POS, true, std::move(*$1));
|
||||||
|
delete $1;
|
||||||
|
}
|
||||||
| { $$ = new ExprString(""); }
|
| { $$ = new ExprString(""); }
|
||||||
;
|
;
|
||||||
|
|
||||||
|
@ -275,9 +318,9 @@ string_parts_interpolated
|
||||||
: string_parts_interpolated STR
|
: string_parts_interpolated STR
|
||||||
{ $$ = $1; $1->emplace_back(state->at(@2), new ExprString(std::string($2))); }
|
{ $$ = $1; $1->emplace_back(state->at(@2), new ExprString(std::string($2))); }
|
||||||
| string_parts_interpolated DOLLAR_CURLY expr '}' { $$ = $1; $1->emplace_back(state->at(@2), $3); }
|
| string_parts_interpolated DOLLAR_CURLY expr '}' { $$ = $1; $1->emplace_back(state->at(@2), $3); }
|
||||||
| DOLLAR_CURLY expr '}' { $$ = new std::vector<std::pair<PosIdx, Expr *>>; $$->emplace_back(state->at(@1), $2); }
|
| DOLLAR_CURLY expr '}' { $$ = new std::vector<std::pair<PosIdx, std::unique_ptr<Expr>>>; $$->emplace_back(state->at(@1), $2); }
|
||||||
| STR DOLLAR_CURLY expr '}' {
|
| STR DOLLAR_CURLY expr '}' {
|
||||||
$$ = new std::vector<std::pair<PosIdx, Expr *>>;
|
$$ = new std::vector<std::pair<PosIdx, std::unique_ptr<Expr>>>;
|
||||||
$$->emplace_back(state->at(@1), new ExprString(std::string($1)));
|
$$->emplace_back(state->at(@1), new ExprString(std::string($1)));
|
||||||
$$->emplace_back(state->at(@2), $3);
|
$$->emplace_back(state->at(@2), $3);
|
||||||
}
|
}
|
||||||
|
@ -293,10 +336,10 @@ path_start
|
||||||
}
|
}
|
||||||
| HPATH {
|
| HPATH {
|
||||||
if (evalSettings.pureEval) {
|
if (evalSettings.pureEval) {
|
||||||
throw Error(
|
THROW(Error(
|
||||||
"the path '%s' can not be resolved in pure mode",
|
"the path '%s' can not be resolved in pure mode",
|
||||||
std::string_view($1.p, $1.l)
|
std::string_view($1.p, $1.l)
|
||||||
);
|
));
|
||||||
}
|
}
|
||||||
Path path(getHome() + std::string($1.p + 1, $1.l - 1));
|
Path path(getHome() + std::string($1.p + 1, $1.l - 1));
|
||||||
$$ = new ExprPath(path);
|
$$ = new ExprPath(path);
|
||||||
|
@ -305,36 +348,40 @@ path_start
|
||||||
|
|
||||||
ind_string_parts
|
ind_string_parts
|
||||||
: ind_string_parts IND_STR { $$ = $1; $1->emplace_back(state->at(@2), $2); }
|
: ind_string_parts IND_STR { $$ = $1; $1->emplace_back(state->at(@2), $2); }
|
||||||
| ind_string_parts DOLLAR_CURLY expr '}' { $$ = $1; $1->emplace_back(state->at(@2), $3); }
|
| ind_string_parts DOLLAR_CURLY expr '}' { $$ = $1; $1->emplace_back(state->at(@2), unp($3)); }
|
||||||
| { $$ = new std::vector<std::pair<PosIdx, std::variant<Expr *, StringToken>>>; }
|
| { $$ = new std::vector<std::pair<PosIdx, std::variant<std::unique_ptr<Expr>, StringToken>>>; }
|
||||||
;
|
;
|
||||||
|
|
||||||
binds
|
binds
|
||||||
: binds attrpath '=' expr ';' { $$ = $1; state->addAttr($$, std::move(*$2), $4, state->at(@2)); delete $2; }
|
: binds attrpath '=' expr ';'
|
||||||
|
{ $$ = $1;
|
||||||
|
if (auto e = state->addAttr($$, std::move(*$2), unp($4), state->at(@2))) THROW(*e, $1, $2);
|
||||||
|
delete $2;
|
||||||
|
}
|
||||||
| binds INHERIT attrs ';'
|
| binds INHERIT attrs ';'
|
||||||
{ $$ = $1;
|
{ $$ = $1;
|
||||||
for (auto & [i, iPos] : *$3) {
|
for (auto & [i, iPos] : *$3) {
|
||||||
if ($$->attrs.find(i.symbol) != $$->attrs.end())
|
if ($$->attrs.find(i.symbol) != $$->attrs.end())
|
||||||
state->dupAttr(i.symbol, iPos, $$->attrs[i.symbol].pos);
|
THROW(state->dupAttr(i.symbol, iPos, $$->attrs[i.symbol].pos), $1);
|
||||||
$$->attrs.emplace(
|
$$->attrs.emplace(
|
||||||
i.symbol,
|
i.symbol,
|
||||||
ExprAttrs::AttrDef(new ExprVar(iPos, i.symbol), iPos, ExprAttrs::AttrDef::Kind::Inherited));
|
ExprAttrs::AttrDef(std::make_unique<ExprVar>(iPos, i.symbol), iPos, ExprAttrs::AttrDef::Kind::Inherited));
|
||||||
}
|
}
|
||||||
delete $3;
|
delete $3;
|
||||||
}
|
}
|
||||||
| binds INHERIT '(' expr ')' attrs ';'
|
| binds INHERIT '(' expr ')' attrs ';'
|
||||||
{ $$ = $1;
|
{ $$ = $1;
|
||||||
if (!$$->inheritFromExprs)
|
if (!$$->inheritFromExprs)
|
||||||
$$->inheritFromExprs = std::make_unique<std::vector<Expr *>>();
|
$$->inheritFromExprs = std::make_unique<std::vector<std::unique_ptr<Expr>>>();
|
||||||
$$->inheritFromExprs->push_back($4);
|
$$->inheritFromExprs->push_back(unp($4));
|
||||||
auto from = new nix::ExprInheritFrom(state->at(@4), $$->inheritFromExprs->size() - 1);
|
|
||||||
for (auto & [i, iPos] : *$6) {
|
for (auto & [i, iPos] : *$6) {
|
||||||
if ($$->attrs.find(i.symbol) != $$->attrs.end())
|
if ($$->attrs.find(i.symbol) != $$->attrs.end())
|
||||||
state->dupAttr(i.symbol, iPos, $$->attrs[i.symbol].pos);
|
THROW(state->dupAttr(i.symbol, iPos, $$->attrs[i.symbol].pos), $1);
|
||||||
|
auto from = std::make_unique<nix::ExprInheritFrom>(state->at(@4), $$->inheritFromExprs->size() - 1);
|
||||||
$$->attrs.emplace(
|
$$->attrs.emplace(
|
||||||
i.symbol,
|
i.symbol,
|
||||||
ExprAttrs::AttrDef(
|
ExprAttrs::AttrDef(
|
||||||
new ExprSelect(iPos, from, i.symbol),
|
std::make_unique<ExprSelect>(iPos, std::move(from), i.symbol),
|
||||||
iPos,
|
iPos,
|
||||||
ExprAttrs::AttrDef::Kind::InheritedFrom));
|
ExprAttrs::AttrDef::Kind::InheritedFrom));
|
||||||
}
|
}
|
||||||
|
@ -352,10 +399,10 @@ attrs
|
||||||
$$->emplace_back(AttrName(state->symbols.create(str->s)), state->at(@2));
|
$$->emplace_back(AttrName(state->symbols.create(str->s)), state->at(@2));
|
||||||
delete str;
|
delete str;
|
||||||
} else
|
} else
|
||||||
throw ParseError({
|
THROW(ParseError({
|
||||||
.msg = HintFmt("dynamic attributes not allowed in inherit"),
|
.msg = HintFmt("dynamic attributes not allowed in inherit"),
|
||||||
.pos = state->positions[state->at(@2)]
|
.pos = state->positions[state->at(@2)]
|
||||||
});
|
}), $1, $2);
|
||||||
}
|
}
|
||||||
| { $$ = new std::vector<std::pair<AttrName, PosIdx>>; }
|
| { $$ = new std::vector<std::pair<AttrName, PosIdx>>; }
|
||||||
;
|
;
|
||||||
|
@ -369,7 +416,7 @@ attrpath
|
||||||
$$->push_back(AttrName(state->symbols.create(str->s)));
|
$$->push_back(AttrName(state->symbols.create(str->s)));
|
||||||
delete str;
|
delete str;
|
||||||
} else
|
} else
|
||||||
$$->push_back(AttrName($3));
|
$$->emplace_back(unp($3));
|
||||||
}
|
}
|
||||||
| attr { $$ = new std::vector<AttrName>; $$->push_back(AttrName(state->symbols.create($1))); }
|
| attr { $$ = new std::vector<AttrName>; $$->push_back(AttrName(state->symbols.create($1))); }
|
||||||
| string_attr
|
| string_attr
|
||||||
|
@ -379,7 +426,7 @@ attrpath
|
||||||
$$->push_back(AttrName(state->symbols.create(str->s)));
|
$$->push_back(AttrName(state->symbols.create(str->s)));
|
||||||
delete str;
|
delete str;
|
||||||
} else
|
} else
|
||||||
$$->push_back(AttrName($1));
|
$$->emplace_back(unp($1));
|
||||||
}
|
}
|
||||||
;
|
;
|
||||||
|
|
||||||
|
@ -394,15 +441,15 @@ string_attr
|
||||||
;
|
;
|
||||||
|
|
||||||
expr_list
|
expr_list
|
||||||
: expr_list expr_select { $$ = $1; $1->elems.push_back($2); /* !!! dangerous */ }
|
: expr_list expr_select { $$ = $1; $1->elems.emplace_back($2); /* !!! dangerous */ }
|
||||||
| { $$ = new ExprList; }
|
| { $$ = new ExprList; }
|
||||||
;
|
;
|
||||||
|
|
||||||
formals
|
formals
|
||||||
: formal ',' formals
|
: formal ',' formals
|
||||||
{ $$ = $3; $$->formals.emplace_back(*$1); delete $1; }
|
{ $$ = $3; $$->formals.emplace_back(std::move(*$1)); delete $1; }
|
||||||
| formal
|
| formal
|
||||||
{ $$ = new Formals; $$->formals.emplace_back(*$1); $$->ellipsis = false; delete $1; }
|
{ $$ = new Formals; $$->formals.emplace_back(std::move(*$1)); $$->ellipsis = false; delete $1; }
|
||||||
|
|
|
|
||||||
{ $$ = new Formals; $$->ellipsis = false; }
|
{ $$ = new Formals; $$->ellipsis = false; }
|
||||||
| ELLIPSIS
|
| ELLIPSIS
|
||||||
|
@ -410,8 +457,8 @@ formals
|
||||||
;
|
;
|
||||||
|
|
||||||
formal
|
formal
|
||||||
: ID { $$ = new Formal{CUR_POS, state->symbols.create($1), 0}; }
|
: ID { $$ = new Formal{CUR_POS, state->symbols.create($1), nullptr}; }
|
||||||
| ID '?' expr { $$ = new Formal{CUR_POS, state->symbols.create($1), $3}; }
|
| ID '?' expr { $$ = new Formal{CUR_POS, state->symbols.create($1), unp($3)}; }
|
||||||
;
|
;
|
||||||
|
|
||||||
%%
|
%%
|
||||||
|
@ -444,6 +491,10 @@ Expr * parseExprFromBuf(
|
||||||
|
|
||||||
yy_scan_buffer(text, length, scanner);
|
yy_scan_buffer(text, length, scanner);
|
||||||
yyparse(scanner, &state);
|
yyparse(scanner, &state);
|
||||||
|
if (state.error) {
|
||||||
|
delete state.result;
|
||||||
|
throw *state.error;
|
||||||
|
}
|
||||||
|
|
||||||
return state.result;
|
return state.result;
|
||||||
}
|
}
|
||||||
|
|
|
@ -244,9 +244,9 @@ static void import(EvalState & state, const PosIdx pos, Value & vPath, Value * v
|
||||||
// args[0]->attrs is already sorted.
|
// args[0]->attrs is already sorted.
|
||||||
|
|
||||||
debug("evaluating file '%1%'", path);
|
debug("evaluating file '%1%'", path);
|
||||||
Expr * e = state.parseExprFromFile(resolveExprPath(path), staticEnv);
|
Expr & e = state.parseExprFromFile(resolveExprPath(path), staticEnv);
|
||||||
|
|
||||||
e->eval(state, *env, v);
|
e.eval(state, *env, v);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -383,19 +383,20 @@ void prim_exec(EvalState & state, const PosIdx pos, Value * * args, Value & v)
|
||||||
try {
|
try {
|
||||||
auto _ = state.realiseContext(context); // FIXME: Handle CA derivations
|
auto _ = state.realiseContext(context); // FIXME: Handle CA derivations
|
||||||
} catch (InvalidPathError & e) {
|
} catch (InvalidPathError & e) {
|
||||||
state.error<EvalError>("cannot execute '%1%', since path '%2%' is not valid", program, e.path).atPos(pos).debugThrow();
|
e.addTrace(state.positions[pos], "while realising the context for builtins.exec");
|
||||||
|
throw;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto output = runProgram(program, true, commandArgs);
|
auto output = runProgram(program, true, commandArgs);
|
||||||
Expr * parsed;
|
Expr * parsed;
|
||||||
try {
|
try {
|
||||||
parsed = state.parseExprFromString(std::move(output), state.rootPath(CanonPath::root));
|
parsed = &state.parseExprFromString(std::move(output), state.rootPath(CanonPath::root));
|
||||||
} catch (Error & e) {
|
} catch (Error & e) {
|
||||||
e.addTrace(state.positions[pos], "while parsing the output from '%1%'", program);
|
e.addTrace(state.positions[pos], "while parsing the output from '%1%'", program);
|
||||||
throw;
|
throw;
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
state.eval(parsed, v);
|
state.eval(*parsed, v);
|
||||||
} catch (Error & e) {
|
} catch (Error & e) {
|
||||||
e.addTrace(state.positions[pos], "while evaluating the output from '%1%'", program);
|
e.addTrace(state.positions[pos], "while evaluating the output from '%1%'", program);
|
||||||
throw;
|
throw;
|
||||||
|
@ -922,14 +923,15 @@ static RegisterPrimOp primop_getEnv({
|
||||||
.args = {"s"},
|
.args = {"s"},
|
||||||
.doc = R"(
|
.doc = R"(
|
||||||
`getEnv` returns the value of the environment variable *s*, or an
|
`getEnv` returns the value of the environment variable *s*, or an
|
||||||
empty string if the variable doesn’t exist. This function should be
|
empty string if the variable doesn't exist. This function should be
|
||||||
used with care, as it can introduce all sorts of nasty environment
|
used with care, as it can introduce all sorts of nasty environment
|
||||||
dependencies in your Nix expression.
|
dependencies in your Nix expression.
|
||||||
|
|
||||||
`getEnv` is used in Nix Packages to locate the file
|
`getEnv` is used in nixpkgs for evil impurities such as locating the file
|
||||||
`~/.nixpkgs/config.nix`, which contains user-local settings for Nix
|
`~/.config/nixpkgs/config.nix` which contains user-local settings for nixpkgs.
|
||||||
Packages. (That is, it does a `getEnv "HOME"` to locate the user’s
|
(That is, it does a `getEnv "HOME"` to locate the user's home directory.)
|
||||||
home directory.)
|
|
||||||
|
When in [pure evaluation mode](@docroot@/command-ref/conf-file.md#conf-pure-eval), this function always returns an empty string.
|
||||||
)",
|
)",
|
||||||
.fun = prim_getEnv,
|
.fun = prim_getEnv,
|
||||||
});
|
});
|
||||||
|
@ -1505,6 +1507,7 @@ static RegisterPrimOp primop_storePath({
|
||||||
in a new path (e.g. `/nix/store/ld01dnzc…-source-source`).
|
in a new path (e.g. `/nix/store/ld01dnzc…-source-source`).
|
||||||
|
|
||||||
Not available in [pure evaluation mode](@docroot@/command-ref/conf-file.md#conf-pure-eval).
|
Not available in [pure evaluation mode](@docroot@/command-ref/conf-file.md#conf-pure-eval).
|
||||||
|
Lix may change this, tracking issue: <https://git.lix.systems/lix-project/lix/issues/402>
|
||||||
|
|
||||||
See also [`builtins.fetchClosure`](#builtins-fetchClosure).
|
See also [`builtins.fetchClosure`](#builtins-fetchClosure).
|
||||||
)",
|
)",
|
||||||
|
@ -2816,7 +2819,7 @@ static void prim_functionArgs(EvalState & state, const PosIdx pos, Value * * arg
|
||||||
auto attrs = state.buildBindings(args[0]->lambda.fun->formals->formals.size());
|
auto attrs = state.buildBindings(args[0]->lambda.fun->formals->formals.size());
|
||||||
for (auto & i : args[0]->lambda.fun->formals->formals)
|
for (auto & i : args[0]->lambda.fun->formals->formals)
|
||||||
// !!! should optimise booleans (allocate only once)
|
// !!! should optimise booleans (allocate only once)
|
||||||
attrs.alloc(i.name, i.pos).mkBool(i.def);
|
attrs.alloc(i.name, i.pos).mkBool(i.def != nullptr);
|
||||||
v.mkAttrs(attrs);
|
v.mkAttrs(attrs);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -4493,7 +4496,7 @@ void EvalState::createBaseEnv()
|
||||||
// the parser needs two NUL bytes as terminators; one of them
|
// the parser needs two NUL bytes as terminators; one of them
|
||||||
// is implied by being a C string.
|
// is implied by being a C string.
|
||||||
"\0";
|
"\0";
|
||||||
eval(parse(code, sizeof(code), derivationInternal, {CanonPath::root}, staticBaseEnv), *vDerivation);
|
eval(*parse(code, sizeof(code), derivationInternal, {CanonPath::root}, staticBaseEnv), *vDerivation);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -140,7 +140,7 @@ static void printValueAsXML(EvalState & state, bool strict, bool location,
|
||||||
if (v.lambda.fun->arg) attrs["name"] = state.symbols[v.lambda.fun->arg];
|
if (v.lambda.fun->arg) attrs["name"] = state.symbols[v.lambda.fun->arg];
|
||||||
if (v.lambda.fun->formals->ellipsis) attrs["ellipsis"] = "1";
|
if (v.lambda.fun->formals->ellipsis) attrs["ellipsis"] = "1";
|
||||||
XMLOpenElement _(doc, "attrspat", attrs);
|
XMLOpenElement _(doc, "attrspat", attrs);
|
||||||
for (auto & i : v.lambda.fun->formals->lexicographicOrder(state.symbols))
|
for (const Formal & i : v.lambda.fun->formals->lexicographicOrder(state.symbols))
|
||||||
doc.writeEmptyElement("attr", singletonAttrs("name", state.symbols[i.name]));
|
doc.writeEmptyElement("attr", singletonAttrs("name", state.symbols[i.name]));
|
||||||
} else
|
} else
|
||||||
doc.writeEmptyElement("varpat", singletonAttrs("name", state.symbols[v.lambda.fun->arg]));
|
doc.writeEmptyElement("varpat", singletonAttrs("name", state.symbols[v.lambda.fun->arg]));
|
||||||
|
|
|
@ -323,11 +323,11 @@ public:
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
inline void mkThunk(Env * e, Expr * ex)
|
inline void mkThunk(Env * e, Expr & ex)
|
||||||
{
|
{
|
||||||
internalType = tThunk;
|
internalType = tThunk;
|
||||||
thunk.env = e;
|
thunk.env = e;
|
||||||
thunk.expr = ex;
|
thunk.expr = &ex;
|
||||||
}
|
}
|
||||||
|
|
||||||
inline void mkApp(Value * l, Value * r)
|
inline void mkApp(Value * l, Value * r)
|
||||||
|
|
|
@ -232,7 +232,7 @@ std::pair<StorePath, Input> fetchFromWorkdir(ref<Store> store, Input & input, co
|
||||||
if (S_ISDIR(st.st_mode)) {
|
if (S_ISDIR(st.st_mode)) {
|
||||||
auto prefix = file + "/";
|
auto prefix = file + "/";
|
||||||
auto i = files.lower_bound(prefix);
|
auto i = files.lower_bound(prefix);
|
||||||
return i != files.end() && (*i).starts_with(prefix);
|
return (i != files.end() && (*i).starts_with(prefix)) || files.count(file);
|
||||||
}
|
}
|
||||||
|
|
||||||
return files.count(file);
|
return files.count(file);
|
||||||
|
|
|
@ -46,7 +46,7 @@ DownloadFileResult downloadFile(
|
||||||
request.expectedETag = getStrAttr(cached->infoAttrs, "etag");
|
request.expectedETag = getStrAttr(cached->infoAttrs, "etag");
|
||||||
FileTransferResult res;
|
FileTransferResult res;
|
||||||
try {
|
try {
|
||||||
res = getFileTransfer()->download(request);
|
res = getFileTransfer()->transfer(request);
|
||||||
} catch (FileTransferError & e) {
|
} catch (FileTransferError & e) {
|
||||||
if (cached) {
|
if (cached) {
|
||||||
warn("%s; using cached version", e.msg());
|
warn("%s; using cached version", e.msg());
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
#include "common-args.hh"
|
#include "common-args.hh"
|
||||||
#include "args/root.hh"
|
#include "args/root.hh"
|
||||||
|
#include "error.hh"
|
||||||
#include "globals.hh"
|
#include "globals.hh"
|
||||||
#include "loggers.hh"
|
#include "loggers.hh"
|
||||||
#include "logging.hh"
|
#include "logging.hh"
|
||||||
|
@ -14,14 +15,14 @@ MixCommonArgs::MixCommonArgs(const std::string & programName)
|
||||||
.shortName = 'v',
|
.shortName = 'v',
|
||||||
.description = "Increase the logging verbosity level.",
|
.description = "Increase the logging verbosity level.",
|
||||||
.category = loggingCategory,
|
.category = loggingCategory,
|
||||||
.handler = {[]() { verbosity = (Verbosity) (verbosity + 1); }},
|
.handler = {[]() { verbosity = verbosityFromIntClamped(int(verbosity) + 1); }},
|
||||||
});
|
});
|
||||||
|
|
||||||
addFlag({
|
addFlag({
|
||||||
.longName = "quiet",
|
.longName = "quiet",
|
||||||
.description = "Decrease the logging verbosity level.",
|
.description = "Decrease the logging verbosity level.",
|
||||||
.category = loggingCategory,
|
.category = loggingCategory,
|
||||||
.handler = {[]() { verbosity = verbosity > lvlError ? (Verbosity) (verbosity - 1) : lvlError; }},
|
.handler = {[]() { verbosity = verbosityFromIntClamped(int(verbosity) - 1); }},
|
||||||
});
|
});
|
||||||
|
|
||||||
addFlag({
|
addFlag({
|
||||||
|
@ -57,7 +58,7 @@ MixCommonArgs::MixCommonArgs(const std::string & programName)
|
||||||
|
|
||||||
addFlag({
|
addFlag({
|
||||||
.longName = "log-format",
|
.longName = "log-format",
|
||||||
.description = "Set the format of log output; one of `raw`, `internal-json`, `bar` or `bar-with-logs`.",
|
.description = "Set the format of log output; one of `raw`, `internal-json`, `bar`, `bar-with-logs`, `multiline` or `multiline-with-logs`.",
|
||||||
.category = loggingCategory,
|
.category = loggingCategory,
|
||||||
.labels = {"format"},
|
.labels = {"format"},
|
||||||
.handler = {[](std::string format) { setLogFormat(format); }},
|
.handler = {[](std::string format) { setLogFormat(format); }},
|
||||||
|
|
|
@ -17,6 +17,10 @@ LogFormat parseLogFormat(const std::string & logFormatStr) {
|
||||||
return LogFormat::bar;
|
return LogFormat::bar;
|
||||||
else if (logFormatStr == "bar-with-logs")
|
else if (logFormatStr == "bar-with-logs")
|
||||||
return LogFormat::barWithLogs;
|
return LogFormat::barWithLogs;
|
||||||
|
else if (logFormatStr == "multiline")
|
||||||
|
return LogFormat::multiline;
|
||||||
|
else if (logFormatStr == "multiline-with-logs")
|
||||||
|
return LogFormat::multilineWithLogs;
|
||||||
throw Error("option 'log-format' has an invalid value '%s'", logFormatStr);
|
throw Error("option 'log-format' has an invalid value '%s'", logFormatStr);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -35,6 +39,17 @@ Logger * makeDefaultLogger() {
|
||||||
logger->setPrintBuildLogs(true);
|
logger->setPrintBuildLogs(true);
|
||||||
return logger;
|
return logger;
|
||||||
}
|
}
|
||||||
|
case LogFormat::multiline: {
|
||||||
|
auto logger = makeProgressBar();
|
||||||
|
logger->setPrintMultiline(true);
|
||||||
|
return logger;
|
||||||
|
}
|
||||||
|
case LogFormat::multilineWithLogs: {
|
||||||
|
auto logger = makeProgressBar();
|
||||||
|
logger->setPrintMultiline(true);
|
||||||
|
logger->setPrintBuildLogs(true);
|
||||||
|
return logger;
|
||||||
|
}
|
||||||
default:
|
default:
|
||||||
abort();
|
abort();
|
||||||
}
|
}
|
||||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue