Compare commits

..
Author SHA1 Message Date
Jade Lovelace 4bbdb2f556 release: merge release 2.90.0 back to mainline
This merge commit returns to the previous state prior to the release but leaves the tag in the branch history.
Release created with releng/create_release.xsh

Change-Id: I7857c2d47041cc181bf5f68606ee4714e05e0436
2024-07-10 08:40:42 -07:00
Jade Lovelace 5bf891b15a release: 2.90.0 "Vanilla Ice Cream"
Release produced with releng/create_release.xsh

Change-Id: Ice53891cdeafff04bd6537be66e8cd6b7dd9678c
2024-07-10 08:40:42 -07:00
Raito Bezariusandjade f72153a510 doc/release-notes: add date for major release
Change-Id: I93aab93c069bb3989c3f8d17e0862899e6f76865
Signed-off-by: Raito Bezarius <raito@lix.systems>
2024-07-10 15:35:00 +00:00
Raito Bezarius 8da08ab551 version: finalize 2.90.0
Tested by Raito, pennae and time. It looks in good shape.

Change-Id: I32492557361888c28fe40553c699833876a1deab
Signed-off-by: Raito Bezarius <raito@lix.systems>
2024-07-10 15:32:19 +02:00
Jade Lovelace 98d0249d5c releng: fix broken manifest from 2.90-rc1
Well that is embarrassing. I think the proper thing is to just quickly
ship a -rc2, so I will open a backport of this.

    $ curl https://releases.lix.systems/manifest.nix
    # This file was generated by releng/create_release.xsh in Lix
    {
        aarch64-linux = "/nix/store/mrbknq000af7iaqhk53bnpk1fvfrc1xp-lix-2.90.0-rc1";
      aarch64-darwin = "/nix/store/z1bdccwsk34iv491aygh0mm1lgpf7yy1-lix-2.90.0-rc1";
      x86_64-darwin = "/nix/store/xqvfpdhzck44v6kyhgi9f8v0xybksb6a-lix-2.90.0-rc1";
      x86_64-linux = "/nix/store/h2ml0nx4477r84y82jgm8y80jpr72gqw-lix-release-tarballs";
    }

Change-Id: I9cf007c850c2faf995a3a9d92457517b8501d1a1
2024-06-16 19:06:45 -07:00
299 changed files with 3259 additions and 6900 deletions
+6 -2
View File
@@ -1,5 +1,9 @@
# shellcheck shell=bash
source_env_if_exists .envrc.local
# Use native-clangStdenvPackages to get clangd by default.
use flake ".#${LIX_SHELL_VARIANT:-native-clangStdenvPackages}" "${LIX_SHELL_EXTRA_ARGS[@]}"
# TODO: `use flake .#native-clangStdenvPackages` on macOS?
use flake ".#${LIX_SHELL_VARIANT:-default}" "${LIX_SHELL_EXTRA_ARGS[@]}"
export MAKEFLAGS="$MAKEFLAGS -e"
if [[ -n "$NIX_BUILD_CORES" ]]; then
export MAKEFLAGS="$MAKEFLAGS -j $NIX_BUILD_CORES"
fi
export GTEST_BRIEF=1
-1
View File
@@ -1 +0,0 @@
This is a file used by the dev shell shellHook in package.nix to check that this is actually a Lix repo before installing git hooks. Its contents have no meaning.
-17
View File
@@ -3,10 +3,6 @@
#
# It's used for crediting people accurately in release notes. The release notes
# script will link to forgejo, then to GitHub if forgejo is not present.
#
# When adding someone from outside the Lix project, you generally want to simply link their GitHub profile without adding a display name unless they are well-known in the community by that display name.
#
# See doc/manual/src/contributing/hacking.md for more documentation on this file's format and typical usage.
9999years:
display_name: wiggles
forgejo: rbt
@@ -44,11 +40,6 @@ cole-h:
display_name: Cole Helbling
github: cole-h
delan:
display_name: delan
forgejo: delan
github: delan
edolstra:
display_name: Eelco Dolstra
github: edolstra
@@ -69,10 +60,6 @@ jade:
forgejo: jade
github: lf-
kloenk:
forgejo: kloenk
github: kloenk
lovesegfault:
github: lovesegfault
@@ -96,10 +83,6 @@ puck:
forgejo: puck
github: puckipedia
quantumjump:
display_name: Quantum Jump
github: QuantumBJump
r-vdp:
github: r-vdp
-4
View File
@@ -110,7 +110,6 @@ manual = custom_target(
builtins_md,
builtin_constants_md,
rl_next_generated,
nix,
],
output : [
'manual',
@@ -187,7 +186,6 @@ foreach command : nix_nested_manpages
],
input : [
manual_md,
nix,
],
output : command[0] + '-' + page + '.1',
install : true,
@@ -300,7 +298,6 @@ foreach page : nix3_manpages
input : [
'render-manpage.sh',
manual_md,
nix,
],
output : page + '.1',
install : true,
@@ -344,7 +341,6 @@ foreach entry : nix_manpages
'render-manpage.sh',
manual_md,
entry.get(3, []),
nix,
],
output : '@0@.@1@'.format(entry[0], entry[1]),
install : true,
+1 -1
View File
@@ -1,4 +1,4 @@
#!/usr/bin/env bash
#!/bin/sh
set -euo pipefail
+1 -1
View File
@@ -1,4 +1,4 @@
#!/usr/bin/env bash
#!/bin/sh
set -euo pipefail
@@ -1,90 +0,0 @@
---
synopsis: "Trace which part of a `foo.bar.baz` expression errors"
cls: 1505, 1506
credits: Qyriad
category: Improvements
---
Previously, if an attribute path selection expression like `linux_4_9.meta.description` it wouldn't show you which one of those parts in the attribute path, or even that that line of code is what caused evaluation of the failing expression.
The previous error looks like this:
```
pkgs.linuxKernel.kernels.linux_4_9.meta.description
error:
… while evaluating the attribute 'linuxKernel.kernels.linux_4_9.meta.description'
at /nix/store/dk2rpyb6ndvfbf19bkb2plcz5y3k8i5v-source/pkgs/top-level/linux-kernels.nix:278:5:
277| } // lib.optionalAttrs config.allowAliases {
278| linux_4_9 = throw "linux 4.9 was removed because it will reach its end of life within 22.11";
| ^
279| linux_4_14 = throw "linux 4.14 was removed because it will reach its end of life within 23.11";
… while calling the 'throw' builtin
at /nix/store/dk2rpyb6ndvfbf19bkb2plcz5y3k8i5v-source/pkgs/top-level/linux-kernels.nix:278:17:
277| } // lib.optionalAttrs config.allowAliases {
278| linux_4_9 = throw "linux 4.9 was removed because it will reach its end of life within 22.11";
| ^
279| linux_4_14 = throw "linux 4.14 was removed because it will reach its end of life within 23.11";
error: linux 4.9 was removed because it will reach its end of life within 22.11
```
Now, the error will look like this:
```
pkgs.linuxKernel.kernels.linux_4_9.meta.description
error:
… while evaluating the attribute 'linuxKernel.kernels.linux_4_9.meta.description'
at /nix/store/dk2rpyb6ndvfbf19bkb2plcz5y3k8i5v-source/pkgs/top-level/linux-kernels.nix:278:5:
277| } // lib.optionalAttrs config.allowAliases {
278| linux_4_9 = throw "linux 4.9 was removed because it will reach its end of life within 22.11";
| ^
279| linux_4_14 = throw "linux 4.14 was removed because it will reach its end of life within 23.11";
… while evaluating 'pkgs.linuxKernel.kernels.linux_4_9' to select 'meta' on it
at «string»:1:1:
1| pkgs.linuxKernel.kernels.linux_4_9.meta.description
| ^
… caused by explicit throw
at /nix/store/dk2rpyb6ndvfbf19bkb2plcz5y3k8i5v-source/pkgs/top-level/linux-kernels.nix:278:17:
277| } // lib.optionalAttrs config.allowAliases {
278| linux_4_9 = throw "linux 4.9 was removed because it will reach its end of life within 22.11";
| ^
279| linux_4_14 = throw "linux 4.14 was removed because it will reach its end of life within 23.11";
error: linux 4.9 was removed because it will reach its end of life within 22.11
```
Not only does the line of code that referenced the failing attribute show up in the trace, it also tells you that it was specifically the `linux_4_9` part that failed.
This includes if the failing part is a top-level binding:
```
let
inherit (pkgs.linuxKernel.kernels) linux_4_9;
in linux_4_9.meta.description
error:
… while evaluating 'linux_4_9' to select 'meta.description' on it
at «string»:3:4:
2| inherit (pkgs.linuxKernel.kernels) linux_4_9;
3| in linux_4_9.meta.description
| ^
… while evaluating the attribute 'linux_4_9'
at /nix/store/dk2rpyb6ndvfbf19bkb2plcz5y3k8i5v-source/pkgs/top-level/linux-kernels.nix:278:5:
277| } // lib.optionalAttrs config.allowAliases {
278| linux_4_9 = throw "linux 4.9 was removed because it will reach its end of life within 22.11";
| ^
279| linux_4_14 = throw "linux 4.14 was removed because it will reach its end of life within 23.11";
… caused by explicit throw
at /nix/store/dk2rpyb6ndvfbf19bkb2plcz5y3k8i5v-source/pkgs/top-level/linux-kernels.nix:278:17:
277| } // lib.optionalAttrs config.allowAliases {
278| linux_4_9 = throw "linux 4.9 was removed because it will reach its end of life within 22.11";
| ^
279| linux_4_14 = throw "linux 4.14 was removed because it will reach its end of life within 23.11";
error: linux 4.9 was removed because it will reach its end of life within 22.11
```
-12
View File
@@ -1,12 +0,0 @@
---
synopsis: "Add a `build-dir` setting to set the backing directory for builds"
cls: 1514
credits: [roberth, tomberek]
category: Improvements
---
`build-dir` can now be set in the Nix configuration to choose the backing directory for the build sandbox.
This can be useful on systems with `/tmp` on tmpfs, or simply to relocate large builds to another disk.
Also, `XDG_RUNTIME_DIR` is no longer considered when selecting the default temporary directory,
as it's not intended to be used for large amounts of data.
@@ -1,70 +0,0 @@
---
synopsis: "Distinguish between explicit throws and errors that happened while evaluating a throw"
cls: 1511
credits: Qyriad
category: Improvements
---
Previously, errors caused by an expression like `throw "invalid argument"` were treated like an error that happened simply while some builtin function was being called:
```
let
throwMsg = p: throw "${p} isn't the right package";
in throwMsg "linuz"
error:
… while calling the 'throw' builtin
at «string»:2:17:
1| let
2| throwMsg = p: throw "${p} isn't the right package";
| ^
3| in throwMsg "linuz"
error: linuz isn't the right package
```
But the error didn't just happen "while" calling the `throw` builtin — it's a throw error!
Now it looks like this:
```
let
throwMsg = p: throw "${p} isn't the right package";
in throwMsg "linuz"
error:
… caused by explicit throw
at «string»:2:17:
1| let
2| throwMsg = p: throw "${p} isn't the right package";
| ^
3| in throwMsg "linuz"
error: linuz isn't the right package
```
This also means that incorrect usage of `throw` or errors evaluating its arguments are easily distinguishable from explicit throws:
```
let
throwMsg = p: throw "${p} isn't the right package";
in throwMsg { attrs = "error when coerced in string interpolation"; }
error:
… while calling the 'throw' builtin
at «string»:2:17:
1| let
2| throwMsg = p: throw "${p} isn't the right package";
| ^
3| in throwMsg { attrs = "error when coerced in string interpolation"; }
… while evaluating a path segment
at «string»:2:24:
1| let
2| throwMsg = p: throw "${p} isn't the right package";
| ^
3| in throwMsg { attrs = "error when coerced in string interpolation"; }
error: cannot coerce a set to a string: { attrs = "error when coerced in string interpolation"; }
```
Here, instead of an actual thrown error, a type error happens first (trying to coerce an attribute set to a string), but that type error happened *while* calling `throw`.
-29
View File
@@ -1,29 +0,0 @@
---
synopsis: Fix nix-collect-garbage --dry-run
issues: [fj#432]
cls: [1566]
category: Fixes
credits: [quantumjump]
---
`nix-collect-garbage --dry-run` did not previously give any output - it simply
exited without even checking to see what paths would be deleted.
```
$ nix-collect-garbage --dry-run
$
```
We updated the behaviour of the flag such that instead it prints out how many
paths it *would* delete, but doesn't actually delete them.
```
$ nix-collect-garbage --dry-run
finding garbage collector roots...
determining live/dead paths...
...
<nix store paths>
...
2670 store paths deleted, 0.00MiB freed
$
```
@@ -1,16 +0,0 @@
---
synopsis: "Hash mismatch diagnostics for fixed-output derivations include the URL"
cls: [1536]
credits: [jade]
category: Improvements
---
Now, when building fixed-output derivations, Lix will guess the URL that was used in the derivation using the `url` or `urls` properties in the derivation environment.
This is a layering violation but making these diagnostics tractable when there are multiple instances of the `AAAA` hash is too significant of an improvement to pass it up.
```
error: hash mismatch in fixed-output derivation '/nix/store/sjfw324j4533lwnpmr5z4icpb85r63ai-x1.drv':
likely URL: https://meow.puppy.forge/puppy.tar.gz
specified: sha256-AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=
got: sha256-a1Qvp3FOOkWpL9kFHgugU1ok5UtRPSu+NwCZKbbaEro=
```
@@ -1,14 +0,0 @@
---
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.
-12
View File
@@ -1,12 +0,0 @@
---
synopsis: "`nix copy` is now several times faster at `querying info about /nix/store/...`"
cls: [1462]
issues: [fj#366]
credits: [jade]
category: Fixes
---
We fixed a locking bug that serialized `querying info about /nix/store/...`
onto just one thread such that it was eating `O(paths to copy * latency)` time
while setting up to copy paths to s3 and other stores. It is now `nproc` times
faster.
@@ -1,21 +0,0 @@
---
synopsis: "Lix no longer speaks the Nix remote-build worker protocol to clients or servers older than CppNix 2.3"
cls: [1207, 1208, 1206, 1205, 1204, 1203, 1479]
issues: [fj#325]
credits: [jade]
category: Breaking Changes
---
CppNix 2.3 was released in 2019, and is the new oldest supported version. We
will increase our support baseline in the future up to a final version of CppNix
2.18 (which may happen soon given that it is the only still-packaged and thus
still-tested >2.3 version), but this step already removes a significant amount
of dead, untested, code paths.
Lix speaks the same version of the protocol as CppNix 2.18 and that fact will
never change in the future; the Lix plans to replace the protocol for evolution
will entail a complete incompatible replacement that will be supported in
parallel with the old protocol. Lix will thus retain remote build compatibility
with CppNix as long as CppNix maintains protocol compatibility with 2.18, and
as long as Lix retains legacy protocol support (which will likely be a long
time given that we plan to convert it to a frozen-in-time shim).
@@ -1,10 +0,0 @@
---
synopsis: "`nix registry add` now requires a shorthand flakeref on the 'from' side"
cls: 1494
credits: delan
category: Improvements
---
The 'from' argument must now be a shorthand flakeref like `nixpkgs` or `nixpkgs/nixos-20.03`, making it harder to accidentally swap the 'from' and 'to' arguments.
Registry entries that map from other flake URLs can still be specified in registry.json, the `nix.registry` option in NixOS, or the `--override-flake` option in the CLI, but they are not guaranteed to work correctly.
@@ -1,9 +0,0 @@
---
synopsis: Allow automatic rejection of configuration options from flakes
cls: [1541]
credits: [alois31]
category: Improvements
---
Setting `accept-flake-config` to `false` now respects user choice by automatically rejecting configuration options set by flakes.
The old behaviour of asking each time is still available (and default) by setting it to the special value `ask`.
@@ -1,8 +0,0 @@
---
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.
-10
View File
@@ -1,10 +0,0 @@
---
synopsis: "Lix now supports building with UndefinedBehaviorSanitizer"
cls: [1483]
credits: [jade]
category: Development
---
You can now build Lix with the configuration option `-Db_sanitize=undefined` and it will both work and pass tests. AddressSanitizer support is also coming soon.
For a list of undefined behaviour fixed by sanitizer usage, see [the gerrit topic "undefined-behaviour"](https://gerrit.lix.systems/q/topic:%22undefined-behaviour%22).
+1 -1
View File
@@ -197,7 +197,7 @@
- [Release Notes](release-notes/release-notes.md)
- [Upcoming release](release-notes/rl-next.md)
<!-- RELENG-AUTO-INSERTION-MARKER (see releng/release_notes.py) -->
- [Lix 2.90 (FIXME date)](release-notes/rl-2.90.md)
- [Lix 2.90 (2024-07-10)](release-notes/rl-2.90.md)
- [Nix 2.18 (2023-09-20)](release-notes/rl-2.18.md)
- [Nix 2.17 (2023-07-24)](release-notes/rl-2.17.md)
- [Nix 2.16 (2023-05-31)](release-notes/rl-2.16.md)
-10
View File
@@ -78,16 +78,6 @@ Most commands in Lix accept the following command-line options:
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`
By default, output written by builders to standard output and standard error is echoed to the Lix command's standard error.
+25 -73
View File
@@ -39,19 +39,17 @@ $ nix-shell -A native-clangStdenvPackages
### Building from the development shell
You can build and test Lix with just:
As always you may run [stdenv's phases by name](https://nixos.org/manual/nixpkgs/unstable/#sec-building-stdenv-package-in-nix-shell), e.g.:
```bash
$ just setup
$ just build
$ just test --suite=check
$ just install
$ just test --suite=installcheck
$ configurePhase
$ buildPhase
$ checkPhase
$ installPhase
$ installCheckPhase
```
(Check and installcheck may both be done after install, allowing you to omit the --suite argument entirely, but this is the order package.nix runs them in.)
You can also build Lix manually:
To build manually, however, use the following:
```bash
$ meson setup ./build "--prefix=$out" $mesonFlags
@@ -66,7 +64,9 @@ $ meson install -C build
$ meson test -C build --suite=installcheck
```
In both cases, Lix will be installed to `$PWD/outputs`, the `/bin` of which is prepended to PATH in the development shells.
(Check and installcheck may both be done after install, allowing you to omit the --suite argument entirely, but this is the order package.nix runs them in.)
This will install Lix to `$PWD/outputs`, the `/bin` of which is prepended to PATH in the development shells.
If the tests fail and Meson helpfully has no output for why, use the `--print-error-logs` option to `meson test`.
@@ -168,26 +168,8 @@ or for Nix with the [`flakes`] and [`nix-command`] experimental features enabled
$ nix build .#packages.aarch64-linux.default
```
### Cross compiling using the Lix flake
Lix can also be easily cross compiled to the following arbitrarily-chosen system doubles, which can be useful for bootstrapping Lix on new platforms.
These are specified in `crossSystems` in `flake.nix`; feel free to submit changes to add new ones if they are useful to you.
- `armv6l-linux`
- `armv7l-linux`
- `riscv64-linux`
For example, to cross-compile Lix for `armv6l-linux` from another Linux, use the following:
```console
$ nix build .#nix-armv6l-linux
```
It's also possible to cross-compile a tarball of binaries suitable for the Lix installer, for example, for `riscv64-linux`:
```console
$ nix build .#nix-riscv64-linux.passthru.binaryTarball
```
Cross-compiled builds are available for ARMv6 (`armv6l-linux`) and ARMv7 (`armv7l-linux`).
Add more [system types](#system-type) to `crossSystems` in `flake.nix` to bootstrap Nix on unsupported platforms.
### Building for multiple platforms at once
@@ -300,8 +282,9 @@ Regular markdown files used for the manual have a base path of their own and the
## API documentation
Doxygen API documentation will be available online in the future ([tracking issue](https://git.lix.systems/lix-project/lix/issues/422)).
You can also build and view it yourself:
Doxygen API documentation is [available
online](https://hydra.nixos.org/job/nix/master/internal-api-docs/latest/download-by-type/doc/internal-api-docs). You
can also build and view it yourself:
```console
# nix build .#hydraJobs.internal-api-docs
@@ -311,50 +294,44 @@ You can also build and view it yourself:
or inside a `nix develop` shell by running:
```bash
$ meson configure build -Dinternal-api-docs=enabled
$ meson compile -C build internal-api-docs
$ xdg-open ./outputs/doc/share/doc/nix/internal-api/html/index.html
```
## Coverage analysis
A coverage analysis report will be available online in the future (FIXME(lix-hydra)).
You can build it yourself:
A coverage analysis report is [available
online](https://hydra.nixos.org/job/nix/master/coverage/latest/download-by-type/report/coverage). You
can build it yourself:
```
# nix build .#hydraJobs.coverage
# xdg-open ./result/coverage/index.html
```
Metrics about the change in line/function coverage over time will be available in the future (FIXME(lix-hydra)).
Metrics about the change in line/function coverage over time are also
[available](https://hydra.nixos.org/job/nix/master/coverage#tabs-charts).
## Add a release note
`doc/manual/rl-next` contains release notes entries for all unreleased changes.
User-visible changes should come with a release note.
Developer-facing changes should have a release note in the Development category if they are significant and if developers should know about them.
### Add an entry
Here's what a complete entry looks like.
The file name is not incorporated in the final document, and is generally a super brief summary of the change synopsis.
Here's what a complete entry looks like. The file name is not incorporated in the document.
```markdown
```
---
synopsis: Basically a title
# 1234 or gh#1234 will refer to CppNix GitHub, fj#1234 will refer to a Lix forgejo issue.
issues: [1234, fj#1234]
# Use this *only* if there is a CppNix pull request associated with this change.
# Use this *only* if there is a CppNix pull request associated with this change
prs: 1238
# List of Lix Gerrit changelist numbers.
# If there is an associated Lix GitHub PR, just put in the Gerrit CL number.
# List of Lix Gerrit changelist numbers; if there is an associated Lix GitHub
# PR, just put in the Gerrit CL number.
cls: [123]
# Heading that this release note will appear under.
category: Breaking Changes
# Add a credit mention in the bottom of the release note.
# your-name is used as a key into doc/manual/change-authors.yml for metadata
credits: [your-name]
---
Here's one or more paragraphs that describe the change.
@@ -369,31 +346,6 @@ Significant changes should add the following header, which moves them to the top
significance: significant
```
The following categories of release notes are supported (see `maintainers/build-release-notes.py`):
- Breaking Changes
- Features
- Improvements
- Fixes
- Packaging
- Development
- Miscellany
The `credits` field, if present, gives credit to the author of the patch in the release notes with a message like "Many thanks to (your-name) for this" and linking to GitHub or Forgejo profiles if listed.
If you are forward-porting a change from CppNix, please credit the original author, and optionally credit yourself.
When adding credits metadata for people external to the project and deciding whether to put in a `display_name`, consider what they are generally known as in the community; even if you know their full name (e.g. from their GitHub profile), we suggest only adding it as a display name if that is what they go by in the community.
There are multiple reasons we follow this practice, but it boils down to privacy and consent: we would rather not capture full names that are not widely used in the community without the consent of the parties involved, even if they are publicly available.
As of this writing, the entries with full names as `display_name` are either members of the CppNix team or people who added them themselves.
The names specified in `credits` are used as keys to look up the authorship info in `doc/manual/change-authors.yml`.
The only mandatory part is that every key appearing in `credits` has an entry present in `change-authors.yml`.
All of the following properties are optional; you can specify `{}` as the metadata if you want a simple non-hyperlinked mention.
The following properties are supported:
- `display_name`: display name used in place of the key when showing names, if present.
- `forgejo`: Forgejo username. The name in the release notes will be a link to this, if present.
- `github`: GitHub username, used if `forgejo` is not set, again making a link.
### Build process
Releases have a precomputed `rl-MAJOR.MINOR.md`, and no `rl-next.md`.
+3
View File
@@ -433,6 +433,9 @@ I grepped `src/` for `get[eE]nv\("` to find the mentions in Lix code.
- `NIX_PROFILE` - Selects which profile `nix-env` will operate on. Documented elsewhere.
- `NIX_SSHOPTS` - Options passed to `ssh(1)` when using a ssh remote store.
Incorrectly documented on `nix-copy-closure` which is *surely* not the only place they are used??
- `_NIX_TEST_NO_LSOF` - Used on non-Linux, non-macOS platforms to disable using `lsof` when finding gc roots.
Since https://git.lix.systems/lix-project/lix/issues/156 was fixed, this should probably just be removed as it was a bad workaround for a macOS issue.
- `_NIX_TEST_GC_SYNC_1` - Path to a pipe that is used to block the GC briefly to validate invariants from the test suite.
- `_NIX_TEST_GC_SYNC_2` - Path to a pipe that is used to block the GC briefly to validate invariants from the test suite.
- `_NIX_TEST_FREE_SPACE_FILE` - Path to a file containing a decimal number with the free space that the GC is to believe it has.
+2 -2
View File
@@ -1,7 +1,7 @@
# Lix 2.90 "Vanilla Ice Cream" (FIXME date)
# Lix 2.90 "Vanilla Ice Cream" (2024-07-10)
# Lix 2.90.0 (FIXME date)
# Lix 2.90.0 (2024-07-10)
## 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)
+3 -16
View File
@@ -84,8 +84,6 @@
];
systems = linuxSystems ++ darwinSystems;
# If you add something here, please update the list in doc/manual/src/contributing/hacking.md.
# Thanks~
crossSystems = [
"armv6l-linux"
"armv7l-linux"
@@ -197,19 +195,14 @@
busybox-sandbox-shell = final.busybox-sandbox-shell or final.default-busybox-sandbox-shell;
};
pegtl = final.nix.passthru.pegtl;
# Export the patched version of boehmgc that Lix uses into the overlay
# for consumers of this flake.
boehmgc-nix = final.nix.passthru.boehmgc-nix;
boehmgc-nix = final.nix.boehmgc-nix;
# And same thing for our build-release-notes package.
build-release-notes = final.nix.passthru.build-release-notes;
build-release-notes = final.nix.build-release-notes;
};
in
{
# for repl debugging
inherit self;
# A Nixpkgs overlay that overrides the 'nix' and
# 'nix.perl-bindings' packages.
overlays.default = overlayFor (p: p.stdenv);
@@ -240,11 +233,6 @@
}
);
# Completion tests for the Nix REPL.
repl-completion = forAllSystems (
system: nixpkgsFor.${system}.native.callPackage ./tests/repl-completion.nix { }
);
# Perl bindings for various platforms.
perlBindings = forAllSystems (system: nixpkgsFor.${system}.native.nix.passthru.perl-bindings);
@@ -342,7 +330,6 @@
rl-next = self.hydraJobs.rl-next.${system}.user;
# Will be empty attr set on i686-linux, and filtered out by forAvailableSystems.
pre-commit = self.hydraJobs.pre-commit.${system};
repl-completion = self.hydraJobs.repl-completion.${system};
}
// (lib.optionalAttrs (builtins.elem system linux64BitSystems)) {
dockerImage = self.hydraJobs.dockerImage.${system};
@@ -392,7 +379,7 @@
nix = pkgs.callPackage ./package.nix {
inherit stdenv officialRelease versionSuffix;
busybox-sandbox-shell = pkgs.busybox-sandbox-shell or pkgs.default-busybox-sandbox;
internalApiDocs = false;
internalApiDocs = true;
};
pre-commit = self.hydraJobs.pre-commit.${pkgs.system} or { };
in
+2 -2
View File
@@ -9,8 +9,8 @@ clean:
rm -rf build
# Prepare meson for building
setup *OPTIONS:
meson setup build --prefix="$PWD/outputs/out" $mesonFlags {{ OPTIONS }}
setup:
meson setup build --prefix="$PWD/outputs/out"
# Build lix
build *OPTIONS:
-2
View File
@@ -20,8 +20,6 @@ SIGNIFICANCECES = {
# This is just hardcoded for better validation. If you think there should be
# more of them, feel free to add more.
#
# Please update doc/manual/src/contributing/hacking.md if you do. Thanks~
CATEGORIES = [
'Breaking Changes',
'Features',
+1 -1
View File
@@ -1,4 +1,4 @@
#!/usr/bin/env bash
#!/bin/sh
# Generates a report of build time based on a meson build using -ftime-trace in
# Clang.
+10 -36
View File
@@ -142,16 +142,6 @@ else
cpp_pch = []
endif
# gcc 12 is known to miscompile some coroutine-based code quite horribly,
# causing (among other things) copies of move-only objects and the double
# frees one would expect when the objects are unique_ptrs. these problems
# often show up as memory corruption when nesting generators (since we do
# treat generators like owned memory) and will cause inexplicable crashs.
assert(
cxx.get_id() != 'gcc' or cxx.version().version_compare('>=13'),
'GCC 12 and earlier are known to miscompile lix coroutines, use GCC 13 or clang.'
)
# Translate some historical and Mesony CPU names to Lixy CPU names.
# FIXME(Qyriad): the 32-bit x86 code is not tested right now, because cross compilation for Lix
@@ -177,7 +167,6 @@ message('canonical Nix system name:', host_system)
is_linux = host_machine.system() == 'linux'
is_darwin = host_machine.system() == 'darwin'
is_freebsd = host_machine.system() == 'freebsd'
is_x64 = host_machine.cpu_family() == 'x86_64'
# Per-platform arguments that you should probably pass to shared_module() invocations.
@@ -233,8 +222,7 @@ brotli = [
openssl = dependency('libcrypto', 'openssl', required : true)
# 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 = dependency('aws-cpp-sdk-core', required : false)
aws_sdk_transfer = dependency('aws-cpp-sdk-transfer', required : aws_sdk.found(), fallback : ['aws_sdk', 'aws_cpp_sdk_transfer_dep'])
if aws_sdk.found()
# The AWS pkg-config adds -std=c++11.
@@ -246,6 +234,12 @@ if aws_sdk.found()
links : 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(
compile_args : false,
includes : true,
@@ -297,14 +291,6 @@ gtest = [
toml11 = dependency('toml11', version : '>=3.7.0', required : true, method : 'cmake')
pegtl = dependency(
'pegtl',
version : '>=3.2.7',
required : true,
method : 'cmake',
modules : [ 'taocpp::pegtl' ],
)
nlohmann_json = dependency('nlohmann_json', required : true)
# lix-doc is a Rust project provided via buildInputs and unfortunately doesn't have any way to be detected.
@@ -353,6 +339,8 @@ endif
# that busybox sh won't run busybox applets as builtins (which would break our sandbox).
lsof = find_program('lsof', native : true)
bison = find_program('bison', native : true)
flex = find_program('flex', native : true)
# This is how Nix does generated headers...
# other instances of header generation use a very similar command.
@@ -442,7 +430,6 @@ add_project_arguments(
'-Wimplicit-fallthrough',
'-Werror=switch',
'-Werror=switch-enum',
'-Werror=unused-result',
'-Wdeprecated-copy',
'-Wignored-qualifiers',
# Enable assertions in libstdc++ by default. Harmless on libc++. Benchmarked
@@ -454,9 +441,7 @@ add_project_arguments(
language : 'cpp',
)
# 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')
if cxx.get_id() in ['gcc', 'clang']
# 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.
#
@@ -471,23 +456,12 @@ if cxx.get_id() in ['gcc', 'clang'] and 'undefined' not in get_option('b_sanitiz
add_project_arguments(sanitize_args, language: 'cpp')
add_project_link_arguments(sanitize_args, language: 'cpp')
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')
if cxx.get_linker_id() in ['ld.bfd', 'ld.gold']
add_project_link_arguments('-Wl,--no-copy-dt-needed-entries', language : 'cpp')
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
# maintainers/buildtime_report.sh BUILD-DIR to simply work in clang builds.
#
+50
View File
@@ -0,0 +1,50 @@
#!/usr/bin/env bash
# Meson will call this with an absolute path to Bash.
# The shebang is just for convenience.
# The parser and lexer tab are generated via custom Meson targets in src/libexpr/meson.build,
# but Meson doesn't support marking only part of a target for install. The generation creates
# both headers (parser-tab.hh, lexer-tab.hh) and source files (parser-tab.cc, lexer-tab.cc),
# and we definitely want the former installed, but not the latter. This script is added to
# Meson's install steps to correct this, as the logic for it is just complex enough to
# warrant separate and careful handling, because both Meson's configured include directory
# may or may not be an absolute path, and DESTDIR may or may not be set at all, but can't be
# manipulated in Meson logic.
set -euo pipefail
echo "cleanup-install: removing Meson-placed C++ sources from dest includedir"
if [[ "${1/--help/}" != "$1" ]]; then
echo "cleanup-install: this script should only be called from the Meson build system"
exit 1
fi
# Ensure the includedir was passed as the first argument
# (set -u will make this fail otherwise).
includedir="$1"
# And then ensure that first argument is a directory that exists.
if ! [[ -d "$1" ]]; then
echo "cleanup-install: this script should only be called from the Meson build system"
echo "argv[1] (${1@Q}) is not a directory"
exit 2
fi
# If DESTDIR environment variable is set, prepend it to the include dir.
# Unfortunately, we cannot do this on the Meson side. We do have an environment variable
# `MESON_INSTALL_DESTDIR_PREFIX`, but that will not refer to the include directory if
# includedir has been set separately, which Lix's split-output derivation does.
# We also cannot simply do an inline bash conditional like "${DESTDIR:=}" or similar,
# because we need to specifically *join* DESTDIR and includedir with a slash, and *not*
# have a slash if DESTDIR isn't set at all, since $includedir could be a relative directory.
# Finally, DESTDIR is only available to us as an environment variable in these install scripts,
# not in Meson logic.
# Therefore, our best option is to have Meson pass this script the configured includedir,
# and perform this dance with it and $DESTDIR.
if [[ -n "${DESTDIR:-}" ]]; then
includedir="$DESTDIR/$includedir"
fi
# Intentionally not using -f.
# If these files don't exist then our assumptions have been violated and we should fail.
rm -v "$includedir/lix/libexpr/parser-tab.cc" "$includedir/lix/libexpr/lexer-tab.cc"
-23
View File
@@ -1,23 +0,0 @@
{
stdenv,
cmake,
ninja,
fetchFromGitHub,
}:
stdenv.mkDerivation {
pname = "pegtl";
version = "3.2.7";
src = fetchFromGitHub {
repo = "PEGTL";
owner = "taocpp";
rev = "refs/tags/3.2.7";
hash = "sha256-IV5YNGE4EWVrmg2Sia/rcU8jCuiBynQGJM6n3DCWTQU=";
};
nativeBuildInputs = [
cmake
ninja
];
}
+8 -27
View File
@@ -10,16 +10,17 @@
boehmgc-nix ? __forDefaults.boehmgc-nix,
boehmgc,
nlohmann_json,
bison,
build-release-notes ? __forDefaults.build-release-notes,
boost,
brotli,
bzip2,
callPackage,
cmake,
curl,
doxygen,
editline-lix ? __forDefaults.editline-lix,
editline,
flex,
git,
gtest,
jq,
@@ -35,7 +36,6 @@
meson,
ninja,
openssl,
pegtl ? __forDefaults.pegtl,
pkg-config,
python3,
rapidcheck,
@@ -76,10 +76,8 @@
configureFlags = prev.configureFlags or [ ] ++ [ (lib.enableFeature true "sigstop") ];
});
lix-doc = callPackage ./lix-doc/package.nix { };
build-release-notes = callPackage ./maintainers/build-release-notes.nix { };
pegtl = callPackage ./misc/pegtl.nix { };
lix-doc = pkgs.callPackage ./lix-doc/package.nix { };
build-release-notes = pkgs.callPackage ./maintainers/build-release-notes.nix { };
},
}:
let
@@ -212,6 +210,8 @@ stdenv.mkDerivation (finalAttrs: {
nativeBuildInputs =
[
bison
flex
python3
meson
ninja
@@ -250,7 +250,6 @@ stdenv.mkDerivation (finalAttrs: {
libsodium
toml11
lix-doc
pegtl
]
++ lib.optionals hostPlatform.isLinux [
libseccomp
@@ -305,9 +304,6 @@ stdenv.mkDerivation (finalAttrs: {
else
appendToVar configureFlags "--disable-tests"
fi
# Fix up /usr/bin/env shebangs relied on by the build
patchShebangs --build tests/ doc/manual/
'';
mesonBuildType = "debugoptimized";
@@ -383,12 +379,7 @@ stdenv.mkDerivation (finalAttrs: {
# Export the patched version of boehmgc.
# flake.nix exports that into its overlay.
passthru = {
inherit (__forDefaults)
boehmgc-nix
editline-lix
build-release-notes
pegtl
;
inherit (__forDefaults) boehmgc-nix editline-lix build-release-notes;
inherit officialRelease;
@@ -401,7 +392,6 @@ stdenv.mkDerivation (finalAttrs: {
bashInteractive,
clang-tools,
clangbuildanalyzer,
doxygen,
glibcLocales,
just,
llvmPackages,
@@ -467,10 +457,6 @@ stdenv.mkDerivation (finalAttrs: {
skopeo
just
nixfmt
# Included above when internalApiDocs is true, but we set that to
# false intentionally to save dev build time.
# To build them in a dev shell, you can set -Dinternal-api-docs=enabled when configuring.
doxygen
# Load-bearing order. Must come before clang-unwrapped below, but after clang_tools above.
stdenv.cc
]
@@ -490,7 +476,7 @@ stdenv.mkDerivation (finalAttrs: {
# https://git.lix.systems/lix-project/lix/src/commit/7575db522e9008685c4009423398f6900a16bcce/src/nix/develop.cc#L240-L241
# this is, of course, absurd.
if [[ $name != lix-shell-env && $name != lix-shell-env-env ]]; then
return
return;
fi
PATH=$prefix/bin:$PATH
@@ -500,11 +486,6 @@ stdenv.mkDerivation (finalAttrs: {
# Make bash completion work.
XDG_DATA_DIRS+=:$out/share
if [[ ! -f ./.this-is-lix ]]; then
echo "Dev shell not started from inside a Lix repo, skipping repo setup" >&2
return
fi
${lib.optionalString (pre-commit-checks ? shellHook) pre-commit-checks.shellHook}
# Allow `touch .nocontribmsg` to turn this notice off.
if ! [[ -f .nocontribmsg ]]; then
-1
View File
@@ -65,6 +65,5 @@ if cxx.get_linker_id() in ['ld.bfd', 'ld.gold']
endif
libstore = dependency('lixstore', 'lix-store', required : true)
libutil = dependency('lixutil', 'lix-util', required : true)
subdir('lib/Nix')
-4
View File
@@ -1,7 +1,3 @@
// SPDX-FileCopyrightText: 2024 Nix and Lix Authors
//
// SPDX-License-Identifier: LGPL-2.1-only
#include "built-path.hh"
#include "derivations.hh"
#include "store-api.hh"
-4
View File
@@ -1,7 +1,3 @@
// SPDX-FileCopyrightText: 2024 Nix and Lix Authors
//
// SPDX-License-Identifier: LGPL-2.1-only
#pragma once
///@file
#include "derived-path.hh"
+1 -5
View File
@@ -1,7 +1,3 @@
// SPDX-FileCopyrightText: 2024 Nix and Lix Authors
//
// SPDX-License-Identifier: LGPL-2.1-only
#include <set>
#include "cmd-profiles.hh"
@@ -248,7 +244,7 @@ StorePath ProfileManifest::build(ref<Store> store)
/* Add the symlink tree to the store. */
StringSink sink;
sink << dumpPath(tempDir);
dumpPath(tempDir, sink);
auto narHash = hashString(htSHA256, sink.s);
-4
View File
@@ -1,7 +1,3 @@
// SPDX-FileCopyrightText: 2024 Nix and Lix Authors
//
// SPDX-License-Identifier: LGPL-2.1-only
#pragma once
///@file
+11
View File
@@ -0,0 +1,11 @@
#include "command-installable-value.hh"
namespace nix {
void InstallableValueCommand::run(ref<Store> store, ref<Installable> installable)
{
auto installableValue = InstallableValue::require(installable);
run(store, installableValue);
}
}
+23
View File
@@ -0,0 +1,23 @@
#pragma once
///@file
#include "installable-value.hh"
#include "command.hh"
namespace nix {
/**
* An InstallableCommand where the single positional argument must be an
* InstallableValue in particular.
*/
struct InstallableValueCommand : InstallableCommand
{
/**
* Entry point to this command
*/
virtual void run(ref<Store> store, ref<InstallableValue> installable) = 0;
void run(ref<Store> store, ref<Installable> installable) override;
};
}
-4
View File
@@ -1,7 +1,3 @@
// SPDX-FileCopyrightText: 2024 Nix and Lix Authors
//
// SPDX-License-Identifier: LGPL-2.1-only
#include "command.hh"
#include "store-api.hh"
#include "local-fs-store.hh"
-4
View File
@@ -1,7 +1,3 @@
// SPDX-FileCopyrightText: 2024 Nix and Lix Authors
//
// SPDX-License-Identifier: LGPL-2.1-only
#pragma once
///@file
-4
View File
@@ -1,7 +1,3 @@
// SPDX-FileCopyrightText: 2024 Nix and Lix Authors
//
// SPDX-License-Identifier: LGPL-2.1-only
#include "eval-settings.hh"
#include "common-eval-args.hh"
#include "shared.hh"
-4
View File
@@ -1,7 +1,3 @@
// SPDX-FileCopyrightText: 2024 Nix and Lix Authors
//
// SPDX-License-Identifier: LGPL-2.1-only
#pragma once
///@file
-4
View File
@@ -1,7 +1,3 @@
// SPDX-FileCopyrightText: 2024 Nix and Lix Authors
//
// SPDX-License-Identifier: LGPL-2.1-only
#include "editor-for.hh"
#include "environment-variables.hh"
#include "source-path.hh"
-4
View File
@@ -1,7 +1,3 @@
// SPDX-FileCopyrightText: 2024 Nix and Lix Authors
//
// SPDX-License-Identifier: LGPL-2.1-only
#pragma once
///@file
-4
View File
@@ -1,7 +1,3 @@
// SPDX-FileCopyrightText: 2024 Nix and Lix Authors
//
// SPDX-License-Identifier: LGPL-2.1-only
#include "globals.hh"
#include "installable-attr-path.hh"
#include "outputs-spec.hh"
-4
View File
@@ -1,7 +1,3 @@
// SPDX-FileCopyrightText: 2024 Nix and Lix Authors
//
// SPDX-License-Identifier: LGPL-2.1-only
#pragma once
///@file
-4
View File
@@ -1,7 +1,3 @@
// SPDX-FileCopyrightText: 2024 Nix and Lix Authors
//
// SPDX-License-Identifier: LGPL-2.1-only
#include "installable-derived-path.hh"
#include "derivations.hh"
-4
View File
@@ -1,7 +1,3 @@
// SPDX-FileCopyrightText: 2024 Nix and Lix Authors
//
// SPDX-License-Identifier: LGPL-2.1-only
#pragma once
///@file
-4
View File
@@ -1,7 +1,3 @@
// SPDX-FileCopyrightText: 2024 Nix and Lix Authors
//
// SPDX-License-Identifier: LGPL-2.1-only
#include "globals.hh"
#include "installable-flake.hh"
#include "installable-derived-path.hh"
-4
View File
@@ -1,7 +1,3 @@
// SPDX-FileCopyrightText: 2024 Nix and Lix Authors
//
// SPDX-License-Identifier: LGPL-2.1-only
#pragma once
///@file
-4
View File
@@ -1,7 +1,3 @@
// SPDX-FileCopyrightText: 2024 Nix and Lix Authors
//
// SPDX-License-Identifier: LGPL-2.1-only
#include "installable-value.hh"
#include "eval-cache.hh"
#include "fetch-to-store.hh"
-4
View File
@@ -1,7 +1,3 @@
// SPDX-FileCopyrightText: 2024 Nix and Lix Authors
//
// SPDX-License-Identifier: LGPL-2.1-only
#pragma once
///@file
+11 -22
View File
@@ -1,7 +1,3 @@
// SPDX-FileCopyrightText: 2024 Nix and Lix Authors
//
// SPDX-License-Identifier: LGPL-2.1-only
#include "globals.hh"
#include "installables.hh"
#include "installable-derived-path.hh"
@@ -218,7 +214,7 @@ void SourceExprCommand::completeInstallable(AddCompletions & completions, std::s
evalSettings.pureEval = false;
auto state = getEvalState();
Expr & e = state->parseExprFromFile(
Expr *e = state->parseExprFromFile(
resolveExprPath(state->checkSourcePath(lookupFileArg(*state, *file)))
);
@@ -397,10 +393,13 @@ ref<eval_cache::EvalCache> openEvalCache(
EvalState & state,
std::shared_ptr<flake::LockedFlake> lockedFlake)
{
auto fingerprint = evalSettings.useEvalCache && evalSettings.pureEval
? std::make_optional(lockedFlake->getFingerprint())
: std::nullopt;
auto rootLoader = [&state, lockedFlake]()
auto fingerprint = lockedFlake->getFingerprint();
return make_ref<nix::eval_cache::EvalCache>(
evalSettings.useEvalCache && evalSettings.pureEval
? std::optional { std::cref(fingerprint) }
: std::nullopt,
state,
[&state, lockedFlake]()
{
/* For testing whether the evaluation cache is
complete. */
@@ -416,17 +415,7 @@ ref<eval_cache::EvalCache> openEvalCache(
assert(aOutputs);
return aOutputs->value;
};
if (fingerprint) {
auto search = state.evalCaches.find(fingerprint.value());
if (search == state.evalCaches.end()) {
search = state.evalCaches.emplace(fingerprint.value(), make_ref<nix::eval_cache::EvalCache>(fingerprint, state, rootLoader)).first;
}
return search->second;
} else {
return make_ref<nix::eval_cache::EvalCache>(std::nullopt, state, rootLoader);
}
});
}
Installables SourceExprCommand::parseInstallables(
@@ -445,13 +434,13 @@ Installables SourceExprCommand::parseInstallables(
auto vFile = state->allocValue();
if (file == "-") {
auto & e = state->parseStdin();
auto e = state->parseStdin();
state->eval(e, *vFile);
}
else if (file)
state->evalFile(lookupFileArg(*state, *file), *vFile);
else {
auto & e = state->parseExprFromString(*expr, state->rootPath(CanonPath::fromCwd()));
auto e = state->parseExprFromString(*expr, state->rootPath(CanonPath::fromCwd()));
state->eval(e, *vFile);
}
-4
View File
@@ -1,7 +1,3 @@
// SPDX-FileCopyrightText: 2024 Nix and Lix Authors
//
// SPDX-License-Identifier: LGPL-2.1-only
#pragma once
///@file
-4
View File
@@ -1,7 +1,3 @@
// SPDX-FileCopyrightText: 2024 Nix and Lix Authors
//
// SPDX-License-Identifier: LGPL-2.1-only
#include "legacy.hh"
namespace nix {
-4
View File
@@ -1,7 +1,3 @@
// SPDX-FileCopyrightText: 2024 Nix and Lix Authors
//
// SPDX-License-Identifier: LGPL-2.1-only
#pragma once
///@file
-4
View File
@@ -1,7 +1,3 @@
// SPDX-FileCopyrightText: 2024 Nix and Lix Authors
//
// SPDX-License-Identifier: LGPL-2.1-only
#include "markdown.hh"
#include "error.hh"
#include "finally.hh"
-4
View File
@@ -1,7 +1,3 @@
// SPDX-FileCopyrightText: 2024 Nix and Lix Authors
//
// SPDX-License-Identifier: LGPL-2.1-only
#pragma once
///@file
+2 -4
View File
@@ -1,9 +1,6 @@
# SPDX-FileCopyrightText: 2024 Nix and Lix Authors
#
# SPDX-License-Identifier: LGPL-2.1-only
libcmd_sources = files(
'built-path.cc',
'command-installable-value.cc',
'cmd-profiles.cc',
'command.cc',
'common-eval-args.cc',
@@ -21,6 +18,7 @@ libcmd_sources = files(
libcmd_headers = files(
'built-path.hh',
'command-installable-value.hh',
'cmd-profiles.hh',
'command.hh',
'common-eval-args.hh',
-4
View File
@@ -1,7 +1,3 @@
// SPDX-FileCopyrightText: 2024 Nix and Lix Authors
//
// SPDX-License-Identifier: LGPL-2.1-only
#include "error.hh"
#include "file-system.hh"
#include "logging.hh"
-4
View File
@@ -1,7 +1,3 @@
// SPDX-FileCopyrightText: 2024 Nix and Lix Authors
//
// SPDX-License-Identifier: LGPL-2.1-only
#pragma once
/// @file
-4
View File
@@ -1,7 +1,3 @@
# SPDX-FileCopyrightText: 2024 Nix and Lix Authors
#
# SPDX-License-Identifier: LGPL-2.1-only
info: initial: functions:
let
final = builtins.foldl' (prev: function: prev // (function info final prev)) initial functions;
+19 -85
View File
@@ -1,14 +1,7 @@
// SPDX-FileCopyrightText: 2024 Nix and Lix Authors
//
// SPDX-License-Identifier: LGPL-2.1-only
#include <cstdio>
#include <editline.h>
#include <iostream>
#include <cstdlib>
#include <cstring>
#include <climits>
#include <string_view>
#include "box_ptr.hh"
#include "repl-interacter.hh"
@@ -36,6 +29,7 @@
#include "local-fs-store.hh"
#include "signals.hh"
#include "print.hh"
#include "progress-bar.hh"
#include "gc-small-vector.hh"
#include "users.hh"
@@ -85,8 +79,6 @@ enum class ProcessLineResult {
PromptAgain,
};
using namespace std::literals::string_view_literals;
struct NixRepl
: AbstractNixRepl
, detail::ReplCompleterMixin
@@ -94,35 +86,6 @@ struct NixRepl
, gc
#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;
Strings loadedFiles;
@@ -159,7 +122,7 @@ struct NixRepl
void reloadFiles();
void addAttrsToScope(Value & attrs);
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 loadDebugTraceEnv(DebugTrace & dt);
@@ -246,7 +209,8 @@ NixRepl::NixRepl(const SearchPath & searchPath, nix::ref<Store> store, ref<EvalS
{
}
void runNix(Path program, const Strings & args)
void runNix(Path program, const Strings & args,
const std::optional<std::string> & input = {})
{
auto subprocessEnv = getEnv();
subprocessEnv["NIX_CONFIG"] = globalConfig.toKeyValue();
@@ -255,7 +219,8 @@ void runNix(Path program, const Strings & args)
.program = settings.nixBinDir+ "/" + program,
.args = args,
.environment = subprocessEnv,
}).wait();
.input = input,
});
return;
}
@@ -303,7 +268,7 @@ ReplExitStatus NixRepl::mainLoop()
/* Stop the progress bar because it interferes with the display of
the repl. */
logger->pause();
stopProgressBar();
std::string input;
@@ -358,36 +323,6 @@ StringSet NixRepl::completePrefix(const std::string & prefix)
{
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(){}[]");
std::string prev, cur;
if (start == std::string::npos) {
@@ -430,9 +365,9 @@ StringSet NixRepl::completePrefix(const std::string & prefix)
auto expr = cur.substr(0, dot);
auto cur2 = cur.substr(dot + 1);
Expr & e = parseString(expr);
Expr * e = parseString(expr);
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?)");
for (auto & i : *v.attrs) {
@@ -654,7 +589,7 @@ ProcessLineResult NixRepl::processLine(std::string line)
// runProgram redirects stdout to a StringSink,
// using runProgram2 to allow editors to display their UI
runProgram2(RunOptions { .program = editor, .searchPath = true, .args = args }).wait();
runProgram2(RunOptions { .program = editor, .searchPath = true, .args = args });
// Reload right after exiting the editor
state->resetFileCache();
@@ -687,10 +622,9 @@ ProcessLineResult NixRepl::processLine(std::string line)
// TODO: this only shows a progress bar for explicitly initiated builds,
// not eval-time fetching or builds performed for IFD.
// But we can't just show it everywhere, since that would erase partial output from evaluation.
logger->resetProgress();
logger->resume();
startProgressBar();
Finally stopLogger([&]() {
logger->pause();
stopProgressBar();
});
state->store->buildPaths({
@@ -824,7 +758,7 @@ ProcessLineResult NixRepl::processLine(std::string line)
line[p + 1] != '=' &&
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());
v.mkThunk(env, e);
addVarToScope(state->symbols.create(name), v);
@@ -949,7 +883,7 @@ Value * NixRepl::getReplOverlaysEvalFunction()
auto code =
#include "repl-overlays.nix.gen.hh"
;
auto & expr = state->parseExprFromString(
auto expr = state->parseExprFromString(
code,
SourcePath(evalReplInitFilesPath),
state->staticBaseEnv
@@ -1057,7 +991,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);
}
@@ -1065,16 +999,16 @@ Expr & NixRepl::parseString(std::string s)
void NixRepl::evalString(std::string s, Value & v)
{
Expr & e = parseString(s);
e.eval(*state, *env, v);
Expr * e = parseString(s);
e->eval(*state, *env, v);
state->forceValue(v, v.determinePos(noPos));
}
Value * NixRepl::evalFile(SourcePath & path)
{
auto & expr = state->parseExprFromFile(path, staticEnv);
auto expr = state->parseExprFromFile(path, staticEnv);
Value * result(state->allocValue());
expr.eval(*state, *env, *result);
expr->eval(*state, *env, *result);
state->forceValue(*result, result->determinePos(noPos));
return result;
}
-4
View File
@@ -1,7 +1,3 @@
// SPDX-FileCopyrightText: 2024 Nix and Lix Authors
//
// SPDX-License-Identifier: LGPL-2.1-only
#pragma once
///@file
-4
View File
@@ -1,7 +1,3 @@
// SPDX-FileCopyrightText: 2024 Nix and Lix Authors
//
// SPDX-License-Identifier: LGPL-2.1-only
#include "attr-path.hh"
#include "eval-inline.hh"
-4
View File
@@ -1,7 +1,3 @@
// SPDX-FileCopyrightText: 2024 Nix and Lix Authors
//
// SPDX-License-Identifier: LGPL-2.1-only
#pragma once
///@file
-4
View File
@@ -1,7 +1,3 @@
// SPDX-FileCopyrightText: 2024 Nix and Lix Authors
//
// SPDX-License-Identifier: LGPL-2.1-only
#include "attr-set.hh"
#include "eval-inline.hh"
-4
View File
@@ -1,7 +1,3 @@
// SPDX-FileCopyrightText: 2024 Nix and Lix Authors
//
// SPDX-License-Identifier: LGPL-2.1-only
#pragma once
///@file
-4
View File
@@ -1,7 +1,3 @@
// SPDX-FileCopyrightText: 2024 Nix and Lix Authors
//
// SPDX-License-Identifier: LGPL-2.1-only
#include "eval-cache.hh"
#include "sqlite.hh"
#include "eval.hh"
-4
View File
@@ -1,7 +1,3 @@
// SPDX-FileCopyrightText: 2024 Nix and Lix Authors
//
// SPDX-License-Identifier: LGPL-2.1-only
#pragma once
///@file
-4
View File
@@ -1,7 +1,3 @@
// SPDX-FileCopyrightText: 2024 Nix and Lix Authors
//
// SPDX-License-Identifier: LGPL-2.1-only
#include "eval-error.hh"
#include "eval.hh"
#include "value.hh"
+1 -9
View File
@@ -1,7 +1,3 @@
// SPDX-FileCopyrightText: 2024 Nix and Lix Authors
//
// SPDX-License-Identifier: LGPL-2.1-only
#pragma once
///@file
@@ -51,16 +47,12 @@ MakeError(MissingArgumentError, EvalError);
MakeError(RestrictedPathError, Error);
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
{
public:
Path path;
InvalidPathError(EvalState & state, const Path & path)
: EvalError(state, "path '%s' did not exist in the store during evaluation", path)
: EvalError(state, "path '%s' is not valid", path)
{
}
};
+2 -6
View File
@@ -1,7 +1,3 @@
// SPDX-FileCopyrightText: 2024 Nix and Lix Authors
//
// SPDX-License-Identifier: LGPL-2.1-only
#pragma once
///@file
@@ -90,11 +86,11 @@ void EvalState::forceValue(Value & v, const PosIdx pos)
{
if (v.isThunk()) {
Env * env = v.thunk.env;
Expr & expr = *v.thunk.expr;
Expr * expr = v.thunk.expr;
try {
v.mkBlackhole();
//checkInterrupt();
expr.eval(*this, *env, v);
expr->eval(*this, *env, v);
} catch (...) {
v.mkThunk(env, expr);
tryFixupBlackHolePos(v, pos);
+5 -7
View File
@@ -1,7 +1,3 @@
// SPDX-FileCopyrightText: 2024 Nix and Lix Authors
//
// SPDX-License-Identifier: LGPL-2.1-only
#include "file-system.hh"
#include "globals.hh"
#include "profiles.hh"
@@ -67,9 +63,11 @@ Strings EvalSettings::getDefaultNixPath()
}
};
add(getNixDefExpr() + "/channels");
add(rootChannelsDir() + "/nixpkgs", "nixpkgs");
add(rootChannelsDir());
if (!evalSettings.restrictEval && !evalSettings.pureEval) {
add(getNixDefExpr() + "/channels");
add(rootChannelsDir() + "/nixpkgs", "nixpkgs");
add(rootChannelsDir());
}
return res;
}
+4 -30
View File
@@ -1,7 +1,3 @@
// SPDX-FileCopyrightText: 2024 Nix and Lix Authors
//
// SPDX-License-Identifier: LGPL-2.1-only
#pragma once
///@file
#include "config.hh"
@@ -19,21 +15,9 @@ struct EvalSettings : Config
static std::string resolvePseudoUrl(std::string_view url);
Setting<bool> enableNativeCode{this, false, "allow-unsafe-native-code-during-evaluation", R"(
Enable built-in functions that allow executing native code.
Whether builtin functions that allow executing native code should be enabled.
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.
In particular, this adds the `importNative` and `exec` builtins.
)"};
Setting<Strings> nixPath{
@@ -79,17 +63,8 @@ struct EvalSettings : Config
R"(
Pure evaluation mode ensures that the result of Nix expressions is fully determined by explicitly declared inputs, and not influenced by external state:
- File system and network access is restricted to accesses to immutable data only:
- 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>)
- Restrict file system and network access to files specified by cryptographic hash
- Disable [`builtins.currentSystem`](@docroot@/language/builtin-constants.md#builtins-currentSystem) and [`builtins.currentTime`](@docroot@/language/builtin-constants.md#builtins-currentTime)
)"
};
@@ -111,7 +86,6 @@ struct EvalSettings : Config
allowed to access `https://github.com/NixOS/patchelf.git`.
)"};
Setting<bool> traceFunctionCalls{this, false, "trace-function-calls",
R"(
If set to `true`, the Nix evaluator will trace every function call.
+129 -251
View File
@@ -1,14 +1,9 @@
// SPDX-FileCopyrightText: 2024 Nix and Lix Authors
//
// SPDX-License-Identifier: LGPL-2.1-only
#include "eval.hh"
#include "eval-settings.hh"
#include "hash.hh"
#include "primops.hh"
#include "print-options.hh"
#include "shared.hh"
#include "suggestions.hh"
#include "types.hh"
#include "store-api.hh"
#include "derivations.hh"
@@ -22,6 +17,7 @@
#include "gc-small-vector.hh"
#include "fetch-to-store.hh"
#include "flake/flakeref.hh"
#include "parser-tab.hh"
#include <algorithm>
#include <chrono>
@@ -422,7 +418,7 @@ EvalState::EvalState(
}
if (evalSettings.restrictEval || evalSettings.pureEval) {
allowedPaths = std::optional(PathSet());
allowedPaths = PathSet();
for (auto & i : searchPath.elements) {
auto r = resolveSearchPathPath(i.path);
@@ -954,14 +950,14 @@ void EvalState::mkList(Value & v, size_t size)
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);
nrThunks++;
}
void EvalState::mkThunk_(Value & v, Expr & expr)
void EvalState::mkThunk_(Value & v, Expr * expr)
{
mkThunk(v, baseEnv, expr);
}
@@ -1062,7 +1058,7 @@ void EvalState::mkSingleDerivedPathString(
Value * Expr::maybeThunk(EvalState & state, Env & env)
{
Value * v = state.allocValue();
mkThunk(*v, env, *this);
mkThunk(*v, env, this);
return v;
}
@@ -1126,7 +1122,7 @@ void EvalState::evalFile(const SourcePath & path_, Value & v, bool mustBeTrivial
e = j->second;
if (!e)
e = &parseExprFromFile(checkSourcePath(resolvedPath));
e = parseExprFromFile(checkSourcePath(resolvedPath));
cacheFile(path, resolvedPath, e, v, mustBeTrivial);
}
@@ -1163,7 +1159,7 @@ void EvalState::cacheFile(
if (mustBeTrivial &&
!(dynamic_cast<ExprAttrs *>(e)))
error<EvalError>("file '%s' must be an attribute set", path).debugThrow();
eval(*e, v);
eval(e, v);
} catch (Error & e) {
addErrorTrace(e, "while evaluating the file '%1%':", resolvedPath.to_string());
throw;
@@ -1174,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 {
Value v;
e.eval(*this, env, v);
e->eval(*this, env, v);
if (v.type() != nBool)
error<TypeError>(
"expected a Boolean but found %1%: %2%",
showType(v),
ValuePrinter(*this, v, errorPrintOptions)
).atPos(pos).withFrame(env, e).debugThrow();
).atPos(pos).withFrame(env, *e).debugThrow();
return v.boolean;
} catch (Error & e) {
e.addTrace(positions[pos], errorCtx);
@@ -1199,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 {
e.eval(*this, env, v);
e->eval(*this, env, v);
if (v.type() != nAttrs)
error<TypeError>(
"expected a set but found %1%: %2%",
showType(v),
ValuePrinter(*this, v, errorPrintOptions)
).withFrame(env, e).debugThrow();
).withFrame(env, *e).debugThrow();
} catch (Error & e) {
e.addTrace(positions[pos], errorCtx);
throw;
@@ -1251,7 +1247,7 @@ Env * ExprAttrs::buildInheritFromEnv(EvalState & state, Env & up)
inheritEnv.up = &up;
Displacement displ = 0;
for (auto & from : *inheritFromExprs)
for (auto from : *inheritFromExprs)
inheritEnv.values[displ++] = from->maybeThunk(state, up);
return &inheritEnv;
@@ -1281,7 +1277,7 @@ void ExprAttrs::eval(EvalState & state, Env & env, Value & v)
Value * vAttr;
if (hasOverrides && i.second.kind != AttrDef::Kind::Inherited) {
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
vAttr = i.second.e->maybeThunk(state, *i.second.chooseByKind(&env2, &env, inheritEnv));
env2.values[displ++] = vAttr;
@@ -1430,26 +1426,11 @@ static std::string showAttrPath(EvalState & state, Env & env, const AttrPath & a
void ExprSelect::eval(EvalState & state, Env & env, Value & v)
{
Value vFirst;
Value vTmp;
PosIdx pos2;
Value * vAttrs = &vTmp;
// Pointer to the current attrset Value in this select chain.
Value * vCurrent = &vFirst;
// Position for the current attrset Value in this select chain.
PosIdx posCurrent;
try {
e->eval(state, env, vFirst);
} catch (Error & e) {
assert(this->e != nullptr);
state.addErrorTrace(
e,
getPos(),
"while evaluating '%s' to select '%s' on it",
ExprPrinter(state, *this->e),
showAttrPath(state.symbols, this->attrPath)
);
throw;
}
e->eval(state, env, vTmp);
try {
auto dts = state.debugRepl
@@ -1462,112 +1443,48 @@ void ExprSelect::eval(EvalState & state, Env & env, Value & v)
showAttrPath(state, env, attrPath))
: nullptr;
for (auto const & [partIdx, currentAttrName] : enumerate(attrPath)) {
for (auto & i : attrPath) {
state.nrLookups++;
Symbol const name = getName(currentAttrName, state, env);
// For formatting errors, which should be done only when needed.
auto partsSoFar = [&]() -> std::string {
std::stringstream ss;
// We start with the base thing this ExprSelect is selecting on.
assert(this->e != nullptr);
this->e->show(state.symbols, ss);
// Then grab each part of the attr path up to this one.
assert(partIdx < attrPath.size());
std::span<AttrName> const parts(
attrPath.begin(),
attrPath.begin() + partIdx
);
// And convert them to strings and join them.
for (auto const & part : parts) {
auto const partName = getName(part, state, env);
ss << "." << state.symbols[partName];
}
return ss.str();
};
try {
state.forceValue(*vCurrent, pos);
} catch (Error & e) {
state.addErrorTrace(
e,
getPos(),
"while evaluating '%s' to select '%s' on it",
partsSoFar(),
state.symbols[name]
);
throw;
}
if (vCurrent->type() != nAttrs) {
// If we have an `or` provided default,
// then this is allowed to not be an attrset.
if (def != nullptr) {
this->def->eval(state, env, v);
Bindings::iterator j;
auto name = getName(i, state, env);
if (def) {
state.forceValue(*vAttrs, pos);
if (vAttrs->type() != nAttrs ||
(j = vAttrs->attrs->find(name)) == vAttrs->attrs->end())
{
def->eval(state, env, v);
return;
}
// Otherwise, we must type error.
state.error<TypeError>(
"expected a set but found %s: %s",
showType(*vCurrent),
ValuePrinter(state, *vCurrent, errorPrintOptions)
).addTrace(
pos,
HintFmt("while selecting '%s' on '%s'", state.symbols[name], partsSoFar())
).debugThrow();
}
// Now that we know this is actually an attrset, try to find an attr
// with the selected name.
Bindings::iterator attrIt = vCurrent->attrs->find(name);
if (attrIt == vCurrent->attrs->end()) {
// If we have an `or` provided default, then we'll use that.
if (def != nullptr) {
this->def->eval(state, env, v);
return;
} else {
state.forceAttrs(*vAttrs, pos, "while selecting an attribute");
if ((j = vAttrs->attrs->find(name)) == vAttrs->attrs->end()) {
std::set<std::string> allAttrNames;
for (auto & attr : *vAttrs->attrs)
allAttrNames.insert(state.symbols[attr.name]);
auto suggestions = Suggestions::bestMatches(allAttrNames, state.symbols[name]);
state.error<EvalError>("attribute '%1%' missing", state.symbols[name])
.atPos(pos).withSuggestions(suggestions).withFrame(env, *this).debugThrow();
}
// Otherwise, missing attr error.
std::set<std::string> allAttrNames;
for (auto const & attr : *vCurrent->attrs) {
allAttrNames.insert(state.symbols[attr.name]);
}
auto suggestions = Suggestions::bestMatches(allAttrNames, state.symbols[name]);
state.error<EvalError>("attribute '%s' missing", state.symbols[name])
.atPos(pos)
.withSuggestions(suggestions)
.withFrame(env, *this)
.debugThrow();
}
// If we're here, then we successfully found the attribute.
// Set our currently operated-on attrset to this one, and keep going.
vCurrent = attrIt->value;
posCurrent = attrIt->pos;
if (state.countCalls) state.attrSelects[posCurrent]++;
vAttrs = j->value;
pos2 = j->pos;
if (state.countCalls) state.attrSelects[pos2]++;
}
state.forceValue(*vCurrent, (posCurrent ? posCurrent : this->pos));
state.forceValue(*vAttrs, (pos2 ? pos2 : this->pos ) );
} catch (Error & e) {
if (posCurrent) {
auto pos2r = state.positions[posCurrent];
if (pos2) {
auto pos2r = state.positions[pos2];
auto origin = std::get_if<SourcePath>(&pos2r.origin);
if (!(origin && *origin == state.derivationInternal))
state.addErrorTrace(e, posCurrent, "while evaluating the attribute '%1%'",
state.addErrorTrace(e, pos2, "while evaluating the attribute '%1%'",
showAttrPath(state, env, attrPath));
}
throw;
}
v = *vCurrent;
v = *vAttrs;
}
@@ -1616,66 +1533,6 @@ 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)
{
if (callDepth > evalSettings.maxCallDepth)
@@ -1729,42 +1586,53 @@ void EvalState::callFunction(Value & fun, size_t nrArgs, Value * * args, Value &
if (lambda.arg)
env2.values[displ++] = args[0];
///* For each formal argument, get the actual argument. If
// there is no matching actual argument but the formal
// argument has a default, use the default. */
auto const formalsMatch = matchupFormals(
*this,
env2,
displ,
lambda,
*args[0]->attrs
);
for (auto const & missingArg : formalsMatch.missing) {
auto const missing = symbols[missingArg];
error<TypeError>("function '%s' called without required argument '%s'", lambda.getName(symbols), missing)
.atPos(lambda.pos)
.withTrace(pos, "from call site")
.withFrame(*fun.lambda.env, lambda)
.debugThrow();
}
for (auto const & unexpectedArg : formalsMatch.unexpected) {
auto const unex = symbols[unexpectedArg];
std::set<std::string> formalNames;
for (auto const & formal : lambda.formals->formals) {
formalNames.insert(symbols[formal.name]);
/* For each formal argument, get the actual argument. If
there is no matching actual argument but the formal
argument has a default, use the default. */
size_t attrsUsed = 0;
for (auto & i : lambda.formals->formals) {
auto j = args[0]->attrs->get(i.name);
if (!j) {
if (!i.def) {
error<TypeError>("function '%1%' called without required argument '%2%'",
(lambda.name ? std::string(symbols[lambda.name]) : "anonymous lambda"),
symbols[i.name])
.atPos(lambda.pos)
.withTrace(pos, "from call site")
.withFrame(*fun.lambda.env, lambda)
.debugThrow();
}
env2.values[displ++] = i.def->maybeThunk(*this, env2);
} else {
attrsUsed++;
env2.values[displ++] = j->value;
}
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++;
if (countCalls) incrFunctionCall(&lambda);
@@ -1774,7 +1642,9 @@ void EvalState::callFunction(Value & fun, size_t nrArgs, Value * * args, Value &
? makeDebugTraceStacker(
*this, *lambda.body, env2, positions[lambda.pos],
"while calling %s",
lambda.getQuotedName(symbols))
lambda.name
? concatStrings("'", symbols[lambda.name], "'")
: "anonymous lambda")
: nullptr;
lambda.body->eval(*this, env2, vCur);
@@ -1784,7 +1654,9 @@ void EvalState::callFunction(Value & fun, size_t nrArgs, Value * * args, Value &
e,
lambda.pos,
"while calling %s",
lambda.getQuotedName(symbols));
lambda.name
? concatStrings("'", symbols[lambda.name], "'")
: "anonymous lambda");
if (pos) addErrorTrace(e, pos, "from call site");
}
throw;
@@ -1811,15 +1683,6 @@ void EvalState::callFunction(Value & fun, size_t nrArgs, Value * * args, Value &
try {
fn->fun(*this, vCur.determinePos(noPos), args, vCur);
} catch (ThrownError & e) {
// Distinguish between an error that simply happened while "throw"
// was being evaluated and an explicit thrown error.
if (fn->name == "throw") {
addErrorTrace(e, pos, "caused by explicit %s", "throw");
} else {
addErrorTrace(e, pos, "while calling the '%s' builtin", fn->name);
}
throw;
} catch (Error & e) {
addErrorTrace(e, pos, "while calling the '%1%' builtin", fn->name);
throw;
@@ -2009,13 +1872,13 @@ void ExprWith::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.
(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)
{
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;
cond->show(state.symbols, out);
state.error<AssertionError>("assertion '%1%' failed", out.str()).atPos(pos).withFrame(env, *this).debugThrow();
@@ -2026,7 +1889,7 @@ void ExprAssert::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: !
}
@@ -2048,27 +1911,27 @@ void ExprOpNEq::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)
{
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)
{
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)
{
Value v1, v2;
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, 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.nrOpUpdates++;
@@ -2172,10 +2035,10 @@ void ExprConcatStrings::eval(EvalState & state, Env & env, Value & v)
};
// 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();
for (auto & [i_pos, i] : es) {
for (auto & [i_pos, i] : *es) {
Value & vTmp = *vTmpP++;
i->eval(state, env, vTmp);
@@ -2205,7 +2068,7 @@ void ExprConcatStrings::eval(EvalState & state, Env & env, Value & v)
} else
state.error<EvalError>("cannot add %1% to a float", showType(vTmp)).atPos(i_pos).withFrame(env, *this).debugThrow();
} 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
canonized in the first place if it's coming from a ./${foo} type
path */
@@ -2889,39 +2752,39 @@ SourcePath resolveExprPath(SourcePath path)
}
Expr & EvalState::parseExprFromFile(const SourcePath & path)
Expr * EvalState::parseExprFromFile(const SourcePath & path)
{
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();
// readFile hopefully have left some extra space for terminators
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
// into their respective Pos::Origin until the parser stops overwriting its input
// data.
auto s = make_ref<std::string>(s_);
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);
}
Expr & EvalState::parseStdin()
Expr * EvalState::parseStdin()
{
// NOTE this method (and parseExprFromString) must take care to *fully copy* their
// input into their respective Pos::Origin until the parser stops overwriting its
@@ -2931,7 +2794,7 @@ Expr & EvalState::parseStdin()
// drainFD should have left some extra space for terminators
auto s = make_ref<std::string>(buffer);
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);
}
@@ -3020,6 +2883,21 @@ std::optional<std::string> EvalState::resolveSearchPathPath(const SearchPath::Pa
}
Expr * EvalState::parse(
char * text,
size_t length,
Pos::Origin origin,
const SourcePath & basePath,
std::shared_ptr<StaticEnv> & staticEnv)
{
auto result = parseExprFromBuf(text, length, origin, basePath, symbols, positions, exprSymbols);
result->bindVars(*this, staticEnv);
return result;
}
std::string ExternalValueBase::coerceToString(EvalState & state, const PosIdx & pos, NixStringContext & context, bool copyMore, bool copyToStore) const
{
state.error<TypeError>(
+34 -49
View File
@@ -1,7 +1,3 @@
// SPDX-FileCopyrightText: 2024 Nix and Lix Authors
//
// SPDX-License-Identifier: LGPL-2.1-only
#pragma once
///@file
@@ -13,13 +9,14 @@
#include "symbol-table.hh"
#include "config.hh"
#include "experimental-features.hh"
#include "input-accessor.hh"
#include "search-path.hh"
#include "repl-exit-status.hh"
#include <gc/gc_allocator.h>
#include <map>
#include <optional>
#include <unordered_map>
#include <mutex>
#include <functional>
namespace nix {
@@ -36,31 +33,12 @@ class EvalState;
class StorePath;
struct SingleDerivedPath;
enum RepairFlag : bool;
struct MemoryInputAccessor;
namespace eval_cache {
class EvalCache;
}
/** Alias for std::map which uses boehmgc's allocator conditional on us actually
* using boehmgc in this build.
*/
#if HAVE_BOEHMGC
template<typename KeyT, typename ValueT>
using GcMap = std::map<
KeyT,
ValueT,
std::less<KeyT>,
traceable_allocator<std::pair<KeyT const, ValueT>>
>;
#else
using GcMap = std::map<KeyT, ValueT>
#endif
/**
* Function that implements a primop.
*/
using PrimOpImpl = void(EvalState & state, PosIdx pos, Value ** args, Value & v);
typedef void (* PrimOpFun) (EvalState & state, const PosIdx pos, Value * * args, Value & v);
/**
* Info about a primitive operation, and its implementation
@@ -94,7 +72,7 @@ struct PrimOp
/**
* Implementation of the primop.
*/
std::function<PrimOpImpl> fun;
std::function<std::remove_pointer<PrimOpFun>::type> fun;
/**
* Optional experimental for this to be gated on.
@@ -133,7 +111,11 @@ struct Constant
bool impureOnly = false;
};
using ValMap = GcMap<std::string, Value *>;
#if HAVE_BOEHMGC
typedef std::map<std::string, Value *, std::less<std::string>, traceable_allocator<std::pair<const std::string, Value *> > > ValMap;
#else
typedef std::map<std::string, Value *> ValMap;
#endif
struct Env
{
@@ -228,7 +210,7 @@ public:
/**
* Debugger
*/
std::function<ReplExitStatus(ref<EvalState> es, ValMap const & extraEnv)> debugRepl;
ReplExitStatus (* debugRepl)(ref<EvalState> es, const ValMap & extraEnv);
bool debugStop;
bool inDebugger = false;
int trylevel;
@@ -252,11 +234,6 @@ public:
return *new EvalErrorBuilder<T>(*this, args...);
}
/**
* A cache for evaluation caches, so as to reuse the same root value if possible
*/
std::map<const Hash, ref<eval_cache::EvalCache>> evalCaches;
private:
/* Cache for calls to addToStore(); maps source paths to the store
@@ -266,13 +243,21 @@ private:
/**
* A cache from path names to parse trees.
*/
using FileParseCache = GcMap<SourcePath, Expr *>;
#if HAVE_BOEHMGC
typedef std::map<SourcePath, Expr *, std::less<SourcePath>, traceable_allocator<std::pair<const SourcePath, Expr *>>> FileParseCache;
#else
typedef std::map<SourcePath, Expr *> FileParseCache;
#endif
FileParseCache fileParseCache;
/**
* A cache from path names to values.
*/
using FileEvalCache = GcMap<SourcePath, Value>;
#if HAVE_BOEHMGC
typedef std::map<SourcePath, Value, std::less<SourcePath>, traceable_allocator<std::pair<const SourcePath, Value>>> FileEvalCache;
#else
typedef std::map<SourcePath, Value> FileEvalCache;
#endif
FileEvalCache fileEvalCache;
SearchPath searchPath;
@@ -355,16 +340,16 @@ public:
/**
* Parse a Nix expression from the specified file.
*/
Expr & parseExprFromFile(const SourcePath & path);
Expr & parseExprFromFile(const SourcePath & path, std::shared_ptr<StaticEnv> & staticEnv);
Expr * parseExprFromFile(const SourcePath & path);
Expr * parseExprFromFile(const SourcePath & path, std::shared_ptr<StaticEnv> & staticEnv);
/**
* 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);
Expr * parseExprFromString(std::string s, const SourcePath & basePath, std::shared_ptr<StaticEnv> & staticEnv);
Expr * parseExprFromString(std::string s, const SourcePath & basePath);
Expr & parseStdin();
Expr * parseStdin();
/**
* Evaluate an expression read from the given file to normal
@@ -405,15 +390,15 @@ public:
*
* @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
* type.
*/
inline bool evalBool(Env & env, Expr & e);
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 bool evalBool(Env & env, Expr * e);
inline bool evalBool(Env & env, Expr * e, const PosIdx pos, std::string_view errorCtx);
inline void evalAttrs(Env & env, Expr * e, Value & v, const PosIdx pos, std::string_view errorCtx);
/**
* If `v` is a thunk, enter it and overwrite `v` with the result
@@ -634,7 +619,7 @@ public:
}
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);
/**
@@ -743,15 +728,15 @@ private:
bool countCalls;
using PrimOpCalls = std::map<std::string, size_t>;
typedef std::map<std::string, size_t> PrimOpCalls;
PrimOpCalls primOpCalls;
using FunctionCalls = std::map<ExprLambda *, size_t>;
typedef std::map<ExprLambda *, size_t> FunctionCalls;
FunctionCalls functionCalls;
void incrFunctionCall(ExprLambda * fun);
using AttrSelects = std::map<PosIdx, size_t>;
typedef std::map<PosIdx, size_t> AttrSelects;
AttrSelects attrSelects;
friend struct ExprOpUpdate;
@@ -793,7 +778,7 @@ std::string showType(const Value & v);
*/
SourcePath resolveExprPath(SourcePath path);
static constexpr std::string_view corepkgsPrefix{"/__corepkgs__/"};
static const std::string corepkgsPrefix{"/__corepkgs__/"};
}
-4
View File
@@ -1,7 +1,3 @@
# SPDX-FileCopyrightText: 2024 Nix and Lix Authors
#
# SPDX-License-Identifier: LGPL-2.1-only
{
system ? "", # obsolete
url,
-4
View File
@@ -1,7 +1,3 @@
# SPDX-FileCopyrightText: 2024 Nix and Lix Authors
#
# SPDX-License-Identifier: LGPL-2.1-only
lockFileStr: rootSrc: rootSubdir:
let
+19 -39
View File
@@ -1,7 +1,3 @@
// SPDX-FileCopyrightText: 2024 Nix and Lix Authors
//
// SPDX-License-Identifier: LGPL-2.1-only
#include "flake.hh"
#include "logging.hh"
#include "users.hh"
@@ -55,46 +51,30 @@ void ConfigFile::apply()
else
assert(false);
bool trusted = whitelist.count(baseName);
if (!trusted) {
switch (nix::fetchSettings.acceptFlakeConfig) {
case AcceptFlakeConfig::True: {
trusted = true;
break;
}
case AcceptFlakeConfig::Ask: {
auto trustedList = readTrustedList();
auto tlname = get(trustedList, name);
if (auto saved = tlname ? get(*tlname, valueS) : nullptr) {
trusted = *saved;
printInfo("Using saved setting for '%s = %s' from ~/.local/share/nix/trusted-settings.json.", name, valueS);
} else {
// FIXME: filter ANSI escapes, newlines, \r, etc.
if (std::tolower(logger->ask(fmt("Do you want to allow configuration setting '%s' to be set to '" ANSI_RED "%s" ANSI_NORMAL "' (y/N)? This may allow the flake to gain root, see the nix.conf manual page.", name, valueS)).value_or('n')) == 'y') {
trusted = true;
} else {
warn("you can set '%s' to '%b' to automatically reject configuration options supplied by flakes", "accept-flake-config", false);
}
if (std::tolower(logger->ask(fmt("do you want to permanently mark this value as %s (y/N)?", trusted ? "trusted": "untrusted" )).value_or('n')) == 'y') {
trustedList[name][valueS] = trusted;
writeTrustedList(trustedList);
}
if (!whitelist.count(baseName) && !nix::fetchSettings.acceptFlakeConfig) {
bool trusted = false;
auto trustedList = readTrustedList();
auto tlname = get(trustedList, name);
if (auto saved = tlname ? get(*tlname, valueS) : nullptr) {
trusted = *saved;
printInfo("Using saved setting for '%s = %s' from ~/.local/share/nix/trusted-settings.json.", name, valueS);
} else {
// FIXME: filter ANSI escapes, newlines, \r, etc.
if (std::tolower(logger->ask(fmt("do you want to allow configuration setting '%s' to be set to '" ANSI_RED "%s" ANSI_NORMAL "' (y/N)?", name, valueS)).value_or('n')) == 'y') {
trusted = true;
}
if (std::tolower(logger->ask(fmt("do you want to permanently mark this value as %s (y/N)?", trusted ? "trusted": "untrusted" )).value_or('n')) == 'y') {
trustedList[name][valueS] = trusted;
writeTrustedList(trustedList);
}
break;
}
case nix::AcceptFlakeConfig::False: {
trusted = false;
break;
};
if (!trusted) {
warn("ignoring untrusted flake configuration setting '%s'.\nPass '%s' to trust it", name, "--accept-flake-config");
continue;
}
}
if (trusted) {
debug("accepting trusted flake configuration setting '%s'", name);
globalConfig.set(name, valueS);
} else {
warn("ignoring untrusted flake configuration setting '%s', pass '%s' to trust it (may allow the flake to gain root, see the nix.conf manual page)", name, "--accept-flake-config");
}
globalConfig.set(name, valueS);
}
}
-4
View File
@@ -1,7 +1,3 @@
// SPDX-FileCopyrightText: 2024 Nix and Lix Authors
//
// SPDX-License-Identifier: LGPL-2.1-only
#include "flake.hh"
#include "eval.hh"
#include "eval-settings.hh"
-4
View File
@@ -1,7 +1,3 @@
// SPDX-FileCopyrightText: 2024 Nix and Lix Authors
//
// SPDX-License-Identifier: LGPL-2.1-only
#pragma once
///@file
+3 -7
View File
@@ -1,7 +1,3 @@
// SPDX-FileCopyrightText: 2024 Nix and Lix Authors
//
// SPDX-License-Identifier: LGPL-2.1-only
#include "flakeref.hh"
#include "store-api.hh"
#include "url.hh"
@@ -89,8 +85,8 @@ std::pair<FlakeRef, std::string> parseFlakeRefWithFragment(
+ "(?:#(" + queryRegex + "))?",
std::regex::ECMAScript);
static std::regex flakeShorthandRegex(
flakeShorthandRegexS
static std::regex flakeRegex(
"((" + flakeIdRegexS + ")(?:/(?:" + refAndOrRevRegex + "))?)"
+ "(?:#(" + queryRegex + "))?",
std::regex::ECMAScript);
@@ -99,7 +95,7 @@ std::pair<FlakeRef, std::string> parseFlakeRefWithFragment(
/* Check if 'url' is a flake ID. This is an abbreviated syntax for
'flake:<flake-id>?ref=<ref>&rev=<rev>'. */
if (std::regex_match(url, match, flakeShorthandRegex)) {
if (std::regex_match(url, match, flakeRegex)) {
auto parsedURL = ParsedURL{
.url = url,
.base = "flake:" + match.str(1),
-4
View File
@@ -1,7 +1,3 @@
// SPDX-FileCopyrightText: 2024 Nix and Lix Authors
//
// SPDX-License-Identifier: LGPL-2.1-only
#pragma once
///@file
-4
View File
@@ -1,7 +1,3 @@
// SPDX-FileCopyrightText: 2024 Nix and Lix Authors
//
// SPDX-License-Identifier: LGPL-2.1-only
#include "lockfile.hh"
#include "store-api.hh"
#include "url-parts.hh"
-4
View File
@@ -1,7 +1,3 @@
// SPDX-FileCopyrightText: 2024 Nix and Lix Authors
//
// SPDX-License-Identifier: LGPL-2.1-only
#pragma once
///@file
-4
View File
@@ -1,7 +1,3 @@
# SPDX-FileCopyrightText: 2024 Nix and Lix Authors
#
# SPDX-License-Identifier: LGPL-2.1-only
libexpr_generated_headers += custom_target(
command : [ 'bash', '-c', 'echo \'R"__NIX_STR(\' | cat - @INPUT@ && echo \')__NIX_STR"\'' ],
input : 'call-flake.nix',
-4
View File
@@ -1,7 +1,3 @@
// SPDX-FileCopyrightText: 2024 Nix and Lix Authors
//
// SPDX-License-Identifier: LGPL-2.1-only
#include "function-trace.hh"
#include "logging.hh"
-4
View File
@@ -1,7 +1,3 @@
// SPDX-FileCopyrightText: 2024 Nix and Lix Authors
//
// SPDX-License-Identifier: LGPL-2.1-only
#pragma once
///@file
-4
View File
@@ -1,7 +1,3 @@
// SPDX-FileCopyrightText: 2024 Nix and Lix Authors
//
// SPDX-License-Identifier: LGPL-2.1-only
#pragma once
///@file
+6 -10
View File
@@ -1,7 +1,3 @@
// SPDX-FileCopyrightText: 2024 Nix and Lix Authors
//
// SPDX-License-Identifier: LGPL-2.1-only
#include "get-drvs.hh"
#include "eval-inline.hh"
#include "derivations.hh"
@@ -49,7 +45,7 @@ DrvInfo::DrvInfo(EvalState & state, ref<Store> store, const std::string & drvPat
}
std::string DrvInfo::queryName()
std::string DrvInfo::queryName() const
{
if (name == "" && attrs) {
auto i = attrs->find(state->sName);
@@ -60,7 +56,7 @@ std::string DrvInfo::queryName()
}
std::string DrvInfo::querySystem()
std::string DrvInfo::querySystem() const
{
if (system == "" && attrs) {
auto i = attrs->find(state->sSystem);
@@ -70,7 +66,7 @@ std::string DrvInfo::querySystem()
}
std::optional<StorePath> DrvInfo::queryDrvPath()
std::optional<StorePath> DrvInfo::queryDrvPath() const
{
if (!drvPath && attrs) {
Bindings::iterator i = attrs->find(state->sDrvPath);
@@ -84,7 +80,7 @@ std::optional<StorePath> DrvInfo::queryDrvPath()
}
StorePath DrvInfo::requireDrvPath()
StorePath DrvInfo::requireDrvPath() const
{
if (auto drvPath = queryDrvPath())
return *drvPath;
@@ -92,7 +88,7 @@ StorePath DrvInfo::requireDrvPath()
}
StorePath DrvInfo::queryOutPath()
StorePath DrvInfo::queryOutPath() const
{
if (!outPath && attrs) {
Bindings::iterator i = attrs->find(state->sOutPath);
@@ -168,7 +164,7 @@ DrvInfo::Outputs DrvInfo::queryOutputs(bool withPaths, bool onlyOutputsToInstall
}
std::string DrvInfo::queryOutputName()
std::string DrvInfo::queryOutputName() const
{
if (outputName == "" && attrs) {
Bindings::iterator i = attrs->find(state->sOutputName);
+11 -15
View File
@@ -1,7 +1,3 @@
// SPDX-FileCopyrightText: 2024 Nix and Lix Authors
//
// SPDX-License-Identifier: LGPL-2.1-only
#pragma once
///@file
@@ -23,11 +19,11 @@ public:
private:
EvalState * state;
std::string name;
std::string system;
std::optional<std::optional<StorePath>> drvPath;
std::optional<StorePath> outPath;
std::string outputName;
mutable std::string name;
mutable std::string system;
mutable std::optional<std::optional<StorePath>> drvPath;
mutable std::optional<StorePath> outPath;
mutable std::string outputName;
Outputs outputs;
/**
@@ -51,12 +47,12 @@ public:
DrvInfo(EvalState & state, std::string attrPath, Bindings * attrs);
DrvInfo(EvalState & state, ref<Store> store, const std::string & drvPathWithOutputs);
std::string queryName();
std::string querySystem();
std::optional<StorePath> queryDrvPath();
StorePath requireDrvPath();
StorePath queryOutPath();
std::string queryOutputName();
std::string queryName() const;
std::string querySystem() const;
std::optional<StorePath> queryDrvPath() const;
StorePath requireDrvPath() const;
StorePath queryOutPath() const;
std::string queryOutputName() const;
/**
* Return the unordered map of output names to (optional) output paths.
* The "outputs to install" are determined by `meta.outputsToInstall`.
@@ -1,7 +1,3 @@
# SPDX-FileCopyrightText: 2024 Nix and Lix Authors
#
# SPDX-License-Identifier: LGPL-2.1-only
attrs@{
drvPath,
outputs,
-4
View File
@@ -1,7 +1,3 @@
// SPDX-FileCopyrightText: 2024 Nix and Lix Authors
//
// SPDX-License-Identifier: LGPL-2.1-only
#include "json-to-value.hh"
#include "value.hh"
#include "eval.hh"
-4
View File
@@ -1,7 +1,3 @@
// SPDX-FileCopyrightText: 2024 Nix and Lix Authors
//
// SPDX-License-Identifier: LGPL-2.1-only
#pragma once
///@file
+296
View File
@@ -0,0 +1,296 @@
%option reentrant bison-bridge bison-locations
%option align
%option noyywrap
%option never-interactive
%option stack
%option nodefault
%option nounput noyy_top_state
%s DEFAULT
%x STRING
%x IND_STRING
%x INPATH
%x INPATH_SLASH
%x PATH_START
%{
#ifdef __clang__
#pragma clang diagnostic ignored "-Wunneeded-internal-declaration"
#endif
// yacc generates code that uses unannotated fallthrough.
#pragma GCC diagnostic ignored "-Wimplicit-fallthrough"
#ifdef __clang__
#pragma clang diagnostic ignored "-Wimplicit-fallthrough"
#endif
#include "nixexpr.hh"
#include "parser-tab.hh"
#include "strings.hh"
using namespace nix;
namespace nix {
#define CUR_POS state->at(*yylloc)
static void initLoc(YYLTYPE * loc)
{
loc->first_line = loc->last_line = 0;
loc->first_column = loc->last_column = 0;
}
static void adjustLoc(YYLTYPE * loc, const char * s, size_t len)
{
loc->stash();
loc->first_column = loc->last_column;
loc->last_column += len;
}
// we make use of the fact that the parser receives a private copy of the input
// string and can munge around in it.
static StringToken unescapeStr(SymbolTable & symbols, char * s, size_t length)
{
char * result = s;
char * t = s;
char c;
// the input string is terminated with *two* NULs, so we can safely take
// *one* character after the one being checked against.
while ((c = *s++)) {
if (c == '\\') {
c = *s++;
if (c == 'n') *t = '\n';
else if (c == 'r') *t = '\r';
else if (c == 't') *t = '\t';
else *t = c;
}
else if (c == '\r') {
/* Normalise CR and CR/LF into LF. */
*t = '\n';
if (*s == '\n') s++; /* cr/lf */
}
else *t = c;
t++;
}
return {result, size_t(t - result)};
}
}
#define YY_USER_INIT initLoc(yylloc)
#define YY_USER_ACTION adjustLoc(yylloc, yytext, yyleng);
#define PUSH_STATE(state) yy_push_state(state, yyscanner)
#define POP_STATE() yy_pop_state(yyscanner)
%}
ANY .|\n
ID [a-zA-Z\_][a-zA-Z0-9\_\'\-]*
INT [0-9]+
FLOAT (([1-9][0-9]*\.[0-9]*)|(0?\.[0-9]+))([Ee][+-]?[0-9]+)?
PATH_CHAR [a-zA-Z0-9\.\_\-\+]
PATH {PATH_CHAR}*(\/{PATH_CHAR}+)+\/?
PATH_SEG {PATH_CHAR}*\/
HPATH \~(\/{PATH_CHAR}+)+\/?
HPATH_START \~\/
SPATH \<{PATH_CHAR}+(\/{PATH_CHAR}+)*\>
URI [a-zA-Z][a-zA-Z0-9\+\-\.]*\:[a-zA-Z0-9\%\/\?\:\@\&\=\+\$\,\-\_\.\!\~\*\']+
%%
if { return IF; }
then { return THEN; }
else { return ELSE; }
assert { return ASSERT; }
with { return WITH; }
let { return LET; }
in { return IN; }
rec { return REC; }
inherit { return INHERIT; }
or { return OR_KW; }
\.\.\. { return ELLIPSIS; }
\=\= { return EQ; }
\!\= { return NEQ; }
\<\= { return LEQ; }
\>\= { return GEQ; }
\&\& { return AND; }
\|\| { return OR; }
\-\> { return IMPL; }
\/\/ { return UPDATE; }
\+\+ { return CONCAT; }
{ID} { yylval->id = {yytext, (size_t) yyleng}; return ID; }
{INT} { errno = 0;
std::optional<int64_t> numMay = string2Int<int64_t>(yytext);
if (numMay.has_value()) {
yylval->n = *numMay;
} else {
throw ParseError(ErrorInfo{
.msg = HintFmt("invalid integer '%1%'", yytext),
.pos = state->positions[CUR_POS],
});
}
return INT;
}
{FLOAT} { errno = 0;
yylval->nf = strtod(yytext, 0);
if (errno != 0)
throw ParseError(ErrorInfo{
.msg = HintFmt("invalid float '%1%'", yytext),
.pos = state->positions[CUR_POS],
});
return FLOAT;
}
\$\{ { PUSH_STATE(DEFAULT); return DOLLAR_CURLY; }
\} { /* State INITIAL only exists at the bottom of the stack and is
used as a marker. DEFAULT replaces it everywhere else.
Popping when in INITIAL state causes an empty stack exception,
so don't */
if (YYSTATE != INITIAL)
POP_STATE();
return '}';
}
\{ { PUSH_STATE(DEFAULT); return '{'; }
\" { PUSH_STATE(STRING); return '"'; }
<STRING>([^\$\"\\]|\$[^\{\"\\]|\\{ANY}|\$\\{ANY})*\$/\" |
<STRING>([^\$\"\\]|\$[^\{\"\\]|\\{ANY}|\$\\{ANY})+ {
/* It is impossible to match strings ending with '$' with one
regex because trailing contexts are only valid at the end
of a rule. (A sane but undocumented limitation.) */
yylval->str = unescapeStr(state->symbols, yytext, yyleng);
return STR;
}
<STRING>\$\{ { PUSH_STATE(DEFAULT); return DOLLAR_CURLY; }
<STRING>\" { POP_STATE(); return '"'; }
<STRING>\$|\\|\$\\ {
/* This can only occur when we reach EOF, otherwise the above
(...|\$[^\{\"\\]|\\.|\$\\.)+ would have triggered.
This is technically invalid, but we leave the problem to the
parser who fails with exact location. */
return EOF;
}
\'\'(\ *\n)? { PUSH_STATE(IND_STRING); return IND_STRING_OPEN; }
<IND_STRING>([^\$\']|\$[^\{\']|\'[^\'\$])+ {
yylval->str = {yytext, (size_t) yyleng, true};
return IND_STR;
}
<IND_STRING>\'\'\$ |
<IND_STRING>\$ {
yylval->str = {"$", 1};
return IND_STR;
}
<IND_STRING>\'\'\' {
yylval->str = {"''", 2};
return IND_STR;
}
<IND_STRING>\'\'\\{ANY} {
yylval->str = unescapeStr(state->symbols, yytext + 2, yyleng - 2);
return IND_STR;
}
<IND_STRING>\$\{ { PUSH_STATE(DEFAULT); return DOLLAR_CURLY; }
<IND_STRING>\'\' { POP_STATE(); return IND_STRING_CLOSE; }
<IND_STRING>\' {
yylval->str = {"'", 1};
return IND_STR;
}
{PATH_SEG}\$\{ |
{HPATH_START}\$\{ {
PUSH_STATE(PATH_START);
yyless(0);
yylloc->unstash();
}
<PATH_START>{PATH_SEG} {
POP_STATE();
PUSH_STATE(INPATH_SLASH);
yylval->path = {yytext, (size_t) yyleng};
return PATH;
}
<PATH_START>{HPATH_START} {
POP_STATE();
PUSH_STATE(INPATH_SLASH);
yylval->path = {yytext, (size_t) yyleng};
return HPATH;
}
{PATH} {
if (yytext[yyleng-1] == '/')
PUSH_STATE(INPATH_SLASH);
else
PUSH_STATE(INPATH);
yylval->path = {yytext, (size_t) yyleng};
return PATH;
}
{HPATH} {
if (yytext[yyleng-1] == '/')
PUSH_STATE(INPATH_SLASH);
else
PUSH_STATE(INPATH);
yylval->path = {yytext, (size_t) yyleng};
return HPATH;
}
<INPATH,INPATH_SLASH>\$\{ {
POP_STATE();
PUSH_STATE(INPATH);
PUSH_STATE(DEFAULT);
return DOLLAR_CURLY;
}
<INPATH,INPATH_SLASH>{PATH}|{PATH_SEG}|{PATH_CHAR}+ {
POP_STATE();
if (yytext[yyleng-1] == '/')
PUSH_STATE(INPATH_SLASH);
else
PUSH_STATE(INPATH);
yylval->str = {yytext, (size_t) yyleng};
return STR;
}
<INPATH>{ANY} |
<INPATH><<EOF>> {
/* if we encounter a non-path character we inform the parser that the path has
ended with a PATH_END token and re-parse this character in the default
context (it may be ')', ';', or something of that sort) */
POP_STATE();
yyless(0);
yylloc->unstash();
return PATH_END;
}
<INPATH_SLASH>{ANY} |
<INPATH_SLASH><<EOF>> {
throw ParseError(ErrorInfo{
.msg = HintFmt("path has a trailing slash"),
.pos = state->positions[CUR_POS],
});
}
{SPATH} { yylval->path = {yytext, (size_t) yyleng}; return SPATH; }
{URI} { yylval->uri = {yytext, (size_t) yyleng}; return URI; }
[ \t\r\n]+ /* eat up whitespace */
\#[^\r\n]* /* single-line comments */
\/\*([^*]|\*+[^*/])*\*+\/ /* long comments */
{ANY} {
/* Don't return a negative number, as this will cause
Bison to stop parsing without an error. */
return (unsigned char) yytext[0];
}
%%
+53 -7
View File
@@ -1,6 +1,53 @@
# SPDX-FileCopyrightText: 2024 Nix and Lix Authors
#
# SPDX-License-Identifier: LGPL-2.1-only
parser_tab = custom_target(
input : 'parser.y',
output : [
'parser-tab.cc',
'parser-tab.hh',
],
command : [
'bison',
'-v',
'-o',
'@OUTPUT0@',
'@INPUT@',
'-d',
],
# NOTE(Qyriad): Meson doesn't support installing only part of a custom target, so we add
# an install script below which removes parser-tab.cc.
install : true,
install_dir : includedir / 'lix/libexpr',
)
lexer_tab = custom_target(
input : [
'lexer.l',
parser_tab,
],
output : [
'lexer-tab.cc',
'lexer-tab.hh',
],
command : [
'flex',
'--outfile',
'@OUTPUT0@',
'--header-file=' + '@OUTPUT1@',
'@INPUT0@',
],
# NOTE(Qyriad): Meson doesn't support installing only part of a custom target, so we add
# an install script below which removes lexer-tab.cc.
install : true,
install_dir : includedir / 'lix/libexpr',
)
# TODO(Qyriad): When the parser and lexer are rewritten this should be removed.
# NOTE(Qyriad): We do this this way instead of an inline bash or rm command
# due to subtleties in Meson. Check the comments in cleanup-install.bash for details.
meson.add_install_script(
bash,
meson.project_source_root() / 'meson/cleanup-install.bash',
'@0@'.format(includedir),
)
libexpr_generated_headers = [
gen_header.process('primops/derivation.nix', preserve_path_from : meson.current_source_dir()),
@@ -28,7 +75,6 @@ libexpr_sources = files(
'get-drvs.cc',
'json-to-value.cc',
'nixexpr.cc',
'parser/parser.cc',
'paths.cc',
'primops.cc',
'print-ambiguous.cc',
@@ -64,9 +110,7 @@ libexpr_headers = files(
'get-drvs.hh',
'json-to-value.hh',
'nixexpr.hh',
'parser/change_head.hh',
'parser/grammar.hh',
'parser/state.hh',
'parser-state.hh',
'pos-idx.hh',
'pos-table.hh',
'primops.hh',
@@ -85,6 +129,8 @@ libexpr_headers = files(
libexpr = library(
'lixexpr',
libexpr_sources,
parser_tab,
lexer_tab,
libexpr_generated_headers,
dependencies : [
liblixutil,
+9 -20
View File
@@ -1,7 +1,3 @@
// SPDX-FileCopyrightText: 2024 Nix and Lix Authors
//
// SPDX-License-Identifier: LGPL-2.1-only
#include "nixexpr.hh"
#include "derivations.hh"
#include "eval.hh"
@@ -54,13 +50,6 @@ void ExprVar::show(const SymbolTable & symbols, std::ostream & str) const
str << symbols[name];
}
void ExprInheritFrom::show(SymbolTable const & symbols, std::ostream & str) const
{
str << "(/* expanded inherit (expr) */ ";
fromExpr->show(symbols, str);
str << ")";
}
void ExprSelect::show(const SymbolTable & symbols, std::ostream & str) const
{
str << "(";
@@ -90,7 +79,7 @@ void ExprAttrs::showBindings(const SymbolTable & symbols, std::ostream & str) co
return sa < sb;
});
std::vector<Symbol> inherits;
std::map<Displacement, std::vector<Symbol>> inheritsFrom;
std::map<ExprInheritFrom *, std::vector<Symbol>> inheritsFrom;
for (auto & i : sorted) {
switch (i->second.kind) {
case AttrDef::Kind::Plain:
@@ -101,7 +90,7 @@ void ExprAttrs::showBindings(const SymbolTable & symbols, std::ostream & str) co
case AttrDef::Kind::InheritedFrom: {
auto & select = dynamic_cast<ExprSelect &>(*i->second.e);
auto & from = dynamic_cast<ExprInheritFrom &>(*select.e);
inheritsFrom[from.displ].push_back(i->first);
inheritsFrom[&from].push_back(i->first);
break;
}
}
@@ -113,7 +102,7 @@ void ExprAttrs::showBindings(const SymbolTable & symbols, std::ostream & str) co
}
for (const auto & [from, syms] : inheritsFrom) {
str << "inherit (";
(*inheritFromExprs)[from]->show(symbols, str);
(*inheritFromExprs)[from->displ]->show(symbols, str);
str << ")";
for (auto sym : syms) str << " " << symbols[sym];
str << "; ";
@@ -162,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
// same expression being printed in two different ways depending on its
// context. always use lexicographic ordering to avoid this.
for (const Formal & i : formals->lexicographicOrder(symbols)) {
for (auto & i : formals->lexicographicOrder(symbols)) {
if (first) first = false; else str << ", ";
str << symbols[i.name];
if (i.def) {
@@ -187,7 +176,7 @@ void ExprCall::show(const SymbolTable & symbols, std::ostream & str) const
{
str << '(';
fun->show(symbols, str);
for (auto & e : args) {
for (auto e : args) {
str << ' ';
e->show(symbols, str);
}
@@ -242,7 +231,7 @@ void ExprConcatStrings::show(const SymbolTable & symbols, std::ostream & str) co
{
bool first = true;
str << "(";
for (auto & i : es) {
for (auto & i : *es) {
if (first) first = false; else str << " + ";
i.second->show(symbols, str);
}
@@ -386,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
// invisible, but the evaluator does not allow for this yet.
auto inner = std::make_shared<StaticEnv>(nullptr, env.get(), 0);
for (auto & from : *inheritFromExprs)
for (auto from : *inheritFromExprs)
from->bindVars(es, env);
return inner;
@@ -473,7 +462,7 @@ void ExprCall::bindVars(EvalState & es, const std::shared_ptr<const StaticEnv> &
es.exprEnvs.insert(std::make_pair(this, env));
fun->bindVars(es, env);
for (auto & e : args)
for (auto e : args)
e->bindVars(es, env);
}
@@ -558,7 +547,7 @@ void ExprConcatStrings::bindVars(EvalState & es, const std::shared_ptr<const Sta
if (es.debugRepl)
es.exprEnvs.insert(std::make_pair(this, env));
for (auto & i : this->es)
for (auto & i : *this->es)
i.second->bindVars(es, env);
}
+47 -99
View File
@@ -1,7 +1,3 @@
// SPDX-FileCopyrightText: 2024 Nix and Lix Authors
//
// SPDX-License-Identifier: LGPL-2.1-only
#pragma once
///@file
@@ -32,9 +28,9 @@ struct StaticEnv;
struct AttrName
{
Symbol symbol;
std::unique_ptr<Expr> expr;
Expr * expr;
AttrName(Symbol s) : symbol(s) {};
AttrName(std::unique_ptr<Expr> e) : expr(std::move(e)) {};
AttrName(Expr * e) : expr(e) {};
};
typedef std::vector<AttrName> AttrPath;
@@ -46,21 +42,12 @@ std::string showAttrPath(const SymbolTable & symbols, const AttrPath & attrPath)
struct Expr
{
protected:
Expr(Expr &&) = default;
Expr & operator=(Expr &&) = default;
public:
struct AstSymbols {
Symbol sub, lessThan, mul, div, or_, findFile, nixPath, body;
};
Expr() = default;
Expr(const Expr &) = delete;
Expr & operator=(const Expr &) = delete;
virtual ~Expr() { };
virtual void show(const SymbolTable & symbols, std::ostream & str) const;
virtual void bindVars(EvalState & es, const std::shared_ptr<const StaticEnv> & env);
virtual void eval(EvalState & state, Env & env, Value & v);
@@ -148,46 +135,32 @@ struct ExprVar : Expr
*/
struct ExprInheritFrom : ExprVar
{
ref<Expr> fromExpr;
ExprInheritFrom(PosIdx pos, Displacement displ, ref<Expr> fromExpr)
: ExprVar(pos, {}), fromExpr(fromExpr)
ExprInheritFrom(PosIdx pos, Displacement displ): ExprVar(pos, {})
{
this->level = 0;
this->displ = displ;
this->fromWith = nullptr;
}
void show(SymbolTable const & symbols, std::ostream & str) const override;
void bindVars(EvalState & es, const std::shared_ptr<const StaticEnv> & env) override;
void bindVars(EvalState & es, const std::shared_ptr<const StaticEnv> & env);
};
struct ExprSelect : Expr
{
PosIdx pos;
/** The expression attributes are being selected on. e.g. `foo` in `foo.bar.baz`. */
std::unique_ptr<Expr> e;
/** A default value specified with `or`, if the selected attr doesn't exist.
* e.g. `bix` in `foo.bar.baz or bix`
*/
std::unique_ptr<Expr> def;
/** The path of attributes being selected. e.g. `bar.baz` in `foo.bar.baz.` */
Expr * e, * def;
AttrPath attrPath;
ExprSelect(const PosIdx & pos, std::unique_ptr<Expr> e, AttrPath attrPath, std::unique_ptr<Expr> def) : pos(pos), e(std::move(e)), def(std::move(def)), attrPath(std::move(attrPath)) { };
ExprSelect(const PosIdx & pos, std::unique_ptr<Expr> e, Symbol name) : pos(pos), e(std::move(e)) { attrPath.push_back(AttrName(name)); };
ExprSelect(const PosIdx & pos, Expr * e, AttrPath attrPath, Expr * def) : pos(pos), e(e), def(def), attrPath(std::move(attrPath)) { };
ExprSelect(const PosIdx & pos, Expr * e, Symbol name) : pos(pos), e(e), def(0) { attrPath.push_back(AttrName(name)); };
PosIdx getPos() const override { return pos; }
COMMON_METHODS
};
struct ExprOpHasAttr : Expr
{
std::unique_ptr<Expr> e;
Expr * e;
AttrPath attrPath;
ExprOpHasAttr(std::unique_ptr<Expr> e, AttrPath attrPath) : e(std::move(e)), attrPath(std::move(attrPath)) { };
ExprOpHasAttr(Expr * e, AttrPath attrPath) : e(e), attrPath(std::move(attrPath)) { };
PosIdx getPos() const override { return e->getPos(); }
COMMON_METHODS
};
@@ -207,11 +180,11 @@ struct ExprAttrs : Expr
};
Kind kind;
std::unique_ptr<Expr> e;
Expr * e;
PosIdx pos;
Displacement displ; // displacement
AttrDef(std::unique_ptr<Expr> e, const PosIdx & pos, Kind kind = Kind::Plain)
: kind(kind), e(std::move(e)), pos(pos) { };
AttrDef(Expr * e, const PosIdx & pos, Kind kind = Kind::Plain)
: kind(kind), e(e), pos(pos) { };
AttrDef() { };
template<typename T>
@@ -230,12 +203,12 @@ struct ExprAttrs : Expr
};
typedef std::map<Symbol, AttrDef> AttrDefs;
AttrDefs attrs;
std::unique_ptr<std::vector<ref<Expr>>> inheritFromExprs;
std::unique_ptr<std::vector<Expr *>> inheritFromExprs;
struct DynamicAttrDef {
std::unique_ptr<Expr> nameExpr, valueExpr;
Expr * nameExpr, * valueExpr;
PosIdx pos;
DynamicAttrDef(std::unique_ptr<Expr> nameExpr, std::unique_ptr<Expr> valueExpr, const PosIdx & pos)
: nameExpr(std::move(nameExpr)), valueExpr(std::move(valueExpr)), pos(pos) { };
DynamicAttrDef(Expr * nameExpr, Expr * valueExpr, const PosIdx & pos)
: nameExpr(nameExpr), valueExpr(valueExpr), pos(pos) { };
};
typedef std::vector<DynamicAttrDef> DynamicAttrDefs;
DynamicAttrDefs dynamicAttrs;
@@ -252,7 +225,7 @@ struct ExprAttrs : Expr
struct ExprList : Expr
{
std::vector<std::unique_ptr<Expr>> elems;
std::vector<Expr *> elems;
ExprList() { };
COMMON_METHODS
Value * maybeThunk(EvalState & state, Env & env) override;
@@ -267,7 +240,7 @@ struct Formal
{
PosIdx pos;
Symbol name;
std::unique_ptr<Expr> def;
Expr * def;
};
/** Attribute set destructuring in arguments of a lambda, if present */
@@ -284,9 +257,9 @@ struct Formals
return it != formals.end() && it->name == arg;
}
std::vector<std::reference_wrapper<const Formal>> lexicographicOrder(const SymbolTable & symbols) const
std::vector<Formal> lexicographicOrder(const SymbolTable & symbols) const
{
std::vector<std::reference_wrapper<const Formal>> result(formals.begin(), formals.end());
std::vector<Formal> result(formals.begin(), formals.end());
std::sort(result.begin(), result.end(),
[&] (const Formal & a, const Formal & b) {
std::string_view sa = symbols[a.name], sb = symbols[b.name];
@@ -310,55 +283,30 @@ struct ExprLambda : Expr
Symbol arg;
/** Formals are present when the lambda destructures an attr set as
* argument, with or without ellipsis */
std::unique_ptr<Formals> formals;
std::unique_ptr<Expr> body;
ExprLambda(PosIdx pos, Symbol arg, std::unique_ptr<Formals> formals, std::unique_ptr<Expr> body)
: pos(pos), arg(arg), formals(std::move(formals)), body(std::move(body))
Formals * formals;
Expr * body;
ExprLambda(PosIdx pos, Symbol arg, Formals * formals, Expr * body)
: pos(pos), arg(arg), formals(formals), body(body)
{
};
ExprLambda(PosIdx pos, std::unique_ptr<Formals> formals, std::unique_ptr<Expr> body)
: pos(pos), formals(std::move(formals)), body(std::move(body))
ExprLambda(PosIdx pos, Formals * formals, Expr * body)
: pos(pos), formals(formals), body(body)
{
}
void setName(Symbol name) override;
std::string showNamePos(const EvalState & state) const;
inline bool hasFormals() const { return formals != nullptr; }
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
};
struct ExprCall : Expr
{
std::unique_ptr<Expr> fun;
std::vector<std::unique_ptr<Expr>> args;
Expr * fun;
std::vector<Expr *> args;
PosIdx pos;
ExprCall(const PosIdx & pos, std::unique_ptr<Expr> fun, std::vector<std::unique_ptr<Expr>> && args)
: fun(std::move(fun)), args(std::move(args)), pos(pos)
ExprCall(const PosIdx & pos, Expr * fun, std::vector<Expr *> && args)
: fun(fun), args(args), pos(pos)
{ }
PosIdx getPos() const override { return pos; }
COMMON_METHODS
@@ -366,19 +314,19 @@ struct ExprCall : Expr
struct ExprLet : Expr
{
std::unique_ptr<ExprAttrs> attrs;
std::unique_ptr<Expr> body;
ExprLet(std::unique_ptr<ExprAttrs> attrs, std::unique_ptr<Expr> body) : attrs(std::move(attrs)), body(std::move(body)) { };
ExprAttrs * attrs;
Expr * body;
ExprLet(ExprAttrs * attrs, Expr * body) : attrs(attrs), body(body) { };
COMMON_METHODS
};
struct ExprWith : Expr
{
PosIdx pos;
std::unique_ptr<Expr> attrs, body;
Expr * attrs, * body;
size_t prevWith;
ExprWith * parentWith;
ExprWith(const PosIdx & pos, std::unique_ptr<Expr> attrs, std::unique_ptr<Expr> body) : pos(pos), attrs(std::move(attrs)), body(std::move(body)) { };
ExprWith(const PosIdx & pos, Expr * attrs, Expr * body) : pos(pos), attrs(attrs), body(body) { };
PosIdx getPos() const override { return pos; }
COMMON_METHODS
};
@@ -386,8 +334,8 @@ struct ExprWith : Expr
struct ExprIf : Expr
{
PosIdx pos;
std::unique_ptr<Expr> cond, then, 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_)) { };
Expr * cond, * then, * else_;
ExprIf(const PosIdx & pos, Expr * cond, Expr * then, Expr * else_) : pos(pos), cond(cond), then(then), else_(else_) { };
PosIdx getPos() const override { return pos; }
COMMON_METHODS
};
@@ -395,16 +343,16 @@ struct ExprIf : Expr
struct ExprAssert : Expr
{
PosIdx pos;
std::unique_ptr<Expr> cond, 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)) { };
Expr * cond, * body;
ExprAssert(const PosIdx & pos, Expr * cond, Expr * body) : pos(pos), cond(cond), body(body) { };
PosIdx getPos() const override { return pos; }
COMMON_METHODS
};
struct ExprOpNot : Expr
{
std::unique_ptr<Expr> e;
ExprOpNot(std::unique_ptr<Expr> e) : e(std::move(e)) { };
Expr * e;
ExprOpNot(Expr * e) : e(e) { };
PosIdx getPos() const override { return e->getPos(); }
COMMON_METHODS
};
@@ -413,9 +361,9 @@ struct ExprOpNot : Expr
struct name : Expr \
{ \
PosIdx pos; \
std::unique_ptr<Expr> e1, e2; \
name(std::unique_ptr<Expr> e1, std::unique_ptr<Expr> e2) : e1(std::move(e1)), e2(std::move(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)) { }; \
Expr * e1, * e2; \
name(Expr * e1, Expr * e2) : e1(e1), e2(e2) { }; \
name(const PosIdx & pos, Expr * e1, Expr * e2) : pos(pos), e1(e1), e2(e2) { }; \
void show(const SymbolTable & symbols, std::ostream & str) const override \
{ \
str << "("; e1->show(symbols, str); str << " " s " "; e2->show(symbols, str); str << ")"; \
@@ -440,9 +388,9 @@ struct ExprConcatStrings : Expr
{
PosIdx pos;
bool forceString;
std::vector<std::pair<PosIdx, std::unique_ptr<Expr>>> es;
ExprConcatStrings(const PosIdx & pos, bool forceString, std::vector<std::pair<PosIdx, std::unique_ptr<Expr>>> es)
: pos(pos), forceString(forceString), es(std::move(es)) { };
std::vector<std::pair<PosIdx, Expr *>> * es;
ExprConcatStrings(const PosIdx & pos, bool forceString, std::vector<std::pair<PosIdx, Expr *>> * es)
: pos(pos), forceString(forceString), es(es) { };
PosIdx getPos() const override { return pos; }
COMMON_METHODS
};
@@ -1,50 +1,61 @@
// SPDX-FileCopyrightText: 2024 Nix and Lix Authors
//
// SPDX-License-Identifier: LGPL-2.1-only
#pragma once
///@file
#include "eval.hh"
namespace nix::parser {
namespace nix {
/**
* @note Storing a C-style `char *` and `size_t` allows us to avoid
* having to define the special members that using string_view here
* would implicitly delete.
*/
struct StringToken
{
std::string_view s;
const char * p;
size_t l;
bool hasIndentation;
operator std::string_view() const { return s; }
operator std::string_view() const { return {p, l}; }
};
struct State
struct ParserLocation
{
int first_line, first_column;
int last_line, last_column;
// backup to recover from yyless(0)
int stashed_first_column, stashed_last_column;
void stash() {
stashed_first_column = first_column;
stashed_last_column = last_column;
}
void unstash() {
first_column = stashed_first_column;
last_column = stashed_last_column;
}
};
struct ParserState
{
SymbolTable & symbols;
PosTable & positions;
Expr * result;
SourcePath basePath;
PosTable::Origin origin;
const Expr::AstSymbols & s;
void dupAttr(const AttrPath & attrPath, const PosIdx pos, const PosIdx prevPos);
void dupAttr(Symbol attr, const PosIdx pos, const PosIdx prevPos);
void addAttr(ExprAttrs * attrs, AttrPath && attrPath, std::unique_ptr<Expr> e, const PosIdx pos);
std::unique_ptr<Formals> validateFormals(std::unique_ptr<Formals> formals, PosIdx pos = noPos, Symbol arg = {});
std::unique_ptr<Expr> stripIndentation(const PosIdx pos,
std::vector<std::pair<PosIdx, std::variant<std::unique_ptr<Expr>, StringToken>>> && es);
// lazy positioning means we don't get byte offsets directly, in.position() would work
// but also requires line and column (which is expensive)
PosIdx at(const auto & in)
{
return positions.add(origin, in.begin() - in.input().begin());
}
PosIdx atEnd(const auto & in)
{
return positions.add(origin, in.end() - in.input().begin());
}
void addAttr(ExprAttrs * attrs, AttrPath && attrPath, Expr * e, const PosIdx pos);
Formals * validateFormals(Formals * formals, PosIdx pos = noPos, Symbol arg = {});
Expr * stripIndentation(const PosIdx pos,
std::vector<std::pair<PosIdx, std::variant<Expr *, StringToken>>> && es);
PosIdx at(const ParserLocation & loc);
};
inline void State::dupAttr(const AttrPath & attrPath, const PosIdx pos, const PosIdx prevPos)
inline void ParserState::dupAttr(const AttrPath & attrPath, const PosIdx pos, const PosIdx prevPos)
{
throw ParseError({
.msg = HintFmt("attribute '%1%' already defined at %2%",
@@ -53,7 +64,7 @@ inline void State::dupAttr(const AttrPath & attrPath, const PosIdx pos, const Po
});
}
inline void State::dupAttr(Symbol attr, const PosIdx pos, const PosIdx prevPos)
inline void ParserState::dupAttr(Symbol attr, const PosIdx pos, const PosIdx prevPos)
{
throw ParseError({
.msg = HintFmt("attribute '%1%' already defined at %2%", symbols[attr], positions[prevPos]),
@@ -61,7 +72,7 @@ inline void State::dupAttr(Symbol attr, const PosIdx pos, const PosIdx prevPos)
});
}
inline void State::addAttr(ExprAttrs * attrs, AttrPath && attrPath, std::unique_ptr<Expr> e, const PosIdx pos)
inline void ParserState::addAttr(ExprAttrs * attrs, AttrPath && attrPath, Expr * e, const PosIdx pos)
{
AttrPath::iterator i;
// All attrpaths have at least one attr
@@ -73,25 +84,20 @@ inline void State::addAttr(ExprAttrs * attrs, AttrPath && attrPath, std::unique_
ExprAttrs::AttrDefs::iterator j = attrs->attrs.find(i->symbol);
if (j != attrs->attrs.end()) {
if (j->second.kind != ExprAttrs::AttrDef::Kind::Inherited) {
ExprAttrs * attrs2 = dynamic_cast<ExprAttrs *>(j->second.e.get());
if (!attrs2) {
attrPath.erase(i + 1, attrPath.end());
dupAttr(attrPath, pos, j->second.pos);
}
ExprAttrs * attrs2 = dynamic_cast<ExprAttrs *>(j->second.e);
if (!attrs2) dupAttr({attrPath.begin(), i + 1}, pos, j->second.pos);
attrs = attrs2;
} else {
attrPath.erase(i + 1, attrPath.end());
dupAttr(attrPath, pos, j->second.pos);
}
} else
dupAttr({attrPath.begin(), i + 1}, pos, j->second.pos);
} else {
auto next = attrs->attrs.emplace(std::piecewise_construct,
std::tuple(i->symbol),
std::tuple(std::make_unique<ExprAttrs>(), pos));
attrs = static_cast<ExprAttrs *>(next.first->second.e.get());
ExprAttrs * nested = new ExprAttrs;
attrs->attrs[i->symbol] = ExprAttrs::AttrDef(nested, pos);
attrs = nested;
}
} else {
auto & next = attrs->dynamicAttrs.emplace_back(std::move(i->expr), std::make_unique<ExprAttrs>(), pos);
attrs = static_cast<ExprAttrs *>(next.valueExpr.get());
ExprAttrs *nested = new ExprAttrs;
attrs->dynamicAttrs.push_back(ExprAttrs::DynamicAttrDef(i->expr, nested, pos));
attrs = nested;
}
}
// Expr insertion.
@@ -103,41 +109,41 @@ inline void State::addAttr(ExprAttrs * attrs, AttrPath && attrPath, std::unique_
// e and the expr pointed by the attr path are two attribute sets,
// we want to merge them.
// Otherwise, throw an error.
auto * ae = dynamic_cast<ExprAttrs *>(e.get());
auto * jAttrs = dynamic_cast<ExprAttrs *>(j->second.e.get());
auto ae = dynamic_cast<ExprAttrs *>(e);
auto jAttrs = dynamic_cast<ExprAttrs *>(j->second.e);
if (jAttrs && ae) {
if (ae->inheritFromExprs && !jAttrs->inheritFromExprs)
jAttrs->inheritFromExprs = std::make_unique<std::vector<ref<Expr>>>();
jAttrs->inheritFromExprs = std::make_unique<std::vector<Expr *>>();
for (auto & ad : ae->attrs) {
auto j2 = jAttrs->attrs.find(ad.first);
if (j2 != jAttrs->attrs.end()) // Attr already defined in iAttrs, error.
return dupAttr(ad.first, j2->second.pos, ad.second.pos);
dupAttr(ad.first, j2->second.pos, ad.second.pos);
jAttrs->attrs.emplace(ad.first, ad.second);
if (ad.second.kind == ExprAttrs::AttrDef::Kind::InheritedFrom) {
auto & sel = dynamic_cast<ExprSelect &>(*ad.second.e);
auto & from = dynamic_cast<ExprInheritFrom &>(*sel.e);
from.displ += jAttrs->inheritFromExprs->size();
}
jAttrs->attrs.emplace(ad.first, std::move(ad.second));
}
std::ranges::move(ae->dynamicAttrs, std::back_inserter(jAttrs->dynamicAttrs));
if (ae->inheritFromExprs)
std::ranges::move(*ae->inheritFromExprs, std::back_inserter(*jAttrs->inheritFromExprs));
jAttrs->dynamicAttrs.insert(jAttrs->dynamicAttrs.end(), ae->dynamicAttrs.begin(), ae->dynamicAttrs.end());
if (ae->inheritFromExprs) {
jAttrs->inheritFromExprs->insert(jAttrs->inheritFromExprs->end(),
ae->inheritFromExprs->begin(), ae->inheritFromExprs->end());
}
} else {
dupAttr(attrPath, pos, j->second.pos);
}
} else {
// This attr path is not defined. Let's create it.
attrs->attrs.emplace(i->symbol, ExprAttrs::AttrDef(e, pos));
e->setName(i->symbol);
attrs->attrs.emplace(std::piecewise_construct,
std::tuple(i->symbol),
std::tuple(std::move(e), pos));
}
} else {
attrs->dynamicAttrs.emplace_back(std::move(i->expr), std::move(e), pos);
attrs->dynamicAttrs.push_back(ExprAttrs::DynamicAttrDef(i->expr, e, pos));
}
}
inline std::unique_ptr<Formals> State::validateFormals(std::unique_ptr<Formals> formals, PosIdx pos, Symbol arg)
inline Formals * ParserState::validateFormals(Formals * formals, PosIdx pos, Symbol arg)
{
std::sort(formals->formals.begin(), formals->formals.end(),
[] (const auto & a, const auto & b) {
@@ -166,10 +172,10 @@ inline std::unique_ptr<Formals> State::validateFormals(std::unique_ptr<Formals>
return formals;
}
inline std::unique_ptr<Expr> State::stripIndentation(const PosIdx pos,
std::vector<std::pair<PosIdx, std::variant<std::unique_ptr<Expr>, StringToken>>> && es)
inline Expr * ParserState::stripIndentation(const PosIdx pos,
std::vector<std::pair<PosIdx, std::variant<Expr *, StringToken>>> && es)
{
if (es.empty()) return std::make_unique<ExprString>("");
if (es.empty()) return new ExprString("");
/* Figure out the minimum indentation. Note that by design
whitespace-only final lines are not taken into account. (So
@@ -187,11 +193,11 @@ inline std::unique_ptr<Expr> State::stripIndentation(const PosIdx pos,
}
continue;
}
for (size_t j = 0; j < str->s.size(); ++j) {
for (size_t j = 0; j < str->l; ++j) {
if (atStartOfLine) {
if (str->s[j] == ' ')
if (str->p[j] == ' ')
curIndent++;
else if (str->s[j] == '\n') {
else if (str->p[j] == '\n') {
/* Empty line, doesn't influence minimum
indentation. */
curIndent = 0;
@@ -199,7 +205,7 @@ inline std::unique_ptr<Expr> State::stripIndentation(const PosIdx pos,
atStartOfLine = false;
if (curIndent < minIndent) minIndent = curIndent;
}
} else if (str->s[j] == '\n') {
} else if (str->p[j] == '\n') {
atStartOfLine = true;
curIndent = 0;
}
@@ -207,35 +213,35 @@ inline std::unique_ptr<Expr> State::stripIndentation(const PosIdx pos,
}
/* Strip spaces from each line. */
std::vector<std::pair<PosIdx, std::unique_ptr<Expr>>> es2;
auto * es2 = new std::vector<std::pair<PosIdx, Expr *>>;
atStartOfLine = true;
size_t curDropped = 0;
size_t n = es.size();
auto i = es.begin();
const auto trimExpr = [&] (std::unique_ptr<Expr> e) {
const auto trimExpr = [&] (Expr * e) {
atStartOfLine = false;
curDropped = 0;
es2.emplace_back(i->first, std::move(e));
es2->emplace_back(i->first, e);
};
const auto trimString = [&] (const StringToken t) {
const auto trimString = [&] (const StringToken & t) {
std::string s2;
for (size_t j = 0; j < t.s.size(); ++j) {
for (size_t j = 0; j < t.l; ++j) {
if (atStartOfLine) {
if (t.s[j] == ' ') {
if (t.p[j] == ' ') {
if (curDropped++ >= minIndent)
s2 += t.s[j];
s2 += t.p[j];
}
else if (t.s[j] == '\n') {
else if (t.p[j] == '\n') {
curDropped = 0;
s2 += t.s[j];
s2 += t.p[j];
} else {
atStartOfLine = false;
curDropped = 0;
s2 += t.s[j];
s2 += t.p[j];
}
} else {
s2 += t.s[j];
if (t.s[j] == '\n') atStartOfLine = true;
s2 += t.p[j];
if (t.p[j] == '\n') atStartOfLine = true;
}
}
@@ -247,17 +253,24 @@ inline std::unique_ptr<Expr> State::stripIndentation(const PosIdx pos,
s2 = std::string(s2, 0, p + 1);
}
es2.emplace_back(i->first, std::make_unique<ExprString>(std::move(s2)));
es2->emplace_back(i->first, new ExprString(std::move(s2)));
};
for (; i != es.end(); ++i, --n) {
std::visit(overloaded { trimExpr, trimString }, std::move(i->second));
std::visit(overloaded { trimExpr, trimString }, i->second);
}
/* If this is a single string, then don't do a concatenation. */
if (es2.size() == 1 && dynamic_cast<ExprString *>(es2[0].second.get())) {
return std::move(es2[0].second);
if (es2->size() == 1 && dynamic_cast<ExprString *>((*es2)[0].second)) {
auto *const result = (*es2)[0].second;
delete es2;
return result;
}
return std::make_unique<ExprConcatStrings>(pos, true, std::move(es2));
return new ExprConcatStrings(pos, true, es2);
}
inline PosIdx ParserState::at(const ParserLocation & loc)
{
return positions.add(origin, loc.first_column);
}
}

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