Compare commits

..

85 Commits

Author SHA1 Message Date
Eelco Dolstra
2cf2bb45fc Fix C++17 build 2023-10-18 19:32:20 +02:00
Eelco Dolstra
70559cf89a Document that redirected tarball flakerefs can specify lastModified
(cherry picked from commit 56763ff918)
2023-10-18 17:08:00 +02:00
Eelco Dolstra
fb0b056d2f Tarball trees: Propagate lastModified
This makes them behave consistently with GitHub/GitLab flakes.

(cherry picked from commit 81045f243f)
(cherry picked from commit eb21904198)
2023-10-18 17:05:52 +02:00
Justin Restivo
38f4fe04e3 fix: build aarch64-darwin
(cherry picked from commit 220aa8e0ac)
2023-08-07 21:39:05 +02:00
Eelco Dolstra
d5f98aa91d Bump version 2023-08-07 21:38:53 +02:00
Eelco Dolstra
5bc21dccc9 Merge pull request #8763 from hercules-ci/backport-7447-to-2.13-maintenance
Backport 7447 to 2.13 maintenance
2023-08-07 19:30:27 +02:00
Eelco Dolstra
01401ffffd Merge remote-tracking branch 'origin/backport-8483-to-2.13-maintenance' into 2.13-maintenance 2023-08-07 16:43:57 +02:00
Eelco Dolstra
d971ce3ed4 Add docs
(cherry picked from commit cab03fb779)
2023-08-07 16:38:50 +02:00
Eelco Dolstra
dc718e28c9 Allow tarball URLs to redirect to a lockable immutable URL
Previously, for tarball flakes, we recorded the original URL of the
tarball flake, rather than the URL to which it ultimately
redirects. Thus, a flake URL like
http://example.org/patchelf-latest.tar that redirects to
http://example.org/patchelf-<revision>.tar was not really usable. We
couldn't record the redirected URL, because sites like GitHub redirect
to CDN URLs that we can't rely on to be stable.

So now we use the redirected URL only if the server returns the
`x-nix-is-immutable` or `x-amz-meta-nix-is-immutable` headers in its
response.

(cherry picked from commit 1ad3328c5e)
2023-08-07 16:38:30 +02:00
Eelco Dolstra
cb425af931 Add a generic check for rev attribute mismatches
(cherry picked from commit 3402b650cd)
2023-08-07 16:33:57 +02:00
Eelco Dolstra
3cc2ff77da GC server: Clear O_NONBLOCK on the right file descriptor
The bug fix in 6d30f9e6fe erroneously
cleared O_NONBLOCK on the server rather than client FD (leaving both
in an incorrect state).

Fixes #8551.

(cherry picked from commit a6a75ecad8)
2023-08-07 16:31:04 +02:00
Eelco Dolstra
0a0a4e2ea3 restoreMountNamespace(): Restore the original root directory
This is necessary when we're in a chroot environment, where the
process root is not the same as the root of the mount namespace
(e.g. in nixos-enter).

Fixes #7602.

(cherry picked from commit e54538c461)
2023-08-07 08:23:32 +00:00
Robert Hensing
995b658e72 Fixup release notes (#8393)
* Fixup release notes
2023-07-31 16:40:57 +02:00
Robert Hensing
1dfcf41645 rl-next.md: Minor improvement
(cherry picked from commit 37c533ed27)
2023-07-31 16:37:44 +02:00
Alex Ameen
1a6446b5eb primop: add readFileType, optimize readDir
Allows checking directory entry type of a single file/directory.

This was added to optimize the use of `builtins.readDir` on some
filesystems and operating systems which cannot detect this information
using POSIX's `readdir`.

Previously `builtins.readDir` would eagerly use system calls to lookup
these filetypes using other interfaces; this change makes these
operations lazy in the attribute values for each file with application
of `builtins.readFileType`.

(cherry picked from commit 153ee460c5)
2023-07-31 16:37:44 +02:00
Eelco Dolstra
25e1eb4dfd Bump version 2023-05-30 11:53:09 +02:00
Théophane Hufschmitt
43aa47cf8a Merge pull request #8410 from tweag/backport-7957-to-2.13-maintenance
[Backport 2.13-maintenance] Switch to cachix/install-nix-action@v20
2023-05-28 17:24:13 +02:00
Théophane Hufschmitt
d17ecc5177 Switch to cachix/install-nix-action@v20
Fixes the installation issue with the latest Nix.

Also revert the pinning to nix-2.13 since it's not needed any more.

(cherry picked from commit c3b5499dff)
2023-05-28 17:23:37 +02:00
Théophane Hufschmitt
e0afeef10f Merge pull request #8409 from NixOS/backport-7766-to-2.13-maintenance
[Backport 2.13-maintenance] Bump cachix/install-nix-action from 18 to 19
2023-05-28 17:18:25 +02:00
dependabot[bot]
478b015577 Bump cachix/install-nix-action from 18 to 19
Bumps [cachix/install-nix-action](https://github.com/cachix/install-nix-action) from 18 to 19.
- [Release notes](https://github.com/cachix/install-nix-action/releases)
- [Commits](https://github.com/cachix/install-nix-action/compare/v18...v19)

---
updated-dependencies:
- dependency-name: cachix/install-nix-action
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
(cherry picked from commit 6fdce7a9df)
2023-05-28 15:18:06 +00:00
Théophane Hufschmitt
691d1170e5 Merge pull request #8408 from NixOS/backport-8399-to-2.13-maintenance
[Backport 2.13-maintenance] Properly report build errors on chrooted stores
2023-05-28 17:11:48 +02:00
Théophane Hufschmitt
00811f98fb Properly report build errors on chrooted stores
When encountering a build error, Nix moves the output paths out of the
chroot into their final location (for “easier debugging of build
failures”). However this was broken for chroot stores as it was moving
it to the _logical_ location, not the _physical_ one.

Fix it by moving to the physical (_real_) location.

Fix https://github.com/NixOS/nix/issues/8395

(cherry picked from commit d16a1994fb)
2023-05-28 15:11:26 +00:00
Théophane Hufschmitt
8aa178f75b Merge pull request #8181 from NixOS/backport-8179-to-2.13-maintenance
[Backport 2.13-maintenance] disable gc on coroutine
2023-04-08 13:44:15 +02:00
Yorick van Pelt
ad96bf9791 Add talkative msg for coro gc debug
(cherry picked from commit 62ddd8633c)
2023-04-07 16:21:30 +00:00
Yorick van Pelt
de7803e15f Always disable GC in a coroutine unless the patch is applied
(cherry picked from commit 58d24a4cb6)
2023-04-07 16:21:30 +00:00
Yorick van Pelt
cc90264a7d DisableGC: replace by CoroutineContext, std::shared_ptr<void>
(cherry picked from commit 00bc34430b)
2023-04-07 16:21:30 +00:00
Yorick van Pelt
325ea715b3 Disable GC inside coroutines on mac OS
(cherry picked from commit 2c53ef1bfe)
2023-04-07 16:21:30 +00:00
John Ericson
11f12253ce Merge pull request #8054 from NixOS/backport-8053-to-2.13-maintenance
[Backport 2.13-maintenance] LocalDerivationGoal: set NIX_ATTRS_*_FILE correctly for sandboxed builds
2023-03-16 18:24:51 -04:00
Linus Heckemann
e697c74269 LocalDerivationGoal: set NIX_ATTRS_*_FILE correctly for sandboxed builds
(cherry picked from commit af4cbdafe7)
2023-03-16 15:04:52 +00:00
Eelco Dolstra
9bf0baa7f4 Bump version 2023-02-27 18:13:04 +01:00
Théophane Hufschmitt
4acc684ef7 Merge pull request #7878 from NixOS/backport-7856-to-2.13-maintenance
[Backport 2.13-maintenance] Wait with making /etc unwritable until after build env setup
2023-02-22 06:52:14 +01:00
Yorick van Pelt
11522a573d Wait with making /etc unwritable until after build env setup
This fixes /etc/nsswitch.conf

(cherry picked from commit bbba49b3e4)
2023-02-21 19:22:35 +00:00
Eelco Dolstra
1083ecbb2b Merge pull request #7833 from hercules-ci/backport-7616-to-2.13-maintenance
Backport 7616 to 2.13 maintenance
2023-02-14 16:53:27 +01:00
Eelco Dolstra
911cd5b45a Merge pull request #7837 from NixOS/backport-7830-to-2.13-maintenance
[Backport 2.13-maintenance] Don't allow writing to /etc
2023-02-14 16:52:50 +01:00
Yorick van Pelt
bca7075edc Make /etc writability conditional on uid-range feature
(cherry picked from commit 49fd72a903)
2023-02-14 15:50:29 +00:00
Yorick van Pelt
459832e5c2 container test: make /etc writable
(cherry picked from commit ad1f61c39b)
2023-02-14 15:50:29 +00:00
Yorick van Pelt
58210e5306 Don't allow writing to /etc
(cherry picked from commit db41f74af3)
2023-02-14 15:50:29 +00:00
Robert Hensing
4ca48e3c7f NarInfoDiskCache: Also test id consistency with updated fields
And clarify test

(cherry picked from commit 19b495a48a)
2023-02-14 14:54:51 +01:00
Robert Hensing
2e31c54ce5 NarInfoDiskCache: Keep BinaryCache.id stable and improve test
Fixes #3898

The entire `BinaryCaches` row used to get replaced after it became
stale according to the `timestamp` column. In a concurrent scenario,
this leads to foreign key conflicts as different instances of the
in-process `state.caches` cache now differ, with the consequence that
the older process still tries to use the `id` number of the old record.

Furthermore, this phenomenon appears to have caused the cache for
actual narinfos to be erased about every week, while the default
ttl for narinfos was supposed to be 30 days.

(cherry picked from commit fb94d5cabd)
2023-02-14 14:54:51 +01:00
Robert Hensing
e404ae5bb6 NarInfoDiskCache: Prepare reproducer for #3898
(cherry picked from commit 2ceece3ef3)
2023-02-14 14:54:51 +01:00
Robert Hensing
80498bd923 NarInfoDiskCacheImpl: Make dbPath a parameter
This allows testing with a clean database.

(cherry picked from commit 79f62d2dda)
2023-02-13 12:02:46 +00:00
Robert Hensing
7a67baf951 NarInfoDiskCache: Rename cacheExists -> upToDateCacheExists
This is slightly more accurate considering that an outdated record
may exist in the persistent cache. Possibly-outdated records are
quite relevant as they may be foreign keys to more recent information
that we want to keep, but we will not return them here.

(cherry picked from commit 29f0b196f4)
2023-02-13 12:02:46 +00:00
Robert Hensing
9f788403cf sqlite.cc: Add SQL tracing
Set environment variable NIX_DEBUG_SQLITE_TRACES=1 to log all sql statements.

(cherry picked from commit 8a0ef5d58e)
2023-02-13 12:02:46 +00:00
Robert Hensing
b6ce4e100a local.mk: Don't log docroot comments
These were accidentally logged and do not need to appear in make's
log output.

(cherry picked from commit 1a86d3e98e)
2023-02-13 12:02:46 +00:00
Robert Hensing
9157f94e77 Merge pull request #7746 from NixOS/backport-7705-to-2.13-maintenance
[Backport 2.13-maintenance] perl: run `initLibStore()` on `openStore()`
2023-02-04 13:39:00 +01:00
Maximilian Bosch
77d8066e83 perl: run initLibStore() on openStore()
Since #7478 it's mandatory that `initLibStore()` is called for store
operations. However that's not the case when running `openStore()` in
Perl using the perl-bindings. That breaks e.g. `hydra-eval-jobset` when
built against Nix 2.13 which uses small portions of the store API.

(cherry picked from commit 51013da921)
2023-02-03 22:33:58 +00:00
Eelco Dolstra
286d212d10 Bump version 2023-01-26 12:42:03 +01:00
Eelco Dolstra
435a16b555 Merge pull request #7694 from NixOS/backport-7685-to-2.13-maintenance
[Backport 2.13-maintenance] Fix the 2.13 changelog
2023-01-25 21:28:02 +01:00
John Ericson
13c00e2969 Update doc/manual/src/release-notes/rl-2.13.md
Co-authored-by: Eelco Dolstra <edolstra@gmail.com>
(cherry picked from commit f465e378c4)
2023-01-25 20:08:59 +00:00
John Ericson
e60a492173 Fix the 2.13 changelog
It is just the new CLI that gets the `^` syntax. The old CLI already has
a (slightly different) `!` syntax.

Fixes #7682

(cherry picked from commit 816031173c)
2023-01-25 20:08:59 +00:00
Robert Hensing
da3faaf8a6 Merge pull request #7687 from hercules-ci/2.13-maintenance-revert-addition-of-property-tests
[2.13-maintenance] revert addition of property tests
2023-01-25 18:54:17 +01:00
Robert Hensing
cdad101bc9 Restore some non-rapidcheck tests 2023-01-25 18:11:36 +01:00
Robert Hensing
3424e1e055 Revert "Try to fix #7669"
We've decided against backporting rapidcheck

This reverts commit e60ee9e90a.
2023-01-25 18:09:10 +01:00
Robert Hensing
9566203d0d Partially revert "Test store paths, with property tests"
This reverts the addition of property tests from commit d111d2ed49.
2023-01-25 11:33:29 +01:00
Robert Hensing
65e690a087 Revert "Add rapidcheck dependency for testing"
This reverts commit c92e51ecec.
2023-01-25 11:27:52 +01:00
Robert Hensing
2e66fe9b1e Merge pull request #7680 from NixOS/backport-7679-to-2.13-maintenance
[Backport 2.13-maintenance] Revert "fixup: remove boehmgc patch"
2023-01-24 17:17:24 +01:00
Robert Hensing
7ddbc12bc8 Update boehmgc-coroutine-sp-fallback.diff
(cherry picked from commit 46054f932b)
2023-01-24 15:27:09 +00:00
Robert Hensing
8f170f98f4 Actually complete the revert
(cherry picked from commit 8270dccf60)
2023-01-24 15:27:09 +00:00
Robert Hensing
46daba3852 Revert "fixup: remove boehmgc patch"
It is still necessary.
Please do your research, or f ask the author, which happens to be me.

An evaluator like this is not an environment where "it compiles, so
it works" will ever hold.

This reverts commit 1c40182b12.

(cherry picked from commit 0664ba0a67)
2023-01-24 15:27:09 +00:00
Robert Hensing
75d33d3e89 Merge pull request #7678 from NixOS/backport-7670-to-2.13-maintenance
[Backport 2.13-maintenance] Try to fix #7669
2023-01-24 13:51:31 +01:00
John Ericson
e60ee9e90a Try to fix #7669
The issue *seems* to be the cross jobs, which are missing the `CXXFLAGS`
needed to get rapidcheck.

PR #6538 would be really nice to resurrect which will prevent the
`configureFlags` from going out of sync between the regular build and
the cross build again.

(cherry picked from commit a91709a604)
2023-01-24 11:44:19 +00:00
Robert Hensing
2c552c66af Merge pull request #7666 from NixOS/backport-7657-to-2.13-maintenance
[Backport 2.13-maintenance] Fix #7655
2023-01-23 16:22:59 +01:00
Robert Hensing
aff2c1c642 Merge pull request #7664 from NixOS/backport-7639-to-2.13-maintenance
[Backport 2.13-maintenance] Test store paths, with property tests, fix bug
2023-01-23 15:58:43 +01:00
John Ericson
af27f2fe00 Fix #7655
We had some local variables left over from the older (more
complicated) implementation of this function. They should all be unused,
but one wasn't by mistake.

Delete them all, and replace the one that was still in use as intended.

(cherry picked from commit 0afdf4084c)
2023-01-23 14:43:23 +00:00
John Ericson
3f58d5a76b Expand tests to reproduce #7655
The original `builtins.getContext` test from
1d757292d0 would have caught this. The
problem is that b30be6b450 adding
`builtins.appendContext` modified that test to make it test too much at
once, rather than adding a separate test.

We now have isolated tests for both functions, and also a property test
showing everything put together (in the form of an eta rule for strings
with context). This is better coverage and properly reproduces the bug.

(cherry picked from commit 88d8f6ac48)
2023-01-23 14:43:23 +00:00
John Ericson
d111d2ed49 Test store paths, with property tests
The property test in fact found a bug: we were excluding numbers!

(cherry picked from commit 018e2571aa)
2023-01-23 14:21:44 +00:00
John Ericson
01b30df520 Better-scope Store forward declarations
(cherry picked from commit 685395332d)
2023-01-23 14:21:44 +00:00
John Ericson
c92e51ecec Add rapidcheck dependency for testing
Property tests are great!

Co-authored-by: Cole Helbling <cole.e.helbling@outlook.com>
(cherry picked from commit 7fe308c2f8)
2023-01-23 14:21:44 +00:00
Eelco Dolstra
291e36b1c0 Merge pull request #7637 from NixOS/backport-7636-to-2.13-maintenance
[Backport 2.13-maintenance] Relase notes: add empty flake registry
2023-01-18 19:37:11 +01:00
Eelco Dolstra
c834fa66b3 Bump version 2023-01-18 19:33:30 +01:00
Lorenzo Manacorda
8cdb15408f Relase notes: add empty flake registry
Introduced in #5420

(cherry picked from commit 913782af4d)
2023-01-18 17:04:47 +00:00
Eelco Dolstra
7304806241 Merge pull request #7635 from NixOS/backport-7631-to-2.13-maintenance
[Backport 2.13-maintenance] OutputSpec: Allow all valid output names
2023-01-18 17:19:37 +01:00
Eelco Dolstra
be0f37ad7b Add test for OutputsSpec::Names
From @Ericson2314.

(cherry picked from commit 75c89c3e5e)
2023-01-18 16:09:36 +00:00
Eelco Dolstra
3de7107312 Fix indentation
(cherry picked from commit 8a3b30822b)
2023-01-18 16:09:36 +00:00
Eelco Dolstra
a7e6bb7fd4 Add some tests for illegal output names
(cherry picked from commit 1ebfa6ba2d)
2023-01-18 16:09:36 +00:00
Eelco Dolstra
9ccde7ec9f OutputSpec: Allow all valid output names
Fixes #7624.

(cherry picked from commit 95cfd50d25)
2023-01-18 16:09:36 +00:00
Eelco Dolstra
7cd4a8f73c Merge pull request #7632 from NixOS/backport-7627-to-2.13-maintenance
[Backport 2.13-maintenance] Restore support for channel: URLs in fetchTarball
2023-01-18 14:28:06 +01:00
Eelco Dolstra
fda0d52e3c Restore support for channel: URLs in fetchTarball
Fixes #7625.

(cherry picked from commit 01f268322a)
2023-01-18 13:26:57 +00:00
Eelco Dolstra
f60d45d661 Merge pull request #7622 from NixOS/backport-7621-to-2.13-maintenance
[Backport 2.13-maintenance] Revert #6204 to fix regression, add nixpkgs/lib/tests as regression test
2023-01-18 11:49:56 +01:00
Robert Hensing
cfee550bec flake.nix: Add nixpkgs/lib/tests as regression test
(cherry picked from commit 620e4fb89b)
2023-01-18 01:27:12 +00:00
Robert Hensing
b050a226b2 Revert "Merge pull request #6204 from layus/coerce-string"
This reverts commit a75b7ba30f, reversing
changes made to 9af16c5f74.

(cherry picked from commit 9b33ef3879)
2023-01-18 01:27:12 +00:00
Eelco Dolstra
973144c986 Bump version 2023-01-17 22:06:55 +01:00
Eelco Dolstra
54ef7e7664 Typo
(cherry picked from commit 3ff9fc0d7d)
2023-01-17 17:44:38 +01:00
John Ericson
b5e1da41f6 Try again to fix aarch64-linux build failure
f419ab48e6 was on the right track, but
there are a few more missing `raw()` calls to fix.

(cherry picked from commit 3965b0f75f)
2023-01-17 16:29:27 +01:00
Eelco Dolstra
193462930c Mark official release 2023-01-17 13:49:52 +01:00
227 changed files with 2578 additions and 6078 deletions

5
.github/CODEOWNERS vendored
View File

@@ -5,7 +5,7 @@
# For documentation on this mechanism, see https://help.github.com/articles/about-codeowners/
# Default reviewers if nothing else matches
* @edolstra
* @edolstra @thufschmitt
# This file
.github/CODEOWNERS @edolstra
@@ -13,6 +13,3 @@
# Public documentation
/doc @fricklerhandwerk
*.md @fricklerhandwerk
# Libstore layer
/src/libstore @thufschmitt

View File

@@ -1,28 +0,0 @@
# Motivation
<!-- Briefly explain what the change is about and why it is desirable. -->
# Context
<!-- Provide context. Reference open issues if available. -->
<!-- Non-trivial change: Briefly outline the implementation strategy. -->
<!-- Invasive change: Discuss alternative designs or approaches you considered. -->
<!-- Large change: Provide instructions to reviewers how to read the diff. -->
# Checklist for maintainers
<!-- Contributors: please leave this as is -->
Maintainers: tick if completed or explain if not relevant
- [ ] agreed on idea
- [ ] agreed on implementation strategy
- [ ] tests, as appropriate
- functional tests - `tests/**.sh`
- unit tests - `src/*/tests`
- integration tests - `tests/nixos/*`
- [ ] documentation in the manual
- [ ] code and comments are self-explanatory
- [ ] commit message explains why the change was made
- [ ] new feature or incompatible change: updated release notes

View File

@@ -21,12 +21,12 @@ jobs:
fetch-depth: 0
- name: Create backport PRs
# should be kept in sync with `version`
uses: zeebe-io/backport-action@v1.2.0
uses: zeebe-io/backport-action@v1.0.1
with:
# Config README: https://github.com/zeebe-io/backport-action#backport-action
github_token: ${{ secrets.GITHUB_TOKEN }}
github_workspace: ${{ github.workspace }}
pull_description: |-
Automatic backport to `${target_branch}`, triggered by a label in #${pull_number}.
Bot-based backport to `${target_branch}`, triggered by a label in #${pull_number}.
# should be kept in sync with `uses`
version: v0.0.5

View File

@@ -11,7 +11,6 @@ jobs:
tests:
needs: [check_secrets]
strategy:
fail-fast: false
matrix:
os: [ubuntu-latest, macos-latest]
runs-on: ${{ matrix.os }}
@@ -20,10 +19,7 @@ jobs:
- uses: actions/checkout@v3
with:
fetch-depth: 0
- uses: cachix/install-nix-action@v22
with:
# The sandbox would otherwise be disabled by default on Darwin
extra_nix_config: "sandbox = true"
- uses: cachix/install-nix-action@v20
- run: echo CACHIX_NAME="$(echo $GITHUB_REPOSITORY-install-tests | tr "[A-Z]/" "[a-z]-")" >> $GITHUB_ENV
- uses: cachix/cachix-action@v12
if: needs.check_secrets.outputs.cachix == 'true'
@@ -62,9 +58,7 @@ jobs:
with:
fetch-depth: 0
- run: echo CACHIX_NAME="$(echo $GITHUB_REPOSITORY-install-tests | tr "[A-Z]/" "[a-z]-")" >> $GITHUB_ENV
- uses: cachix/install-nix-action@v22
with:
install_url: https://releases.nixos.org/nix/nix-2.13.3/install
- uses: cachix/install-nix-action@v20
- uses: cachix/cachix-action@v12
with:
name: '${{ env.CACHIX_NAME }}'
@@ -77,14 +71,13 @@ jobs:
needs: [installer, check_secrets]
if: github.event_name == 'push' && needs.check_secrets.outputs.cachix == 'true'
strategy:
fail-fast: false
matrix:
os: [ubuntu-latest, macos-latest]
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v3
- run: echo CACHIX_NAME="$(echo $GITHUB_REPOSITORY-install-tests | tr "[A-Z]/" "[a-z]-")" >> $GITHUB_ENV
- uses: cachix/install-nix-action@v22
- uses: cachix/install-nix-action@v20
with:
install_url: '${{needs.installer.outputs.installerURL}}'
install_options: "--tarball-url-prefix https://${{ env.CACHIX_NAME }}.cachix.org/serve"
@@ -109,9 +102,7 @@ jobs:
- uses: actions/checkout@v3
with:
fetch-depth: 0
- uses: cachix/install-nix-action@v22
with:
install_url: https://releases.nixos.org/nix/nix-2.13.3/install
- uses: cachix/install-nix-action@v20
- run: echo CACHIX_NAME="$(echo $GITHUB_REPOSITORY-install-tests | tr "[A-Z]/" "[a-z]-")" >> $GITHUB_ENV
- run: echo NIX_VERSION="$(nix --experimental-features 'nix-command flakes' eval .\#default.version | tr -d \")" >> $GITHUB_ENV
- uses: cachix/cachix-action@v12

9
.gitignore vendored
View File

@@ -37,14 +37,14 @@ perl/Makefile.config
/src/libexpr/parser-tab.hh
/src/libexpr/parser-tab.output
/src/libexpr/nix.tbl
/src/libexpr/tests/libnixexpr-tests
/src/libexpr/tests/libexpr-tests
# /src/libstore/
*.gen.*
/src/libstore/tests/libnixstore-tests
/src/libstore/tests/libstore-tests
# /src/libutil/
/src/libutil/tests/libnixutil-tests
/src/libutil/tests/libutil-tests
/src/nix/nix
@@ -75,7 +75,7 @@ perl/Makefile.config
# /tests/
/tests/test-tmp
/tests/common/vars-and-functions.sh
/tests/common.sh
/tests/result*
/tests/restricted-innocent
/tests/shell
@@ -103,7 +103,6 @@ outputs/
*.a
*.o
*.o.tmp
*.so
*.dylib
*.dll

View File

@@ -1 +1 @@
2.14.1
2.13.6

View File

@@ -36,4 +36,4 @@ endif
include mk/lib.mk
GLOBAL_CXXFLAGS += -g -Wall -include config.h -std=c++2a -I src
GLOBAL_CXXFLAGS += -g -Wall -include config.h -std=c++17 -I src

View File

@@ -20,8 +20,8 @@ Information on additional installation methods is available on the [Nix download
## Building And Developing
See our [Hacking guide](https://nixos.org/manual/nix/unstable/contributing/hacking.html) in our manual for instruction on how to
to set up a development environment and build Nix from source.
See our [Hacking guide](https://nixos.org/manual/nix/stable/contributing/hacking.html) in our manual for instruction on how to
build nix from source with nix-build or how to get a development environment.
## Additional Resources

View File

@@ -274,15 +274,6 @@ fi
PKG_CHECK_MODULES([GTEST], [gtest_main])
# Look for rapidcheck.
# No pkg-config yet, https://github.com/emil-e/rapidcheck/issues/302
AC_LANG_PUSH(C++)
AC_CHECK_HEADERS([rapidcheck/gtest.h], [], [], [#include <gtest/gtest.h>])
dnl No good for C++ libs with mangled symbols
dnl AC_CHECK_LIB([rapidcheck], [])
AC_LANG_POP(C++)
# Look for nlohmann/json.
PKG_CHECK_MODULES([NLOHMANN_JSON], [nlohmann_json >= 3.9])

View File

@@ -1,25 +1,3 @@
:root {
--sidebar-width: 23em;
}
h1.menu-title::before {
content: "";
background-image: url("./favicon.svg");
padding: 1.25em;
background-position: center center;
background-size: 2em;
background-repeat: no-repeat;
}
h1.menu-title {
padding: 0.5em;
}
.sidebar .sidebar-scrollbox {
padding: 1em;
}
h1:not(:first-of-type) {
margin-top: 1.3em;
}
@@ -27,7 +5,3 @@ h1:not(:first-of-type) {
h2 {
margin-top: 1em;
}
.hljs-meta {
user-select: none;
}

View File

@@ -108,7 +108,7 @@ doc/manual/generated/man1/nix3-manpages: $(d)/src/command-ref/new-cli
done
@touch $@
$(docdir)/manual/index.html: $(MANUAL_SRCS) $(d)/book.toml $(d)/anchors.jq $(d)/custom.css $(d)/src/SUMMARY.md $(d)/src/command-ref/new-cli $(d)/src/command-ref/conf-file.md $(d)/src/language/builtins.md $(d)/src/favicon.png $(d)/src/favicon.svg
$(docdir)/manual/index.html: $(MANUAL_SRCS) $(d)/book.toml $(d)/anchors.jq $(d)/custom.css $(d)/src/SUMMARY.md $(d)/src/command-ref/new-cli $(d)/src/command-ref/conf-file.md $(d)/src/language/builtins.md
$(trace-gen) \
set -euo pipefail; \
RUST_LOG=warn mdbook build doc/manual -d $(DESTDIR)$(docdir)/manual.tmp 2>&1 \

View File

@@ -1,9 +1,7 @@
// redirect rules for URL fragments (client-side) to prevent link rot.
// this must be done on the client side, as web servers do not see the fragment part of the URL.
// it will only work with JavaScript enabled in the browser, but this is the best we can do here.
// see ./_redirects for path redirects (client-side)
// redirect rules for anchors ensure backwards compatibility of URLs.
// this must be done on the client side, as web servers do not see the anchor part of the URL.
// redirects are declared as follows:
// redirections are declared as follows:
// each entry has as its key a path matching the requested URL path, relative to the mdBook document root.
//
// IMPORTANT: it must specify the full path with file name and suffix
@@ -21,7 +19,6 @@ const redirects = {
"chap-distributed-builds": "advanced-topics/distributed-builds.html",
"chap-post-build-hook": "advanced-topics/post-build-hook.html",
"chap-post-build-hook-caveats": "advanced-topics/post-build-hook.html#implementation-caveats",
"chap-writing-nix-expressions": "language/index.html",
"part-command-ref": "command-ref/command-ref.html",
"conf-allow-import-from-derivation": "command-ref/conf-file.html#conf-allow-import-from-derivation",
"conf-allow-new-privileges": "command-ref/conf-file.html#conf-allow-new-privileges",

View File

@@ -61,12 +61,13 @@
- [Files](command-ref/files.md)
- [nix.conf](command-ref/conf-file.md)
- [Architecture](architecture/architecture.md)
- [Protocols](protocols/protocols.md)
- [Serving Tarball Flakes](protocols/tarball-fetcher.md)
- [Glossary](glossary.md)
- [Contributing](contributing/contributing.md)
- [Hacking](contributing/hacking.md)
- [CLI guideline](contributing/cli-guideline.md)
- [Release Notes](release-notes/release-notes.md)
- [Release 2.14 (2023-02-28)](release-notes/rl-2.14.md)
- [Release 2.13 (2023-01-17)](release-notes/rl-2.13.md)
- [Release 2.12 (2022-12-06)](release-notes/rl-2.12.md)
- [Release 2.11 (2022-08-25)](release-notes/rl-2.11.md)

View File

@@ -1,30 +0,0 @@
# redirect rules for paths (server-side) to prevent link rot.
# see ./redirects.js for redirects based on URL fragments (client-side)
#
# concrete user story this supports:
# - user finds URL to the manual for Nix x.y
# - Nix x.z (z > y) is the most recent release
# - updating the version in the URL will show the right thing
#
# format documentation:
# - https://docs.netlify.com/routing/redirects/#syntax-for-the-redirects-file
# - https://docs.netlify.com/routing/redirects/redirect-options/
#
# conventions:
# - always force (<CODE>!) since this allows re-using file names
# - group related paths to ease readability
# - always append new redirects to the end of the file
# - redirects that should have been there but are missing can be inserted where they belong
/expressions/expression-language /language/ 301!
/expressions/language-values /language/values 301!
/expressions/language-constructs /language/constructs 301!
/expressions/language-operators /language/operators 301!
/expressions/* /language/:splat 301!
/package-management/basic-package-mgmt /command-ref/nix-env 301!
/package-management/channels* /command-ref/nix-channel 301!
/package-management/s3-substituter* /command-ref/new-cli/nix3-help-stores#s3-binary-cache-store 301!

View File

@@ -91,16 +91,3 @@ Most Nix commands interpret the following environment variables:
variable sets the initial size of the heap in bytes. It defaults to
384 MiB. Setting it to a low value reduces memory consumption, but
will increase runtime due to the overhead of garbage collection.
## XDG Base Directory
New Nix commands conform to the [XDG Base Directory Specification], and use the following environment variables to determine locations of various state and configuration files:
- [`XDG_CONFIG_HOME`]{#env-XDG_CONFIG_HOME} (default `~/.config`)
- [`XDG_STATE_HOME`]{#env-XDG_STATE_HOME} (default `~/.local/state`)
- [`XDG_CACHE_HOME`]{#env-XDG_CACHE_HOME} (default `~/.cache`)
Classic Nix commands can also be made to follow this standard using the [`use-xdg-base-directories`] configuration option.
[XDG Base Directory Specification]: https://specifications.freedesktop.org/basedir-spec/basedir-spec-latest.html
[`use-xdg-base-directories`]: ../command-ref/conf-file.md#conf-use-xdg-base-directories

View File

@@ -70,7 +70,7 @@ $ nix-instantiate --eval -E '(import <nixpkgs> {}).lib.version'
# Files
- `${XDG_STATE_HOME-$HOME/.local/state}/nix/profiles/channels`\
- `/nix/var/nix/profiles/per-user/username/channels`\
`nix-channel` uses a `nix-env` profile to keep track of previous
versions of the subscribed channels. Every time you run `nix-channel
--update`, a new channel generation (that is, a symlink to the
@@ -79,7 +79,7 @@ $ nix-instantiate --eval -E '(import <nixpkgs> {}).lib.version'
- `~/.nix-defexpr/channels`\
This is a symlink to
`${XDG_STATE_HOME-$HOME/.local/state}/nix/profiles/channels`. It ensures that
`/nix/var/nix/profiles/per-user/username/channels`. It ensures that
`nix-env` can find your channels. In a multi-user installation, you
may also have `~/.nix-defexpr/channels_root`, which links to the
channels of the root user.

View File

@@ -66,11 +66,11 @@ The operation `--realise` essentially “builds” the specified store
paths. Realisation is a somewhat overloaded term:
- If the store path is a *derivation*, realisation ensures that the
output paths of the derivation are [valid] (i.e.,
output paths of the derivation are [valid](../glossary.md) (i.e.,
the output path and its closure exist in the file system). This
can be done in several ways. First, it is possible that the
outputs are already valid, in which case we are done
immediately. Otherwise, there may be [substitutes]
immediately. Otherwise, there may be [substitutes](../glossary.md)
that produce the outputs (e.g., by downloading them). Finally, the
outputs can be produced by running the build task described
by the derivation.
@@ -82,9 +82,6 @@ paths. Realisation is a somewhat overloaded term:
produced through substitutes. If there are no (successful)
substitutes, realisation fails.
[valid]: ../glossary.md#gloss-validity
[substitutes]: ../glossary.md#gloss-substitute
The output path of each derivation is printed on standard output. (For
non-derivations argument, the argument itself is printed.)
@@ -298,8 +295,8 @@ error: cannot delete path `/nix/store/zq0h41l75vlb4z45kzgjjmsjxvcv1qk7-mesa-6.4'
## Description
The operation `--query` displays information about [store path]s.
The queries are described below. At most one query can be
The operation `--query` displays various bits of information about the
store paths . The queries are described below. At most one query can be
specified. The default query is `--outputs`.
The paths *paths* may also be symlinks from outside of the Nix store, to
@@ -319,12 +316,12 @@ symlink.
## Queries
- `--outputs`\
Prints out the [output path]s of the store
Prints out the [output paths](../glossary.md) of the store
derivations *paths*. These are the paths that will be produced when
the derivation is built.
- `--requisites`; `-R`\
Prints out the [closure] of the given *paths*.
Prints out the [closure](../glossary.md) of the store path *paths*.
This query has one option:
@@ -341,12 +338,10 @@ symlink.
derivation and specifying the option `--include-outputs`.
- `--references`\
Prints the set of [references]s of the store paths
Prints the set of [references](../glossary.md) of the store paths
*paths*, that is, their immediate dependencies. (For *all*
dependencies, use `--requisites`.)
[reference]: ../glossary.md#gloss-reference
- `--referrers`\
Prints the set of *referrers* of the store paths *paths*, that is,
the store paths currently existing in the Nix store that refer to
@@ -361,13 +356,11 @@ symlink.
in the Nix store that are dependent on *paths*.
- `--deriver`; `-d`\
Prints the [deriver] of the store paths *paths*. If
Prints the [deriver](../glossary.md) of the store paths *paths*. If
the path has no deriver (e.g., if it is a source file), or if the
deriver is not known (e.g., in the case of a binary-only
deployment), the string `unknown-deriver` is printed.
[deriver]: ../glossary.md#gloss-deriver
- `--graph`\
Prints the references graph of the store paths *paths* in the format
of the `dot` tool of AT\&T's [Graphviz
@@ -488,7 +481,7 @@ depends on `svn`:
$ nix-store -q --roots $(which svn)
/nix/var/nix/profiles/default-81-link
/nix/var/nix/profiles/default-82-link
/home/eelco/.local/state/nix/profiles/profile-97-link
/nix/var/nix/profiles/per-user/eelco/profile-97-link
```
# Operation `--add`
@@ -633,7 +626,7 @@ written to standard output.
A NAR archive is like a TAR or Zip archive, but it contains only the
information that Nix considers important. For instance, timestamps are
elided because all files in the Nix store have their timestamp set to 1
elided because all files in the Nix store have their timestamp set to 0
anyway. Likewise, all permissions are left out except for the execute
bit, because all files in the Nix store have 444 or 555 permission.

View File

@@ -8,64 +8,25 @@ $ git clone https://github.com/NixOS/nix.git
$ cd nix
```
The following instructions assume you already have some version of Nix installed locally, so that you can use it to set up the development environment. If you don't have it installed, follow the [installation instructions].
[installation instructions]: ../installation/installation.md
## Nix with flakes
This section assumes you are using Nix with [flakes] enabled. See the [next section](#classic-nix) for equivalent instructions which don't require flakes.
[flakes]: ../command-ref/new-cli/nix3-flake.md#description
To build all dependencies and start a shell in which all environment
variables are set up so that those dependencies can be found:
To build Nix for the current operating system/architecture use
```console
$ nix develop
$ nix-build
```
This shell also adds `./outputs/bin/nix` to your `$PATH` so you can run `nix` immediately after building it.
To get a shell with one of the other [supported compilation environments](#compilation-environments):
```console
$ nix develop .#native-clang11StdenvPackages
```
> **Note**
>
> Use `ccacheStdenv` to drastically improve rebuild time.
> By default, [ccache](https://ccache.dev) keeps artifacts in `~/.cache/ccache/`.
To build Nix itself in this shell:
```console
[nix-shell]$ ./bootstrap.sh
[nix-shell]$ ./configure $configureFlags --prefix=$(pwd)/outputs/out
[nix-shell]$ make -j $NIX_BUILD_CORES
```
To install it in `$(pwd)/outputs` and test it:
```console
[nix-shell]$ make install
[nix-shell]$ make installcheck -j $NIX_BUILD_CORES
[nix-shell]$ nix --version
nix (Nix) 2.12
```
To build a release version of Nix:
or if you have a flake-enabled nix:
```console
$ nix build
```
You can also build Nix for one of the [supported target platforms](#target-platforms).
This will build `defaultPackage` attribute defined in the `flake.nix`
file. To build for other platforms add one of the following suffixes to
it: aarch64-linux, i686-linux, x86\_64-darwin, x86\_64-linux. i.e.
## Classic Nix
This section is for Nix without [flakes].
```console
$ nix-build -A defaultPackage.x86_64-linux
```
To build all dependencies and start a shell in which all environment
variables are set up so that those dependencies can be found:
@@ -74,16 +35,27 @@ variables are set up so that those dependencies can be found:
$ nix-shell
```
To get a shell with one of the other [supported compilation environments](#compilation-environments):
or if you have a flake-enabled nix:
```console
$ nix-shell -A devShells.x86_64-linux.native-clang11StdenvPackages
$ nix develop
```
> **Note**
>
> You can use `native-ccacheStdenvPackages` to drastically improve rebuild time.
> By default, [ccache](https://ccache.dev) keeps artifacts in `~/.cache/ccache/`.
To get a shell with a different compilation environment (e.g. stdenv,
gccStdenv, clangStdenv, clang11Stdenv, ccacheStdenv):
```console
$ nix-shell -A devShells.x86_64-linux.clang11StdenvPackages
```
or if you have a flake-enabled nix:
```console
$ nix develop .#clang11StdenvPackages
```
Note: you can use `ccacheStdenv` to drastically improve rebuild
time. By default, ccache keeps artifacts in `~/.cache/ccache/`.
To build Nix itself in this shell:
@@ -99,107 +71,28 @@ To install it in `$(pwd)/outputs` and test it:
[nix-shell]$ make install
[nix-shell]$ make installcheck -j $NIX_BUILD_CORES
[nix-shell]$ ./outputs/out/bin/nix --version
nix (Nix) 2.12
nix (Nix) 3.0
```
To build Nix for the current operating system and CPU architecture use
If you have a flakes-enabled Nix you can replace:
```console
$ nix-build
$ nix-shell
```
You can also build Nix for one of the [supported target platforms](#target-platforms).
## Platforms
As specified in [`flake.nix`], Nix can be built for various platforms:
- `aarch64-linux`
- `i686-linux`
- `x86_64-darwin`
- `x86_64-linux`
[`flake.nix`]: https://github.com/nixos/nix/blob/master/flake.nix
In order to build Nix for a different platform than the one you're currently
on, you need to have some way for your system Nix to build code for that
platform. Common solutions include [remote builders] and [binfmt emulation]
(only supported on NixOS).
[remote builders]: ../advanced-topics/distributed-builds.md
[binfmt emulation]: https://nixos.org/manual/nixos/stable/options.html#opt-boot.binfmt.emulatedSystems
These solutions let Nix perform builds as if you're on the native platform, so
executing the build is as simple as
by:
```console
$ nix build .#packages.aarch64-linux.default
$ nix develop
```
for flake-enabled Nix, or
```console
$ nix-build -A packages.aarch64-linux.default
```
for classic Nix.
You can use any of the other supported platforms in place of `aarch64-linux`.
Cross-compiled builds are available for ARMv6 and ARMv7, and Nix on unsupported platforms can be bootstrapped by adding more `crossSystems` in `flake.nix`.
## Compilation environments
Nix can be compiled using multiple environments:
- `stdenv`: default;
- `gccStdenv`: force the use of `gcc` compiler;
- `clangStdenv`: force the use of `clang` compiler;
- `ccacheStdenv`: enable [ccache], a compiler cache to speed up compilation.
To build with one of those environments, you can use
```console
$ nix build .#nix-ccacheStdenv
```
for flake-enabled Nix, or
```console
$ nix-build -A nix-ccacheStdenv
```
for classic Nix.
You can use any of the other supported environments in place of `nix-ccacheStdenv`.
## Editor integration
The `clangd` LSP server is installed by default on the `clang`-based `devShell`s.
See [supported compilation environments](#compilation-environments) and instructions how to set up a shell [with flakes](#nix-with-flakes) or in [classic Nix](#classic-nix).
To use the LSP with your editor, you first need to [set up `clangd`](https://clangd.llvm.org/installation#project-setup) by running:
```console
make clean && bear -- make -j$NIX_BUILD_CORES install
```
Configure your editor to use the `clangd` from the shell, either by running it inside the development shell, or by using [nix-direnv](https://github.com/nix-community/nix-direnv) and [the appropriate editor plugin](https://github.com/direnv/direnv/wiki#editor-integration).
> **Note**
>
> For some editors (e.g. Visual Studio Code), you may need to install a [special extension](https://open-vsx.org/extension/llvm-vs-code-extensions/vscode-clangd) for the editor to interact with `clangd`.
> Some other editors (e.g. Emacs, Vim) need a plugin to support LSP servers in general (e.g. [lsp-mode](https://github.com/emacs-lsp/lsp-mode) for Emacs and [vim-lsp](https://github.com/prabirshrestha/vim-lsp) for vim).
> Editor-specific setup is typically opinionated, so we will not cover it here in more detail.
## Running tests
### Unit-tests
The unit-tests for each Nix library (`libexpr`, `libstore`, etc..) are defined
under `src/{library_name}/tests` using the
[googletest](https://google.github.io/googletest/) and
[rapidcheck](https://github.com/emil-e/rapidcheck) frameworks.
[googletest](https://google.github.io/googletest/) framework.
You can run the whole testsuite with `make check`, or the tests for a specific component with `make libfoo-tests_RUN`. Finer-grained filtering is also possible using the [--gtest_filter](https://google.github.io/googletest/advanced.html#running-a-subset-of-the-tests) command-line option.
@@ -325,7 +218,7 @@ After the CI run completes, you can check the output to extract the installer UR
5. To generate an install command, plug this `install_url` and your GitHub username into this template:
```console
curl -L <install_url> | sh -s -- --tarball-url-prefix https://<github-username>-nix-install-tests.cachix.org/serve
sh <(curl -L <install_url>) --tarball-url-prefix https://<github-username>-nix-install-tests.cachix.org/serve
```
<!-- #### Manually generating test installers

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.2 KiB

View File

@@ -1 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="587.11" height="516.604" viewBox="0 0 550.416 484.317"><defs><linearGradient id="a"><stop offset="0" style="stop-color:#699ad7;stop-opacity:1"/><stop offset=".243" style="stop-color:#7eb1dd;stop-opacity:1"/><stop offset="1" style="stop-color:#7ebae4;stop-opacity:1"/></linearGradient><linearGradient id="b"><stop offset="0" style="stop-color:#415e9a;stop-opacity:1"/><stop offset=".232" style="stop-color:#4a6baf;stop-opacity:1"/><stop offset="1" style="stop-color:#5277c3;stop-opacity:1"/></linearGradient><linearGradient xlink:href="#a" id="c" x1="200.597" x2="290.087" y1="351.411" y2="506.188" gradientTransform="translate(70.65 -1055.151)" gradientUnits="userSpaceOnUse"/><linearGradient xlink:href="#b" id="e" x1="-584.199" x2="-496.297" y1="782.336" y2="937.714" gradientTransform="translate(864.696 -1491.34)" gradientUnits="userSpaceOnUse"/></defs><g style="display:inline;opacity:1" transform="translate(-132.651 958.04)"><path id="d" d="m309.549-710.388 122.197 211.675-56.157.527-32.624-56.87-32.856 56.566-27.903-.011-14.29-24.69 46.81-80.49-33.23-57.826z" style="opacity:1;fill:url(#c);fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:3;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"/><use xlink:href="#d" width="100%" height="100%" transform="rotate(60 407.112 -715.787)"/><use xlink:href="#d" width="100%" height="100%" transform="rotate(-60 407.312 -715.7)"/><use xlink:href="#d" width="100%" height="100%" transform="rotate(180 407.419 -715.756)"/><path id="f" d="m309.549-710.388 122.197 211.675-56.157.527-32.624-56.87-32.856 56.566-27.903-.011-14.29-24.69 46.81-80.49-33.23-57.826z" style="color:#000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000;solid-opacity:1;fill:url(#e);fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:3;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"/><use xlink:href="#f" width="100%" height="100%" style="display:inline" transform="rotate(120 407.34 -716.084)"/><use xlink:href="#f" width="100%" height="100%" style="display:inline" transform="rotate(-120 407.288 -715.87)"/></g></svg>

Before

Width:  |  Height:  |  Size: 2.5 KiB

View File

@@ -19,24 +19,6 @@
[store derivation]: #gloss-store-derivation
- [instantiate]{#gloss-instantiate}, instantiation\
Translate a [derivation] into a [store derivation].
See [`nix-instantiate`](./command-ref/nix-instantiate.md).
[instantiate]: #gloss-instantiate
- [realise]{#gloss-realise}, realisation\
Ensure a [store path] is [valid][validity].
This means either running the `builder` executable as specified in the corresponding [derivation] or fetching a pre-built [store object] from a [substituter].
See [`nix-build`](./command-ref/nix-build.md) and [`nix-store --realise`](./command-ref/nix-store.md#operation---realise).
See [`nix build`](./command-ref/new-cli/nix3-build.md) (experimental).
[realise]: #gloss-realise
- [content-addressed derivation]{#gloss-content-addressed-derivation}\
A derivation which has the
[`__contentAddressed`](./language/advanced-attributes.md#adv-attr-__contentAddressed)
@@ -119,8 +101,6 @@
copy store objects it doesn't have. For details, see the
[`substituters` option](./command-ref/conf-file.md#conf-substituters).
[substituter]: #gloss-substituter
- [purity]{#gloss-purity}\
The assumption that equal Nix derivations when run always produce
the same output. This cannot be guaranteed in general (e.g., a
@@ -163,25 +143,19 @@
to path `Q`, then `Q` is in the closure of `P`. Further, if `Q`
references `R` then `R` is also in the closure of `P`.
[closure]: #gloss-closure
- [output path]{#gloss-output-path}\
A [store path] produced by a [derivation].
[output path]: #gloss-output-path
- [deriver]{#gloss-deriver}\
The [store derivation] that produced an [output path].
The deriver of an *output path* is the store
derivation that built it.
- [validity]{#gloss-validity}\
A store path is valid if all [store object]s in its [closure] can be read from the [store].
For a local store, this means:
- The store path leads to an existing [store object] in that [store].
- The store path is listed in the Nix database as being valid.
- All paths in the store path's [closure] are valid.
[validity]: #gloss-validity
A store path is considered *valid* if it exists in the file system,
is listed in the Nix database as being valid, and if all paths in
its closure are also valid.
- [user environment]{#gloss-user-env}\
An automatically generated store object that consists of a set of

View File

@@ -27,7 +27,7 @@ Set the environment variable and install Nix
```console
$ export NIX_SSL_CERT_FILE=/etc/ssl/my-certificate-bundle.crt
$ curl -L https://nixos.org/nix/install | sh
$ sh <(curl -L https://nixos.org/nix/install)
```
In the shell profile and rc files (for example, `/etc/bashrc`,
@@ -38,7 +38,7 @@ export NIX_SSL_CERT_FILE=/etc/ssl/my-certificate-bundle.crt
```
> **Note**
>
>
> You must not add the export and then do the install, as the Nix
> installer will detect the presence of Nix configuration, and abort.

View File

@@ -1,38 +1,2 @@
# Installation
This section describes how to install and configure Nix for first-time use.
The current recommended option on Linux and MacOS is [multi-user](#multi-user).
## Multi-user
This installation offers better sharing, improved isolation, and more security
over a single user installation.
This option requires either:
* Linux running systemd, with SELinux disabled
* MacOS
```console
$ bash <(curl -L https://nixos.org/nix/install) --daemon
```
## Single-user
> Single-user is not supported on Mac.
This installation has less requirements than the multi-user install, however it
cannot offer equivalent sharing, isolation, or security.
This option is suitable for systems without systemd.
```console
$ bash <(curl -L https://nixos.org/nix/install) --no-daemon
```
## Distributions
The Nix community maintains installers for several distributions.
They can be found in the [`nix-community/nix-installers`](https://github.com/nix-community/nix-installers) repository.
This section describes how to install and configure Nix for first-time
use.

View File

@@ -3,7 +3,7 @@
The easiest way to install Nix is to run the following command:
```console
$ curl -L https://nixos.org/nix/install | sh
$ sh <(curl -L https://nixos.org/nix/install)
```
This will run the installer interactively (causing it to explain what
@@ -27,7 +27,7 @@ you can authenticate with `sudo`.
To explicitly select a single-user installation on your system:
```console
$ curl -L https://nixos.org/nix/install | sh -s -- --no-daemon
$ sh <(curl -L https://nixos.org/nix/install) --no-daemon
```
This will perform a single-user installation of Nix, meaning that `/nix`
@@ -66,7 +66,7 @@ You can instruct the installer to perform a multi-user installation on
your system:
```console
$ curl -L https://nixos.org/nix/install | sh -s -- --daemon
$ sh <(curl -L https://nixos.org/nix/install) --daemon
```
The multi-user installation of Nix will create build users between the
@@ -287,7 +287,7 @@ These install scripts can be used the same as the main NixOS.org
installation script:
```console
$ curl -L https://nixos.org/nix/install | sh
$ sh <(curl -L https://nixos.org/nix/install)
```
In the same directory of the install script are sha256 sums, and gpg

View File

@@ -207,13 +207,13 @@ Derivations can declare some infrequently used optional attributes.
the hash in either hexadecimal or base-32 notation. (See the
[`nix-hash` command](../command-ref/nix-hash.md) for information
about converting to and from base-32 notation.)
- [`__contentAddressed`]{#adv-attr-__contentAddressed}
If this **experimental** attribute is set to true, then the derivation
outputs will be stored in a content-addressed location rather than the
traditional input-addressed one.
This only has an effect if the `ca-derivations` experimental feature is enabled.
This only has an effect if the `ca-derivation` experimental feature is enabled.
Setting this attribute also requires setting `outputHashMode` and `outputHashAlgo` like for *fixed-output derivations* (see above).
- [`passAsFile`]{#adv-attr-passAsFile}\
@@ -255,78 +255,3 @@ Derivations can declare some infrequently used optional attributes.
> substituted. Thus it is usually a good idea to align `system` with
> `builtins.currentSystem` when setting `allowSubstitutes` to
> `false`. For most trivial derivations this should be the case.
- [`__structuredAttrs`]{#adv-attr-structuredAttrs}\
If the special attribute `__structuredAttrs` is set to `true`, the other derivation
attributes are serialised in JSON format and made available to the
builder via the file `.attrs.json` in the builders temporary
directory. This obviates the need for [`passAsFile`](#adv-attr-passAsFile) since JSON files
have no size restrictions, unlike process environments.
It also makes it possible to tweak derivation settings in a structured way; see
[`outputChecks`](#adv-attr-outputChecks) for example.
As a convenience to Bash builders,
Nix writes a script named `.attrs.sh` to the builders directory
that initialises shell variables corresponding to all attributes
that are representable in Bash. This includes non-nested
(associative) arrays. For example, the attribute `hardening.format = true`
ends up as the Bash associative array element `${hardening[format]}`.
- [`outputChecks`]{#adv-attr-outputChecks}\
When using [structured attributes](#adv-attr-structuredAttrs), the `outputChecks`
attribute allows defining checks per-output.
In addition to
[`allowedReferences`](#adv-attr-allowedReferences), [`allowedRequisites`](#adv-attr-allowedRequisites),
[`disallowedReferences`](#adv-attr-disallowedReferences) and [`disallowedRequisites`](#adv-attr-disallowedRequisites),
the following attributes are available:
- `maxSize` defines the maximum size of the resulting [store object](../glossary.md#gloss-store-object).
- `maxClosureSize` defines the maximum size of the output's closure.
- `ignoreSelfRefs` controls whether self-references should be considered when
checking for allowed references/requisites.
Example:
```nix
__structuredAttrs = true;
outputChecks.out = {
# The closure of 'out' must not be larger than 256 MiB.
maxClosureSize = 256 * 1024 * 1024;
# It must not refer to the C compiler or to the 'dev' output.
disallowedRequisites = [ stdenv.cc "dev" ];
};
outputChecks.dev = {
# The 'dev' output must not be larger than 128 KiB.
maxSize = 128 * 1024;
};
```
- [`unsafeDiscardReferences`]{#adv-attr-unsafeDiscardReferences}\
> **Warning**
> This is an experimental feature.
>
> To enable it, add the following to [nix.conf](../command-ref/conf-file.md):
>
> ```
> extra-experimental-features = discard-references
> ```
When using [structured attributes](#adv-attr-structuredAttrs), the
attribute `unsafeDiscardReferences` is an attribute set with a boolean value for each output name.
If set to `true`, it disables scanning the output for runtime dependencies.
Example:
```nix
__structuredAttrs = true;
unsafeDiscardReferences.out = true;
```
This is useful, for example, when generating self-contained filesystem images with
their own embedded Nix store: hashes found inside such an image refer
to the embedded store and not to the host's Nix store.

View File

@@ -91,7 +91,7 @@ This is an incomplete overview of language features, by example.
<tr>
<td>
`"hello ${ { a = "world"; }.a }"`
`"hello ${ { a = "world" }.a }"`
`"1 2 ${toString 3}"`
@@ -191,12 +191,12 @@ This is an incomplete overview of language features, by example.
<tr>
<td>
`<nixpkgs>`
<nixpkgs>
</td>
<td>
Search path for Nix files. Value determined by [`$NIX_PATH` environment variable](../command-ref/env-common.md#env-NIX_PATH).
Search path. Value determined by [`$NIX_PATH` environment variable](../command-ref/env-common.md#env-NIX_PATH).
</td>
</tr>

View File

@@ -24,7 +24,7 @@
| [Equality] | *expr* `==` *expr* | none | 11 |
| Inequality | *expr* `!=` *expr* | none | 11 |
| Logical conjunction (`AND`) | *bool* `&&` *bool* | left | 12 |
| Logical disjunction (`OR`) | *bool* <code>\|\|</code> *bool* | left | 13 |
| Logical disjunction (`OR`) | *bool* `||` *bool* | left | 13 |
| [Logical implication] | *bool* `->` *bool* | none | 14 |
[string]: ./values.md#type-string
@@ -116,16 +116,16 @@ The result is a string.
[store path]: ../glossary.md#gloss-store-path
[store]: ../glossary.md#gloss-store
[String and path concatenation]: #string-and-path-concatenation
[Path and string concatenation]: #path-and-string-concatenation
## Update
> *attrset1* // *attrset2*
> *attrset1* + *attrset2*
Update [attribute set] *attrset1* with names and values from *attrset2*.
The returned attribute set will have of all the attributes in *attrset1* and *attrset2*.
If an attribute name is present in both, the attribute value from the latter is taken.
The returned attribute set will have of all the attributes in *e1* and *e2*.
If an attribute name is present in both, the attribute value from the former is taken.
[Update]: #update
@@ -133,7 +133,7 @@ If an attribute name is present in both, the attribute value from the latter is
Comparison is
- [arithmetic] for [number]s
- [arithmetic] for [number]s
- lexicographic for [string]s and [path]s
- item-wise lexicographic for [list]s:
elements at the same index in both lists are compared according to their type and skipped if they are equal.

View File

@@ -32,13 +32,13 @@ which should print something like:
Priority: 30
On the client side, you can tell Nix to use your binary cache using
`--substituters`, e.g.:
`--option extra-binary-caches`, e.g.:
```console
$ nix-env -iA nixpkgs.firefox --substituters http://avalon:8080/
$ nix-env -iA nixpkgs.firefox --option extra-binary-caches http://avalon:8080/
```
The option `substituters` tells Nix to use this binary cache in
The option `extra-binary-caches` tells Nix to use this binary cache in
addition to your default caches, such as <https://cache.nixos.org>.
Thus, for any path in the closure of Firefox, Nix will first check if
the path is available on the server `avalon` or another binary caches.
@@ -47,4 +47,4 @@ If not, it will fall back to building from source.
You can also tell Nix to always use your binary cache by adding a line
to the `nix.conf` configuration file like this:
substituters = http://avalon:8080/ https://cache.nixos.org/
binary-caches = http://avalon:8080/ https://cache.nixos.org/

View File

@@ -0,0 +1,4 @@
# Protocols
This chapter documents various developer-facing interfaces provided by
Nix.

View File

@@ -0,0 +1,40 @@
# Serving Tarball Flakes
Tarball flakes are served as regular tarballs via HTTP or the file
system (for `file://` URLs).
An HTTP server can return an "immutable" flakeref appropriate for lock
files. This allows users to specify a tarball flake input in
`flake.nix` that requests the latest version of a flake
(e.g. `https://example.org/hello/latest.tar.gz`), while `flake.lock`
will record a URL whose contents will not change
(e.g. `https://example.org/hello/<revision>.tar.gz`). To do so, the
server must return a `Link` header with the `rel` attribute set to
`immutable`, as follows:
```
Link: <flakeref>; rel="immutable"
```
(Note the required `<` and `>` characters around *flakeref*.)
*flakeref* must be a tarball flakeref. It can contain the tarball flake attributes
`narHash`, `rev`, `revCount` and `lastModified`. If `narHash` is included, its
value must be the NAR hash of the unpacked tarball (as computed via
`nix hash path`). Nix checks the contents of the returned tarball
against the `narHash` attribute. The `rev` and `revCount` attributes
are useful when the tarball flake is a mirror of a fetcher type that
has those attributes, such as Git or GitHub. They are not checked by
Nix.
```
Link: <https://example.org/hello/442793d9ec0584f6a6e82fa253850c8085bb150a.tar.gz
?rev=442793d9ec0584f6a6e82fa253850c8085bb150a
&revCount=835
&narHash=sha256-GUm8Uh/U74zFCwkvt9Mri4DSM%2BmHj3tYhXUkYpiv31M%3D>; rel="immutable"
```
(The linebreaks in this example are for clarity and must not be included in the actual response.)
For tarball flakes, the value of the `lastModified` flake attribute is
defined as the timestamp of the newest file inside the tarball.

View File

@@ -4,16 +4,16 @@ This chapter is for impatient people who don't like reading
documentation. For more in-depth information you are kindly referred
to subsequent chapters.
1. Install Nix by running the following:
1. Install single-user Nix by running the following:
```console
$ curl -L https://nixos.org/nix/install | sh
$ bash <(curl -L https://nixos.org/nix/install)
```
The install script will use `sudo`, so make sure you have sufficient rights.
On Linux, `--daemon` can be omitted for a single-user install.
For other installation methods, see [here](installation/installation.md).
This will install Nix in `/nix`. The install script will create
`/nix` using `sudo`, so make sure you have sufficient rights. (For
other installation methods, see
[here](installation/installation.md).)
1. See what installable packages are currently available in the
channel:

View File

@@ -18,10 +18,6 @@
* Instead of "antiquotation", the more common term [string interpolation](../language/string-interpolation.md) is now used consistently.
Historical release notes were not changed.
* Error traces have been reworked to provide detailed explanations and more
accurate error locations. A short excerpt of the trace is now shown by
default when an error occurs.
* Allow explicitly selecting outputs in a store derivation installable, just like we can do with other sorts of installables.
For example,
```shell-session
@@ -42,3 +38,12 @@
* You can now disable the global flake registry by setting the `flake-registry`
configuration option to an empty string. The same can be achieved at runtime with
`--flake-registry ""`.
* Since 2.13.5, a new function `builtins.readFileType` is available. It is similar to
`builtins.readDir` but acts on a single file or directory.
* Since 2.13.5, the `builtins.readDir` function has been optimized when encountering not-yet-known
file types from POSIX's `readdir`. In such cases the type of each file was
discovered by making multiple syscalls. This change makes these operations
lazy such that these lookups will only be performed if the attribute is used.
This optimization affects a minority of filesystems and operating systems.

View File

@@ -1,22 +0,0 @@
# Release 2.14 (2023-02-28)
* A new function `builtins.readFileType` is available. It is similar to
`builtins.readDir` but acts on a single file or directory.
* In flakes, the `.outPath` attribute of a flake now always refers to
the directory containing the `flake.nix`. This was not the case for
when `flake.nix` was in a subdirectory of e.g. a Git repository.
The root of the source of a flake in a subdirectory is still
available in `.sourceInfo.outPath`.
* In derivations that use structured attributes, you can now use `unsafeDiscardReferences`
to disable scanning a given output for runtime dependencies:
```nix
__structuredAttrs = true;
unsafeDiscardReferences.out = true;
```
This is useful e.g. when generating self-contained filesystem images with
their own embedded Nix store: hashes found inside such an image refer
to the embedded store and not to the host's Nix store.
This requires the `discard-references` experimental feature.

View File

@@ -1,2 +0,0 @@
# Release X.Y (202?-??-??)

361
flake.nix
View File

@@ -8,11 +8,10 @@
outputs = { self, nixpkgs, nixpkgs-regression, lowdown-src }:
let
inherit (nixpkgs) lib;
officialRelease = true;
version = lib.fileContents ./.version + versionSuffix;
version = nixpkgs.lib.fileContents ./.version + versionSuffix;
versionSuffix =
if officialRelease
then ""
@@ -26,42 +25,36 @@
stdenvs = [ "gccStdenv" "clangStdenv" "clang11Stdenv" "stdenv" "libcxxStdenv" "ccacheStdenv" ];
forAllSystems = lib.genAttrs systems;
forAllCrossSystems = lib.genAttrs crossSystems;
forAllStdenvs = f:
lib.listToAttrs
forAllSystems = f: nixpkgs.lib.genAttrs systems (system: f system);
forAllSystemsAndStdenvs = f: forAllSystems (system:
nixpkgs.lib.listToAttrs
(map
(stdenvName: {
name = "${stdenvName}Packages";
value = f stdenvName;
})
stdenvs);
(n:
nixpkgs.lib.nameValuePair "${n}Packages" (
f system n
)) stdenvs
)
);
forAllStdenvs = f: nixpkgs.lib.genAttrs stdenvs (stdenv: f stdenv);
# Memoize nixpkgs for different platforms for efficiency.
nixpkgsFor = forAllSystems
(system: let
make-pkgs = crossSystem: stdenv: import nixpkgs {
inherit system crossSystem;
overlays = [
(overlayFor (p: p.${stdenv}))
];
};
stdenvs = forAllStdenvs (make-pkgs null);
native = stdenvs.stdenvPackages;
in {
inherit stdenvs native;
static = native.pkgsStatic;
cross = forAllCrossSystems (crossSystem: make-pkgs crossSystem "stdenv");
});
nixpkgsFor =
let stdenvsPackages = forAllSystemsAndStdenvs
(system: stdenv:
import nixpkgs {
inherit system;
overlays = [
(overlayFor (p: p.${stdenv}))
];
}
);
in
# Add the `stdenvPackages` at toplevel, both because these are the ones
# we want most of the time and for backwards compatibility
forAllSystems (system: stdenvsPackages.${system} // stdenvsPackages.${system}.stdenvPackages);
commonDeps =
{ pkgs
, isStatic ? pkgs.stdenv.hostPlatform.isStatic
}:
with pkgs; rec {
commonDeps = { pkgs, isStatic ? false }: with pkgs; rec {
# Use "busybox-sandbox-shell" if present,
# if not (legacy) fallback and hope it's sufficient.
sh = pkgs.busybox-sandbox-shell or (busybox.override {
@@ -89,9 +82,7 @@
});
configureFlags =
[
"CXXFLAGS=-I${lib.getDev rapidcheck}/extras/gtest/include"
] ++ lib.optionals stdenv.isLinux [
lib.optionals stdenv.isLinux [
"--with-boost=${boost}/lib"
"--with-sandbox-shell=${sh}/bin/busybox"
]
@@ -125,7 +116,6 @@
boost
lowdown-nix
gtest
rapidcheck
]
++ lib.optionals stdenv.isLinux [libseccomp]
++ lib.optional (stdenv.isLinux || stdenv.isDarwin) libsodium
@@ -151,7 +141,7 @@
};
installScriptFor = systems:
with nixpkgsFor.x86_64-linux.native;
with nixpkgsFor.x86_64-linux;
runCommand "installer-script"
{ buildInputs = [ nix ];
}
@@ -215,9 +205,8 @@
installCheckPhase = "make installcheck -j$NIX_BUILD_CORES -l$NIX_BUILD_CORES";
};
binaryTarball = nix: pkgs:
binaryTarball = buildPackages: nix: pkgs:
let
inherit (pkgs) buildPackages;
inherit (pkgs) cacert;
installerClosureInfo = buildPackages.closureInfo { rootPaths = [ nix cacert ]; };
in
@@ -297,15 +286,7 @@
# Forward from the previous stage as we dont want it to pick the lowdown override
nixUnstable = prev.nixUnstable;
nix =
with final;
with commonDeps {
inherit pkgs;
inherit (currentStdenv.hostPlatform) isStatic;
};
let
canRunInstalled = currentStdenv.buildPlatform.canExecute currentStdenv.hostPlatform;
in currentStdenv.mkDerivation {
nix = with final; with commonDeps { inherit pkgs; }; currentStdenv.mkDerivation {
name = "nix-${version}";
inherit version;
@@ -316,26 +297,24 @@
outputs = [ "out" "dev" "doc" ];
nativeBuildInputs = nativeBuildDeps;
buildInputs = buildDeps
# There have been issues building these dependencies
++ lib.optionals (currentStdenv.hostPlatform == currentStdenv.buildPlatform) awsDeps;
buildInputs = buildDeps ++ awsDeps;
propagatedBuildInputs = propagatedDeps;
disallowedReferences = [ boost ];
preConfigure = lib.optionalString (! currentStdenv.hostPlatform.isStatic)
preConfigure =
''
# Copy libboost_context so we don't get all of Boost in our closure.
# https://github.com/NixOS/nixpkgs/issues/45462
mkdir -p $out/lib
cp -pd ${boost}/lib/{libboost_context*,libboost_thread*,libboost_system*} $out/lib
rm -f $out/lib/*.a
${lib.optionalString currentStdenv.hostPlatform.isLinux ''
${lib.optionalString currentStdenv.isLinux ''
chmod u+w $out/lib/*.so.*
patchelf --set-rpath $out/lib:${currentStdenv.cc.cc.lib}/lib $out/lib/libboost_thread.so.*
''}
${lib.optionalString currentStdenv.hostPlatform.isDarwin ''
${lib.optionalString currentStdenv.isDarwin ''
for LIB in $out/lib/*.dylib; do
chmod u+w $LIB
install_name_tool -id $LIB $LIB
@@ -346,9 +325,7 @@
'';
configureFlags = configureFlags ++
[ "--sysconfdir=/etc" ] ++
lib.optional stdenv.hostPlatform.isStatic "--enable-embedded-sandbox-shell" ++
lib.optional (!canRunInstalled) "--disable-doc-gen";
[ "--sysconfdir=/etc" ];
enableParallelBuilding = true;
@@ -361,10 +338,6 @@
postInstall = ''
mkdir -p $doc/nix-support
echo "doc manual $doc/share/doc/nix/manual" >> $doc/nix-support/hydra-build-products
${lib.optionalString currentStdenv.hostPlatform.isStatic ''
mkdir -p $out/nix-support
echo "file binary-dist $out/bin/nix" >> $out/nix-support/hydra-build-products
''}
${lib.optionalString currentStdenv.isDarwin ''
install_name_tool \
-change ${boost}/lib/libboost_context.dylib \
@@ -375,14 +348,11 @@
doInstallCheck = true;
installCheckFlags = "sysconfdir=$(out)/etc";
installCheckTarget = "installcheck"; # work around buggy detection in stdenv
separateDebugInfo = !currentStdenv.hostPlatform.isStatic;
separateDebugInfo = true;
strictDeps = true;
hardeningDisable = lib.optional stdenv.hostPlatform.isStatic "pie";
passthru.perl-bindings = with final; perl.pkgs.toPerlModule (currentStdenv.mkDerivation {
name = "nix-perl-${version}";
@@ -415,7 +385,7 @@
postUnpack = "sourceRoot=$sourceRoot/perl";
});
meta.platforms = lib.platforms.unix;
meta.platforms = systems;
};
lowdown-nix = with final; currentStdenv.mkDerivation rec {
@@ -436,20 +406,7 @@
};
};
nixos-lib = import (nixpkgs + "/nixos/lib") { };
# https://nixos.org/manual/nixos/unstable/index.html#sec-calling-nixos-tests
runNixOSTestFor = system: test: nixos-lib.runTest {
imports = [ test ];
hostPkgs = nixpkgsFor.${system}.native;
defaults = {
nixpkgs.pkgs = nixpkgsFor.${system}.native;
};
_module.args.nixpkgs = nixpkgs;
};
in {
inherit nixpkgsFor;
# A Nixpkgs overlay that overrides the 'nix' and
# 'nix.perl-bindings' packages.
@@ -458,28 +415,32 @@
hydraJobs = {
# Binary package for various platforms.
build = forAllSystems (system: self.packages.${system}.nix);
build = nixpkgs.lib.genAttrs systems (system: self.packages.${system}.nix);
buildStatic = lib.genAttrs linux64BitSystems (system: self.packages.${system}.nix-static);
buildStatic = nixpkgs.lib.genAttrs linux64BitSystems (system: self.packages.${system}.nix-static);
buildCross = forAllCrossSystems (crossSystem:
lib.genAttrs ["x86_64-linux"] (system: self.packages.${system}."nix-${crossSystem}"));
buildCross = nixpkgs.lib.genAttrs crossSystems (crossSystem:
nixpkgs.lib.genAttrs ["x86_64-linux"] (system: self.packages.${system}."nix-${crossSystem}"));
buildNoGc = forAllSystems (system: self.packages.${system}.nix.overrideAttrs (a: { configureFlags = (a.configureFlags or []) ++ ["--enable-gc=no"];}));
buildNoGc = nixpkgs.lib.genAttrs systems (system: self.packages.${system}.nix.overrideAttrs (a: { configureFlags = (a.configureFlags or []) ++ ["--enable-gc=no"];}));
# Perl bindings for various platforms.
perlBindings = forAllSystems (system: nixpkgsFor.${system}.native.nix.perl-bindings);
perlBindings = nixpkgs.lib.genAttrs systems (system: self.packages.${system}.nix.perl-bindings);
# Binary tarball for various platforms, containing a Nix store
# with the closure of 'nix' package, and the second half of
# the installation script.
binaryTarball = forAllSystems (system: binaryTarball nixpkgsFor.${system}.native.nix nixpkgsFor.${system}.native);
binaryTarball = nixpkgs.lib.genAttrs systems (system: binaryTarball nixpkgsFor.${system} nixpkgsFor.${system}.nix nixpkgsFor.${system});
binaryTarballCross = lib.genAttrs ["x86_64-linux"] (system:
forAllCrossSystems (crossSystem:
binaryTarball
self.packages.${system}."nix-${crossSystem}"
nixpkgsFor.${system}.cross.${crossSystem}));
binaryTarballCross = nixpkgs.lib.genAttrs ["x86_64-linux"] (system: builtins.listToAttrs (map (crossSystem: {
name = crossSystem;
value = let
nixpkgsCross = import nixpkgs {
inherit system crossSystem;
overlays = [ self.overlays.default ];
};
in binaryTarball nixpkgsFor.${system} self.packages.${system}."nix-${crossSystem}" nixpkgsCross;
}) crossSystems));
# The first half of the installation script. This is uploaded
# to https://nixos.org/nix/install. It downloads the binary
@@ -489,11 +450,11 @@
installerScriptForGHA = installScriptFor [ "x86_64-linux" "x86_64-darwin" "armv6l-linux" "armv7l-linux"];
# docker image with Nix inside
dockerImage = lib.genAttrs linux64BitSystems (system: self.packages.${system}.dockerImage);
dockerImage = nixpkgs.lib.genAttrs linux64BitSystems (system: self.packages.${system}.dockerImage);
# Line coverage analysis.
coverage =
with nixpkgsFor.x86_64-linux.native;
with nixpkgsFor.x86_64-linux;
with commonDeps { inherit pkgs; };
releaseTools.coverageAnalysis {
@@ -501,10 +462,6 @@
src = self;
configureFlags = [
"CXXFLAGS=-I${lib.getDev pkgs.rapidcheck}/extras/gtest/include"
];
enableParallelBuilding = true;
nativeBuildInputs = nativeBuildDeps;
@@ -523,29 +480,54 @@
};
# System tests.
tests.authorization = runNixOSTestFor "x86_64-linux" ./tests/nixos/authorization.nix;
tests.remoteBuilds = import ./tests/remote-builds.nix {
system = "x86_64-linux";
inherit nixpkgs;
overlay = self.overlays.default;
};
tests.remoteBuilds = runNixOSTestFor "x86_64-linux" ./tests/nixos/remote-builds.nix;
tests.nix-copy-closure = import ./tests/nix-copy-closure.nix {
system = "x86_64-linux";
inherit nixpkgs;
overlay = self.overlays.default;
};
tests.nix-copy-closure = runNixOSTestFor "x86_64-linux" ./tests/nixos/nix-copy-closure.nix;
tests.nssPreload = (import ./tests/nss-preload.nix rec {
system = "x86_64-linux";
inherit nixpkgs;
overlay = self.overlays.default;
});
tests.nssPreload = runNixOSTestFor "x86_64-linux" ./tests/nixos/nss-preload.nix;
tests.githubFlakes = (import ./tests/github-flakes.nix rec {
system = "x86_64-linux";
inherit nixpkgs;
overlay = self.overlays.default;
});
tests.githubFlakes = runNixOSTestFor "x86_64-linux" ./tests/nixos/github-flakes.nix;
tests.sourcehutFlakes = (import ./tests/sourcehut-flakes.nix rec {
system = "x86_64-linux";
inherit nixpkgs;
overlay = self.overlays.default;
});
tests.sourcehutFlakes = runNixOSTestFor "x86_64-linux" ./tests/nixos/sourcehut-flakes.nix;
tests.containers = (import ./tests/containers.nix rec {
system = "x86_64-linux";
inherit nixpkgs;
overlay = self.overlays.default;
});
tests.containers = runNixOSTestFor "x86_64-linux" ./tests/nixos/containers/containers.nix;
tests.setuid = lib.genAttrs
tests.setuid = nixpkgs.lib.genAttrs
["i686-linux" "x86_64-linux"]
(system: runNixOSTestFor system ./tests/nixos/setuid.nix);
(system:
import ./tests/setuid.nix rec {
inherit nixpkgs system;
overlay = self.overlays.default;
});
# Make sure that nix-env still produces the exact same result
# on a particular version of Nixpkgs.
tests.evalNixpkgs =
with nixpkgsFor.x86_64-linux.native;
with nixpkgsFor.x86_64-linux;
runCommand "eval-nixos" { buildInputs = [ nix ]; }
''
type -p nix-env
@@ -556,18 +538,18 @@
'';
tests.nixpkgsLibTests =
forAllSystems (system:
nixpkgs.lib.genAttrs systems (system:
import (nixpkgs + "/lib/tests/release.nix")
{ pkgs = nixpkgsFor.${system}.native; }
{ pkgs = nixpkgsFor.${system}; }
);
metrics.nixpkgs = import "${nixpkgs-regression}/pkgs/top-level/metrics.nix" {
pkgs = nixpkgsFor.x86_64-linux.native;
pkgs = nixpkgsFor.x86_64-linux;
nixpkgs = nixpkgs-regression;
};
installTests = forAllSystems (system:
let pkgs = nixpkgsFor.${system}.native; in
let pkgs = nixpkgsFor.${system}; in
pkgs.runCommand "install-tests" {
againstSelf = testNixVersions pkgs pkgs.nix pkgs.pkgs.nix;
againstCurrentUnstable =
@@ -592,18 +574,67 @@
perlBindings = self.hydraJobs.perlBindings.${system};
installTests = self.hydraJobs.installTests.${system};
nixpkgsLibTests = self.hydraJobs.tests.nixpkgsLibTests.${system};
} // (lib.optionalAttrs (builtins.elem system linux64BitSystems)) {
} // (nixpkgs.lib.optionalAttrs (builtins.elem system linux64BitSystems)) {
dockerImage = self.hydraJobs.dockerImage.${system};
});
packages = forAllSystems (system: rec {
inherit (nixpkgsFor.${system}.native) nix;
inherit (nixpkgsFor.${system}) nix;
default = nix;
} // (lib.optionalAttrs (builtins.elem system linux64BitSystems) {
nix-static = nixpkgsFor.${system}.static.nix;
} // (nixpkgs.lib.optionalAttrs (builtins.elem system linux64BitSystems) {
nix-static = let
nixpkgs = nixpkgsFor.${system}.pkgsStatic;
in with commonDeps { pkgs = nixpkgs; isStatic = true; }; nixpkgs.stdenv.mkDerivation {
name = "nix-${version}";
src = self;
VERSION_SUFFIX = versionSuffix;
outputs = [ "out" "dev" "doc" ];
nativeBuildInputs = nativeBuildDeps;
buildInputs = buildDeps ++ propagatedDeps;
# Work around pkgsStatic disabling all tests.
# Remove in NixOS 22.11, see https://github.com/NixOS/nixpkgs/pull/140271.
preHook =
''
doCheck=1
doInstallCheck=1
'';
configureFlags =
configureFlags ++
[ "--sysconfdir=/etc"
"--enable-embedded-sandbox-shell"
];
enableParallelBuilding = true;
makeFlags = "profiledir=$(out)/etc/profile.d";
installFlags = "sysconfdir=$(out)/etc";
postInstall = ''
mkdir -p $doc/nix-support
echo "doc manual $doc/share/doc/nix/manual" >> $doc/nix-support/hydra-build-products
mkdir -p $out/nix-support
echo "file binary-dist $out/bin/nix" >> $out/nix-support/hydra-build-products
'';
installCheckFlags = "sysconfdir=$(out)/etc";
stripAllList = ["bin"];
strictDeps = true;
hardeningDisable = [ "pie" ];
};
dockerImage =
let
pkgs = nixpkgsFor.${system}.native;
pkgs = nixpkgsFor.${system};
image = import ./docker.nix { inherit pkgs; tag = version; };
in
pkgs.runCommand
@@ -615,30 +646,65 @@
ln -s ${image} $image
echo "file binary-dist $image" >> $out/nix-support/hydra-build-products
'';
} // builtins.listToAttrs (map
(crossSystem: {
name = "nix-${crossSystem}";
value = nixpkgsFor.${system}.cross.${crossSystem}.nix;
})
crossSystems)
// builtins.listToAttrs (map
(stdenvName: {
name = "nix-${stdenvName}";
value = nixpkgsFor.${system}.stdenvs."${stdenvName}Packages".nix;
})
stdenvs)));
}
devShells = let
makeShell = pkgs: stdenv:
// builtins.listToAttrs (map (crossSystem: {
name = "nix-${crossSystem}";
value = let
nixpkgsCross = import nixpkgs {
inherit system crossSystem;
overlays = [ self.overlays.default ];
};
in with commonDeps { pkgs = nixpkgsCross; }; nixpkgsCross.stdenv.mkDerivation {
name = "nix-${version}";
src = self;
VERSION_SUFFIX = versionSuffix;
outputs = [ "out" "dev" "doc" ];
nativeBuildInputs = nativeBuildDeps;
buildInputs = buildDeps ++ propagatedDeps;
configureFlags = [ "--sysconfdir=/etc" "--disable-doc-gen" ];
enableParallelBuilding = true;
makeFlags = "profiledir=$(out)/etc/profile.d";
doCheck = true;
installFlags = "sysconfdir=$(out)/etc";
postInstall = ''
mkdir -p $doc/nix-support
echo "doc manual $doc/share/doc/nix/manual" >> $doc/nix-support/hydra-build-products
mkdir -p $out/nix-support
echo "file binary-dist $out/bin/nix" >> $out/nix-support/hydra-build-products
'';
doInstallCheck = true;
installCheckFlags = "sysconfdir=$(out)/etc";
};
}) (if system == "x86_64-linux" then crossSystems else [])))
// (builtins.listToAttrs (map (stdenvName:
nixpkgsFor.${system}.lib.nameValuePair
"nix-${stdenvName}"
nixpkgsFor.${system}."${stdenvName}Packages".nix
) stdenvs)));
devShells = forAllSystems (system:
forAllStdenvs (stdenv:
with nixpkgsFor.${system};
with commonDeps { inherit pkgs; };
stdenv.mkDerivation {
nixpkgsFor.${system}.${stdenv}.mkDerivation {
name = "nix";
outputs = [ "out" "dev" "doc" ];
nativeBuildInputs = nativeBuildDeps
++ (lib.optionals stdenv.cc.isClang [ pkgs.bear pkgs.clang-tools ]);
nativeBuildInputs = nativeBuildDeps;
buildInputs = buildDeps ++ propagatedDeps ++ awsDeps;
inherit configureFlags;
@@ -656,21 +722,10 @@
# Make bash completion work.
XDG_DATA_DIRS+=:$out/share
'';
};
in
forAllSystems (system:
let
makeShells = prefix: pkgs:
lib.mapAttrs'
(k: v: lib.nameValuePair "${prefix}-${k}" v)
(forAllStdenvs (stdenvName: makeShell pkgs pkgs.${stdenvName}));
in
(makeShells "native" nixpkgsFor.${system}.native) //
(makeShells "static" nixpkgsFor.${system}.static) //
(forAllCrossSystems (crossSystem: let pkgs = nixpkgsFor.${system}.cross.${crossSystem}; in makeShell pkgs pkgs.stdenv)) //
{
default = self.devShells.${system}.native-stdenvPackages;
}
);
}
)
// { default = self.devShells.${system}.stdenv; }
);
};
}

View File

@@ -1,12 +0,0 @@
# Backporting
To [automatically backport a pull request](https://github.com/NixOS/nix/blob/master/.github/workflows/backport.yml) to a release branch once it's merged, assign it a label of the form [`backport <branch>`](https://github.com/NixOS/nix/labels?q=backport).
Since [GitHub Actions workflows will not trigger other workflows](https://docs.github.com/en/actions/using-workflows/triggering-a-workflow#triggering-a-workflow-from-a-workflow), checks on the automatic backport need to be triggered by another actor.
This is achieved by closing and reopening the backport pull request.
This specifically affects the [`installer_test`] check.
Note that it only runs after the other tests, so it may take a while to appear.
[`installer_test`]: https://github.com/NixOS/nix/blob/895dfc656a21f6252ddf48df0d1f215effa04ecb/.github/workflows/ci.yml#L70-L91

View File

@@ -1,181 +0,0 @@
# Nix release process
## Release artifacts
The release process is intended to create the following for each
release:
* A Git tag
* Binary tarballs in https://releases.nixos.org/?prefix=nix/
* Docker images
* Closures in https://cache.nixos.org
* (Optionally) Updated `fallback-paths.nix` in Nixpkgs
* An updated manual on https://nixos.org/manual/nix/stable/
## Creating a new release from the `master` branch
* Make sure that the [Hydra `master` jobset](https://hydra.nixos.org/jobset/nix/master) succeeds.
* In a checkout of the Nix repo, make sure you're on `master` and run
`git pull`.
* Move the contents of `doc/manual/src/release-notes/rl-next.md`
(except the first line) to
`doc/manual/src/release-notes/rl-$VERSION.md` (where `$VERSION` is
the contents of `.version` *without* the patch level, e.g. `2.12`
rather than `2.12.0`).
* Add a header to `doc/manual/src/release-notes/rl-$VERSION.md` like
```
# Release 2.12 (2022-12-06)
```
* Proof-read / edit / rearrange the release notes. Breaking changes
and highlights should go to the top.
* Add a link to the release notes to `doc/manual/src/SUMMARY.md.in`
(*not* `SUMMARY.md`), e.g.
```
- [Release 2.12 (2022-12-06)](release-notes/rl-2.12.md)
```
* Run
```console
$ git checkout -b release-notes
$ git add doc/manual/src/release-notes/rl-$VERSION.md
$ git commit -a -m 'Release notes'
$ git push --set-upstream $REMOTE release-notes
```
* Create a PR for `release-notes`.
* Wait for the PR to be merged.
* Create a branch for the release:
```console
$ git checkout master
$ git pull
$ git checkout -b $VERSION-maintenance
```
* Mark the release as stable:
```console
$ git cherry-pick f673551e71942a52b6d7ae66af8b67140904a76a
```
This removes the link to `rl-next.md` from the manual and sets
`officialRelease = true` in `flake.nix`.
* Push the release branch:
```console
$ git push --set-upstream origin $VERSION-maintenance
```
* Create a jobset for the release branch on Hydra as follows:
* Go to the jobset of the previous release
(e.g. https://hydra.nixos.org/jobset/nix/maintenance-2.11).
* Select `Actions -> Clone this jobset`.
* Set identifier to `maintenance-$VERSION`.
* Set description to `$VERSION release branch`.
* Set flake URL to `github:NixOS/nix/$VERSION-maintenance`.
* Hit `Create jobset`.
* Wait for the new jobset to evaluate and build. If impatient, go to
the evaluation and select `Actions -> Bump builds to front of
queue`.
* When the jobset evaluation has succeeded building, take note of the
evaluation ID (e.g. `1780832` in
`https://hydra.nixos.org/eval/1780832`).
* Tag the release and upload the release artifacts to
[`releases.nixos.org`](https://releases.nixos.org/) and [Docker Hub](https://hub.docker.com/):
```console
$ IS_LATEST=1 ./maintainers/upload-release.pl <EVAL-ID>
```
Note: `IS_LATEST=1` causes the `latest-release` branch to be
force-updated. This is used by the `nixos.org` website to get the
[latest Nix manual](https://nixos.org/manual/nixpkgs/unstable/).
TODO: This script requires the right AWS credentials. Document.
TODO: This script currently requires a
`/home/eelco/Dev/nix-pristine` and
`/home/eelco/Dev/nixpkgs-pristine`.
TODO: trigger nixos.org netlify: https://docs.netlify.com/configure-builds/build-hooks/
* Prepare for the next point release by editing `.version` to
e.g.
```console
$ echo 2.12.1 > .version
$ git commit -a -m 'Bump version'
$ git push
```
Commit and push this to the maintenance branch.
* Bump the version of `master`:
```console
$ git checkout master
$ git pull
$ NEW_VERSION=2.13.0
$ echo -n $NEW_VERSION > .version
$ git checkout -b bump-$NEW_VERSION
$ git commit -a -m 'Bump version'
$ git push --set-upstream origin bump-$NEW_VERSION
```
Make a pull request and auto-merge it.
* Create a milestone for the next release, move all unresolved issues
from the previous milestone, and close the previous milestone. Set
the date for the next milestone 6 weeks from now.
* Create a backport label
* Post an [announcement on Discourse](https://discourse.nixos.org/c/announcements/8), including the contents of
`rl-$VERSION.md`.
## Creating a point release
* Wait for the desired evaluation of the maintenance jobset to finish
building.
* Run
```console
$ IS_LATEST=1 ./maintainers/upload-release.pl <EVAL-ID>
```
Omit `IS_LATEST=1` when creating a point release that is not on the
most recent stable branch. This prevents `nixos.org` to going back
to an older release.
* Bump the version number of the release branch as above (e.g. to
`2.12.2`).
## Recovering from mistakes
`upload-release.pl` should be idempotent. For instance a wrong `IS_LATEST` value can be fixed that way, by running the script on the actual latest release.

View File

@@ -67,7 +67,6 @@ define build-library
$(1)_LDFLAGS_USE :=
$(1)_LDFLAGS_USE_INSTALLED :=
$(1)_LIB_CLOSURE := $(1)
$$(eval $$(call create-dir, $$(_d)))
@@ -126,15 +125,13 @@ define build-library
$(1)_PATH := $$(_d)/$$($(1)_NAME).a
$$($(1)_PATH): $$($(1)_OBJS) | $$(_d)/
+$$(trace-ld) $(LD) -Ur -o $$(_d)/$$($(1)_NAME).o $$^
$$(trace-ld) $(LD) $$(ifndef $(HOST_DARWIN),-U) -r -o $$(_d)/$$($(1)_NAME).o $$^
$$(trace-ar) $(AR) crs $$@ $$(_d)/$$($(1)_NAME).o
$(1)_LDFLAGS_USE += $$($(1)_PATH) $$($(1)_LDFLAGS) $$(foreach lib, $$($(1)_LIBS), $$($$(lib)_LDFLAGS_USE))
$(1)_LDFLAGS_USE += $$($(1)_PATH) $$($(1)_LDFLAGS)
$(1)_INSTALL_PATH := $$(libdir)/$$($(1)_NAME).a
$(1)_LIB_CLOSURE += $$($(1)_LIBS)
endif
$(1)_LDFLAGS_USE += $$($(1)_LDFLAGS_PROPAGATED)

View File

@@ -3,9 +3,6 @@ programs-list :=
# Build a program with symbolic name $(1). The program is defined by
# various variables prefixed by $(1)_:
#
# - $(1)_NAME: the name of the program (e.g. foo); defaults to
# $(1).
#
# - $(1)_DIR: the directory where the (non-installed) program will be
# placed.
#
@@ -26,12 +23,11 @@ programs-list :=
# - $(1)_INSTALL_DIR: the directory where the program will be
# installed; defaults to $(bindir).
define build-program
$(1)_NAME ?= $(1)
_d := $(buildprefix)$$($(1)_DIR)
_srcs := $$(sort $$(foreach src, $$($(1)_SOURCES), $$(src)))
$(1)_OBJS := $$(addprefix $(buildprefix), $$(addsuffix .o, $$(basename $$(_srcs))))
_libs := $$(foreach lib, $$($(1)_LIBS), $$(foreach lib2, $$($$(lib)_LIB_CLOSURE), $$($$(lib2)_PATH)))
$(1)_PATH := $$(_d)/$$($(1)_NAME)
_libs := $$(foreach lib, $$($(1)_LIBS), $$($$(lib)_PATH))
$(1)_PATH := $$(_d)/$(1)
$$(eval $$(call create-dir, $$(_d)))
@@ -42,7 +38,7 @@ define build-program
ifdef $(1)_INSTALL_DIR
$(1)_INSTALL_PATH := $$($(1)_INSTALL_DIR)/$$($(1)_NAME)
$(1)_INSTALL_PATH := $$($(1)_INSTALL_DIR)/$(1)
$$(eval $$(call create-dir, $$($(1)_INSTALL_DIR)))
@@ -58,7 +54,7 @@ define build-program
else
$(DESTDIR)$$($(1)_INSTALL_PATH): $$($(1)_PATH) | $(DESTDIR)$$($(1)_INSTALL_DIR)/
+$$(trace-install) install -t $(DESTDIR)$$($(1)_INSTALL_DIR) $$<
install -t $(DESTDIR)$$($(1)_INSTALL_DIR) $$<
endif
endif

View File

@@ -1,6 +1,6 @@
makefiles = local.mk
GLOBAL_CXXFLAGS += -g -Wall -std=c++2a -I ../src
GLOBAL_CXXFLAGS += -g -Wall -std=c++17 -I ../src
-include Makefile.config

View File

@@ -136,7 +136,7 @@ EOF
cat <<EOF
$step. Delete the files Nix added to your system:
sudo rm -rf "/etc/nix" "$NIX_ROOT" "$ROOT_HOME/.nix-profile" "$ROOT_HOME/.nix-defexpr" "$ROOT_HOME/.nix-channels" "$ROOT_HOME/.local/state/nix" "$ROOT_HOME/.cache/nix" "$HOME/.nix-profile" "$HOME/.nix-defexpr" "$HOME/.nix-channels" "$HOME/.local/state/nix" "$HOME/.cache/nix"
sudo rm -rf /etc/nix $NIX_ROOT $ROOT_HOME/.nix-profile $ROOT_HOME/.nix-defexpr $ROOT_HOME/.nix-channels $HOME/.nix-profile $HOME/.nix-defexpr $HOME/.nix-channels
and that is it.

View File

@@ -188,8 +188,6 @@ fi
# shellcheck source=./nix-profile.sh.in
. "$nix/etc/profile.d/nix.sh"
NIX_LINK="$HOME/.nix-profile"
if ! "$nix/bin/nix-env" -i "$nix"; then
echo "$0: unable to install Nix into your default profile" >&2
exit 1
@@ -198,7 +196,7 @@ fi
# Install an SSL certificate bundle.
if [ -z "$NIX_SSL_CERT_FILE" ] || ! [ -f "$NIX_SSL_CERT_FILE" ]; then
"$nix/bin/nix-env" -i "$cacert"
export NIX_SSL_CERT_FILE="$NIX_LINK/etc/ssl/certs/ca-bundle.crt"
export NIX_SSL_CERT_FILE="$HOME/.nix-profile/etc/ssl/certs/ca-bundle.crt"
fi
# Subscribe the user to the Nixpkgs channel and fetch it.
@@ -216,8 +214,8 @@ fi
added=
p=
p_sh=$NIX_LINK/etc/profile.d/nix.sh
p_fish=$NIX_LINK/etc/profile.d/nix.fish
p_sh=$HOME/.nix-profile/etc/profile.d/nix.sh
p_fish=$HOME/.nix-profile/etc/profile.d/nix.fish
if [ -z "$NIX_INSTALLER_NO_MODIFY_PROFILE" ]; then
# Make the shell source nix.sh during login.
for i in .bash_profile .bash_login .profile; do

View File

@@ -24,17 +24,12 @@ $1
EOF
}
escape_systemd_env() {
temp_var="${1//\'/\\\'}"
echo "${temp_var//\%/%%}"
}
# Gather all non-empty proxy environment variables into a string
create_systemd_proxy_env() {
vars="http_proxy https_proxy ftp_proxy no_proxy HTTP_PROXY HTTPS_PROXY FTP_PROXY NO_PROXY"
for v in $vars; do
if [ "x${!v:-}" != "x" ]; then
echo "Environment=${v}=$(escape_systemd_env ${!v})"
echo "Environment=${v}=${!v}"
fi
done
}

View File

@@ -2,33 +2,7 @@
if [ -n "${__ETC_PROFILE_NIX_SOURCED:-}" ]; then return; fi
__ETC_PROFILE_NIX_SOURCED=1
NIX_LINK=$HOME/.nix-profile
if [ -n "${XDG_STATE_HOME-}" ]; then
NIX_LINK_NEW="$XDG_STATE_HOME/nix/profile"
else
NIX_LINK_NEW=$HOME/.local/state/nix/profile
fi
if [ -e "$NIX_LINK_NEW" ]; then
NIX_LINK="$NIX_LINK_NEW"
else
if [ -t 2 ] && [ -e "$NIX_LINK_NEW" ]; then
warning="\033[1;35mwarning:\033[0m"
printf "$warning Both %s and legacy %s exist; using the latter.\n" "$NIX_LINK_NEW" "$NIX_LINK" 1>&2
if [ "$(realpath "$NIX_LINK")" = "$(realpath "$NIX_LINK_NEW")" ]; then
printf " Since the profiles match, you can safely delete either of them.\n" 1>&2
else
# This should be an exceptionally rare occasion: the only way to get it would be to
# 1. Update to newer Nix;
# 2. Remove .nix-profile;
# 3. Set the $NIX_LINK_NEW to something other than the default user profile;
# 4. Roll back to older Nix.
# If someone did all that, they can probably figure out how to migrate the profile.
printf "$warning Profiles do not match. You should manually migrate from %s to %s.\n" "$NIX_LINK" "$NIX_LINK_NEW" 1>&2
fi
fi
fi
export NIX_PROFILES="@localstatedir@/nix/profiles/default $NIX_LINK"
export NIX_PROFILES="@localstatedir@/nix/profiles/default $HOME/.nix-profile"
# Set $NIX_SSL_CERT_FILE so that Nixpkgs applications like curl work.
if [ -n "${NIX_SSL_CERT_FILE:-}" ]; then
@@ -60,5 +34,4 @@ else
unset -f check_nix_profiles
fi
export PATH="$NIX_LINK/bin:@localstatedir@/nix/profiles/default/bin:$PATH"
unset NIX_LINK
export PATH="$HOME/.nix-profile/bin:@localstatedir@/nix/profiles/default/bin:$PATH"

View File

@@ -2,35 +2,11 @@ if [ -n "$HOME" ] && [ -n "$USER" ]; then
# Set up the per-user profile.
NIX_LINK="$HOME/.nix-profile"
if [ -n "${XDG_STATE_HOME-}" ]; then
NIX_LINK_NEW="$XDG_STATE_HOME/nix/profile"
else
NIX_LINK_NEW="$HOME/.local/state/nix/profile"
fi
if [ -e "$NIX_LINK_NEW" ]; then
NIX_LINK="$NIX_LINK_NEW"
else
if [ -t 2 ] && [ -e "$NIX_LINK_NEW" ]; then
warning="\033[1;35mwarning:\033[0m"
printf "$warning Both %s and legacy %s exist; using the latter.\n" "$NIX_LINK_NEW" "$NIX_LINK" 1>&2
if [ "$(realpath "$NIX_LINK")" = "$(realpath "$NIX_LINK_NEW")" ]; then
printf " Since the profiles match, you can safely delete either of them.\n" 1>&2
else
# This should be an exceptionally rare occasion: the only way to get it would be to
# 1. Update to newer Nix;
# 2. Remove .nix-profile;
# 3. Set the $NIX_LINK_NEW to something other than the default user profile;
# 4. Roll back to older Nix.
# If someone did all that, they can probably figure out how to migrate the profile.
printf "$warning Profiles do not match. You should manually migrate from %s to %s.\n" "$NIX_LINK" "$NIX_LINK_NEW" 1>&2
fi
fi
fi
NIX_LINK=$HOME/.nix-profile
# Set up environment.
# This part should be kept in sync with nixpkgs:nixos/modules/programs/environment.nix
export NIX_PROFILES="@localstatedir@/nix/profiles/default $NIX_LINK"
export NIX_PROFILES="@localstatedir@/nix/profiles/default $HOME/.nix-profile"
# Set $NIX_SSL_CERT_FILE so that Nixpkgs applications like curl work.
if [ -e /etc/ssl/certs/ca-certificates.crt ]; then # NixOS, Ubuntu, Debian, Gentoo, Arch
@@ -55,5 +31,5 @@ if [ -n "$HOME" ] && [ -n "$USER" ]; then
fi
export PATH="$NIX_LINK/bin:$PATH"
unset NIX_LINK NIX_LINK_NEW
unset NIX_LINK
fi

View File

@@ -72,7 +72,6 @@ static int main_build_remote(int argc, char * * argv)
settings.set(name, value);
}
auto maxBuildJobs = settings.maxBuildJobs;
settings.maxBuildJobs.set("1"); // hack to make tests with local?root= work
initPlugins();
@@ -113,14 +112,10 @@ static int main_build_remote(int argc, char * * argv)
drvPath = store->parseStorePath(readString(source));
auto requiredFeatures = readStrings<std::set<std::string>>(source);
/* It would be possible to build locally after some builds clear out,
so don't show the warning now: */
bool couldBuildLocally = maxBuildJobs > 0
auto canBuildLocally = amWilling
&& ( neededSystem == settings.thisSystem
|| settings.extraPlatforms.get().count(neededSystem) > 0)
&& allSupportedLocally(*store, requiredFeatures);
/* It's possible to build this locally right now: */
bool canBuildLocally = amWilling && couldBuildLocally;
/* Error ignored here, will be caught later */
mkdir(currentLoad.c_str(), 0777);
@@ -219,7 +214,7 @@ static int main_build_remote(int argc, char * * argv)
% concatStringsSep<StringSet>(", ", m.supportedFeatures)
% concatStringsSep<StringSet>(", ", m.mandatoryFeatures);
printMsg(couldBuildLocally ? lvlChatty : lvlWarn, error);
printMsg(canBuildLocally ? lvlChatty : lvlWarn, error);
std::cerr << "# decline\n";
}

View File

@@ -4,7 +4,6 @@
#include "derivations.hh"
#include "nixexpr.hh"
#include "profiles.hh"
#include "repl.hh"
#include <nlohmann/json.hpp>
@@ -122,22 +121,12 @@ ref<EvalState> EvalCommand::getEvalState()
;
if (startReplOnEvalErrors) {
evalState->debugRepl = &AbstractNixRepl::runSimple;
evalState->debugRepl = &runRepl;
};
}
return ref<EvalState>(evalState);
}
MixOperateOnOptions::MixOperateOnOptions()
{
addFlag({
.longName = "derivation",
.description = "Operate on the [store derivation](../../glossary.md#gloss-store-derivation) rather than its outputs.",
.category = installablesCategory,
.handler = {&operateOn, OperateOn::Derivation},
});
}
BuiltPathsCommand::BuiltPathsCommand(bool recursive)
: recursive(recursive)
{
@@ -219,6 +208,20 @@ void StorePathCommand::run(ref<Store> store, std::vector<StorePath> && storePath
run(store, *storePaths.begin());
}
Strings editorFor(const Path & file, uint32_t line)
{
auto editor = getEnv("EDITOR").value_or("cat");
auto args = tokenizeString<Strings>(editor);
if (line > 0 && (
editor.find("emacs") != std::string::npos ||
editor.find("nano") != std::string::npos ||
editor.find("vim") != std::string::npos ||
editor.find("kak") != std::string::npos))
args.push_back(fmt("+%d", line));
args.push_back(file);
return args;
}
MixProfile::MixProfile()
{
addFlag({

View File

@@ -94,8 +94,12 @@ struct SourceExprCommand : virtual Args, MixFlakeOptions
{
std::optional<Path> file;
std::optional<std::string> expr;
bool readOnlyMode = false;
SourceExprCommand();
// FIXME: move this; not all commands (e.g. 'nix run') use it.
OperateOn operateOn = OperateOn::Output;
SourceExprCommand(bool supportReadOnlyMode = false);
std::vector<std::shared_ptr<Installable>> parseInstallables(
ref<Store> store, std::vector<std::string> ss);
@@ -110,11 +114,6 @@ struct SourceExprCommand : virtual Args, MixFlakeOptions
void completeInstallable(std::string_view prefix);
};
struct MixReadOnlyOption : virtual Args
{
MixReadOnlyOption();
};
/* A command that operates on a list of "installables", which can be
store paths, attribute paths, Nix expressions, etc. */
struct InstallablesCommand : virtual Args, SourceExprCommand
@@ -140,7 +139,7 @@ struct InstallableCommand : virtual Args, SourceExprCommand
{
std::shared_ptr<Installable> installable;
InstallableCommand();
InstallableCommand(bool supportReadOnlyMode = false);
void prepare() override;
@@ -154,15 +153,8 @@ private:
std::string _installable{"."};
};
struct MixOperateOnOptions : virtual Args
{
OperateOn operateOn = OperateOn::Output;
MixOperateOnOptions();
};
/* A command that operates on zero or more store paths. */
struct BuiltPathsCommand : InstallablesCommand, virtual MixOperateOnOptions
struct BuiltPathsCommand : public InstallablesCommand
{
private:
@@ -235,6 +227,10 @@ static RegisterCommand registerCommand2(std::vector<std::string> && name)
return RegisterCommand(std::move(name), [](){ return make_ref<T>(); });
}
/* Helper function to generate args that invoke $EDITOR on
filename:lineno. */
Strings editorFor(const Path & file, uint32_t line);
struct MixProfile : virtual StoreCommand
{
std::optional<Path> profile;
@@ -284,4 +280,8 @@ void printClosureDiff(
const StorePath & afterPath,
std::string_view indent);
void runRepl(
ref<EvalState> evalState,
const ValMap & extraEnv);
}

View File

@@ -161,7 +161,7 @@ Path lookupFileArg(EvalState & state, std::string_view s)
{
if (EvalSettings::isPseudoUrl(s)) {
auto storePath = fetchers::downloadTarball(
state.store, EvalSettings::resolvePseudoUrl(s), "source", false).first.storePath;
state.store, EvalSettings::resolvePseudoUrl(s), "source", false).tree.storePath;
return state.store->toRealPath(storePath);
}

View File

@@ -1,20 +0,0 @@
#include "util.hh"
#include "editor-for.hh"
namespace nix {
Strings editorFor(const Path & file, uint32_t line)
{
auto editor = getEnv("EDITOR").value_or("cat");
auto args = tokenizeString<Strings>(editor);
if (line > 0 && (
editor.find("emacs") != std::string::npos ||
editor.find("nano") != std::string::npos ||
editor.find("vim") != std::string::npos ||
editor.find("kak") != std::string::npos))
args.push_back(fmt("+%d", line));
args.push_back(file);
return args;
}
}

View File

@@ -1,11 +0,0 @@
#pragma once
#include "types.hh"
namespace nix {
/* Helper function to generate args that invoke $EDITOR on
filename:lineno. */
Strings editorFor(const Path & file, uint32_t line);
}

View File

@@ -1,109 +0,0 @@
#include "globals.hh"
#include "installable-attr-path.hh"
#include "outputs-spec.hh"
#include "util.hh"
#include "command.hh"
#include "attr-path.hh"
#include "common-eval-args.hh"
#include "derivations.hh"
#include "eval-inline.hh"
#include "eval.hh"
#include "get-drvs.hh"
#include "store-api.hh"
#include "shared.hh"
#include "flake/flake.hh"
#include "eval-cache.hh"
#include "url.hh"
#include "registry.hh"
#include "build-result.hh"
#include <regex>
#include <queue>
#include <nlohmann/json.hpp>
namespace nix {
InstallableAttrPath::InstallableAttrPath(
ref<EvalState> state,
SourceExprCommand & cmd,
Value * v,
const std::string & attrPath,
ExtendedOutputsSpec extendedOutputsSpec)
: InstallableValue(state)
, cmd(cmd)
, v(allocRootValue(v))
, attrPath(attrPath)
, extendedOutputsSpec(std::move(extendedOutputsSpec))
{ }
std::pair<Value *, PosIdx> InstallableAttrPath::toValue(EvalState & state)
{
auto [vRes, pos] = findAlongAttrPath(state, attrPath, *cmd.getAutoArgs(state), **v);
state.forceValue(*vRes, pos);
return {vRes, pos};
}
DerivedPathsWithInfo InstallableAttrPath::toDerivedPaths()
{
auto v = toValue(*state).first;
Bindings & autoArgs = *cmd.getAutoArgs(*state);
DrvInfos drvInfos;
getDerivations(*state, *v, "", autoArgs, drvInfos, false);
// Backward compatibility hack: group results by drvPath. This
// helps keep .all output together.
std::map<StorePath, OutputsSpec> byDrvPath;
for (auto & drvInfo : drvInfos) {
auto drvPath = drvInfo.queryDrvPath();
if (!drvPath)
throw Error("'%s' is not a derivation", what());
auto newOutputs = std::visit(overloaded {
[&](const ExtendedOutputsSpec::Default & d) -> OutputsSpec {
std::set<std::string> outputsToInstall;
for (auto & output : drvInfo.queryOutputs(false, true))
outputsToInstall.insert(output.first);
return OutputsSpec::Names { std::move(outputsToInstall) };
},
[&](const ExtendedOutputsSpec::Explicit & e) -> OutputsSpec {
return e;
},
}, extendedOutputsSpec.raw());
auto [iter, didInsert] = byDrvPath.emplace(*drvPath, newOutputs);
if (!didInsert)
iter->second = iter->second.union_(newOutputs);
}
DerivedPathsWithInfo res;
for (auto & [drvPath, outputs] : byDrvPath)
res.push_back({
.path = DerivedPath::Built {
.drvPath = drvPath,
.outputs = outputs,
},
});
return res;
}
InstallableAttrPath InstallableAttrPath::parse(
ref<EvalState> state,
SourceExprCommand & cmd,
Value * v,
std::string_view prefix,
ExtendedOutputsSpec extendedOutputsSpec)
{
return {
state, cmd, v,
prefix == "." ? "" : std::string { prefix },
extendedOutputsSpec
};
}
}

View File

@@ -1,56 +0,0 @@
#include "globals.hh"
#include "installable-value.hh"
#include "outputs-spec.hh"
#include "util.hh"
#include "command.hh"
#include "attr-path.hh"
#include "common-eval-args.hh"
#include "derivations.hh"
#include "eval-inline.hh"
#include "eval.hh"
#include "get-drvs.hh"
#include "store-api.hh"
#include "shared.hh"
#include "eval-cache.hh"
#include "url.hh"
#include "registry.hh"
#include "build-result.hh"
#include <regex>
#include <queue>
#include <nlohmann/json.hpp>
namespace nix {
class InstallableAttrPath : public InstallableValue
{
SourceExprCommand & cmd;
RootValue v;
std::string attrPath;
ExtendedOutputsSpec extendedOutputsSpec;
InstallableAttrPath(
ref<EvalState> state,
SourceExprCommand & cmd,
Value * v,
const std::string & attrPath,
ExtendedOutputsSpec extendedOutputsSpec);
std::string what() const override { return attrPath; };
std::pair<Value *, PosIdx> toValue(EvalState & state) override;
DerivedPathsWithInfo toDerivedPaths() override;
public:
static InstallableAttrPath parse(
ref<EvalState> state,
SourceExprCommand & cmd,
Value * v,
std::string_view prefix,
ExtendedOutputsSpec extendedOutputsSpec);
};
}

View File

@@ -1,70 +0,0 @@
#include "installable-derived-path.hh"
#include "derivations.hh"
namespace nix {
std::string InstallableDerivedPath::what() const
{
return derivedPath.to_string(*store);
}
DerivedPathsWithInfo InstallableDerivedPath::toDerivedPaths()
{
return {{.path = derivedPath, .info = {} }};
}
std::optional<StorePath> InstallableDerivedPath::getStorePath()
{
return std::visit(overloaded {
[&](const DerivedPath::Built & bfd) {
return bfd.drvPath;
},
[&](const DerivedPath::Opaque & bo) {
return bo.path;
},
}, derivedPath.raw());
}
InstallableDerivedPath InstallableDerivedPath::parse(
ref<Store> store,
std::string_view prefix,
ExtendedOutputsSpec extendedOutputsSpec)
{
auto derivedPath = std::visit(overloaded {
// If the user did not use ^, we treat the output more liberally.
[&](const ExtendedOutputsSpec::Default &) -> DerivedPath {
// First, we accept a symlink chain or an actual store path.
auto storePath = store->followLinksToStorePath(prefix);
// Second, we see if the store path ends in `.drv` to decide what sort
// of derived path they want.
//
// This handling predates the `^` syntax. The `^*` in
// `/nix/store/hash-foo.drv^*` unambiguously means "do the
// `DerivedPath::Built` case", so plain `/nix/store/hash-foo.drv` could
// also unambiguously mean "do the DerivedPath::Opaque` case".
//
// Issue #7261 tracks reconsidering this `.drv` dispatching.
return storePath.isDerivation()
? (DerivedPath) DerivedPath::Built {
.drvPath = std::move(storePath),
.outputs = OutputsSpec::All {},
}
: (DerivedPath) DerivedPath::Opaque {
.path = std::move(storePath),
};
},
// If the user did use ^, we just do exactly what is written.
[&](const ExtendedOutputsSpec::Explicit & outputSpec) -> DerivedPath {
return DerivedPath::Built {
.drvPath = store->parseStorePath(prefix),
.outputs = outputSpec,
};
},
}, extendedOutputsSpec.raw());
return InstallableDerivedPath {
store,
std::move(derivedPath),
};
}
}

View File

@@ -1,28 +0,0 @@
#pragma once
#include "installables.hh"
namespace nix {
struct InstallableDerivedPath : Installable
{
ref<Store> store;
DerivedPath derivedPath;
InstallableDerivedPath(ref<Store> store, DerivedPath && derivedPath)
: store(store), derivedPath(std::move(derivedPath))
{ }
std::string what() const override;
DerivedPathsWithInfo toDerivedPaths() override;
std::optional<StorePath> getStorePath() override;
static InstallableDerivedPath parse(
ref<Store> store,
std::string_view prefix,
ExtendedOutputsSpec extendedOutputsSpec);
};
}

View File

@@ -1,235 +0,0 @@
#include "globals.hh"
#include "installable-flake.hh"
#include "installable-derived-path.hh"
#include "outputs-spec.hh"
#include "util.hh"
#include "command.hh"
#include "attr-path.hh"
#include "common-eval-args.hh"
#include "derivations.hh"
#include "eval-inline.hh"
#include "eval.hh"
#include "get-drvs.hh"
#include "store-api.hh"
#include "shared.hh"
#include "flake/flake.hh"
#include "eval-cache.hh"
#include "url.hh"
#include "registry.hh"
#include "build-result.hh"
#include <regex>
#include <queue>
#include <nlohmann/json.hpp>
namespace nix {
std::vector<std::string> InstallableFlake::getActualAttrPaths()
{
std::vector<std::string> res;
for (auto & prefix : prefixes)
res.push_back(prefix + *attrPaths.begin());
for (auto & s : attrPaths)
res.push_back(s);
return res;
}
Value * InstallableFlake::getFlakeOutputs(EvalState & state, const flake::LockedFlake & lockedFlake)
{
auto vFlake = state.allocValue();
callFlake(state, lockedFlake, *vFlake);
auto aOutputs = vFlake->attrs->get(state.symbols.create("outputs"));
assert(aOutputs);
state.forceValue(*aOutputs->value, [&]() { return aOutputs->value->determinePos(noPos); });
return aOutputs->value;
}
static std::string showAttrPaths(const std::vector<std::string> & paths)
{
std::string s;
for (const auto & [n, i] : enumerate(paths)) {
if (n > 0) s += n + 1 == paths.size() ? " or " : ", ";
s += '\''; s += i; s += '\'';
}
return s;
}
InstallableFlake::InstallableFlake(
SourceExprCommand * cmd,
ref<EvalState> state,
FlakeRef && flakeRef,
std::string_view fragment,
ExtendedOutputsSpec extendedOutputsSpec,
Strings attrPaths,
Strings prefixes,
const flake::LockFlags & lockFlags)
: InstallableValue(state),
flakeRef(flakeRef),
attrPaths(fragment == "" ? attrPaths : Strings{(std::string) fragment}),
prefixes(fragment == "" ? Strings{} : prefixes),
extendedOutputsSpec(std::move(extendedOutputsSpec)),
lockFlags(lockFlags)
{
if (cmd && cmd->getAutoArgs(*state)->size())
throw UsageError("'--arg' and '--argstr' are incompatible with flakes");
}
DerivedPathsWithInfo InstallableFlake::toDerivedPaths()
{
Activity act(*logger, lvlTalkative, actUnknown, fmt("evaluating derivation '%s'", what()));
auto attr = getCursor(*state);
auto attrPath = attr->getAttrPathStr();
if (!attr->isDerivation()) {
// FIXME: use eval cache?
auto v = attr->forceValue();
if (v.type() == nPath) {
PathSet context;
auto storePath = state->copyPathToStore(context, Path(v.path));
return {{
.path = DerivedPath::Opaque {
.path = std::move(storePath),
}
}};
}
else if (v.type() == nString) {
PathSet context;
auto s = state->forceString(v, context, noPos, fmt("while evaluating the flake output attribute '%s'", attrPath));
auto storePath = state->store->maybeParseStorePath(s);
if (storePath && context.count(std::string(s))) {
return {{
.path = DerivedPath::Opaque {
.path = std::move(*storePath),
}
}};
} else
throw Error("flake output attribute '%s' evaluates to the string '%s' which is not a store path", attrPath, s);
}
else
throw Error("flake output attribute '%s' is not a derivation or path", attrPath);
}
auto drvPath = attr->forceDerivation();
std::optional<NixInt> priority;
if (attr->maybeGetAttr(state->sOutputSpecified)) {
} else if (auto aMeta = attr->maybeGetAttr(state->sMeta)) {
if (auto aPriority = aMeta->maybeGetAttr("priority"))
priority = aPriority->getInt();
}
return {{
.path = DerivedPath::Built {
.drvPath = std::move(drvPath),
.outputs = std::visit(overloaded {
[&](const ExtendedOutputsSpec::Default & d) -> OutputsSpec {
std::set<std::string> outputsToInstall;
if (auto aOutputSpecified = attr->maybeGetAttr(state->sOutputSpecified)) {
if (aOutputSpecified->getBool()) {
if (auto aOutputName = attr->maybeGetAttr("outputName"))
outputsToInstall = { aOutputName->getString() };
}
} else if (auto aMeta = attr->maybeGetAttr(state->sMeta)) {
if (auto aOutputsToInstall = aMeta->maybeGetAttr("outputsToInstall"))
for (auto & s : aOutputsToInstall->getListOfStrings())
outputsToInstall.insert(s);
}
if (outputsToInstall.empty())
outputsToInstall.insert("out");
return OutputsSpec::Names { std::move(outputsToInstall) };
},
[&](const ExtendedOutputsSpec::Explicit & e) -> OutputsSpec {
return e;
},
}, extendedOutputsSpec.raw()),
},
.info = {
.priority = priority,
.originalRef = flakeRef,
.resolvedRef = getLockedFlake()->flake.lockedRef,
.attrPath = attrPath,
.extendedOutputsSpec = extendedOutputsSpec,
}
}};
}
std::pair<Value *, PosIdx> InstallableFlake::toValue(EvalState & state)
{
return {&getCursor(state)->forceValue(), noPos};
}
std::vector<ref<eval_cache::AttrCursor>>
InstallableFlake::getCursors(EvalState & state)
{
auto evalCache = openEvalCache(state, getLockedFlake());
auto root = evalCache->getRoot();
std::vector<ref<eval_cache::AttrCursor>> res;
Suggestions suggestions;
auto attrPaths = getActualAttrPaths();
for (auto & attrPath : attrPaths) {
debug("trying flake output attribute '%s'", attrPath);
auto attr = root->findAlongAttrPath(parseAttrPath(state, attrPath));
if (attr) {
res.push_back(ref(*attr));
} else {
suggestions += attr.getSuggestions();
}
}
if (res.size() == 0)
throw Error(
suggestions,
"flake '%s' does not provide attribute %s",
flakeRef,
showAttrPaths(attrPaths));
return res;
}
std::shared_ptr<flake::LockedFlake> InstallableFlake::getLockedFlake() const
{
if (!_lockedFlake) {
flake::LockFlags lockFlagsApplyConfig = lockFlags;
lockFlagsApplyConfig.applyNixConfig = true;
_lockedFlake = std::make_shared<flake::LockedFlake>(lockFlake(*state, flakeRef, lockFlagsApplyConfig));
}
return _lockedFlake;
}
FlakeRef InstallableFlake::nixpkgsFlakeRef() const
{
auto lockedFlake = getLockedFlake();
if (auto nixpkgsInput = lockedFlake->lockFile.findInput({"nixpkgs"})) {
if (auto lockedNode = std::dynamic_pointer_cast<const flake::LockedNode>(nixpkgsInput)) {
debug("using nixpkgs flake '%s'", lockedNode->lockedRef);
return std::move(lockedNode->lockedRef);
}
}
return Installable::nixpkgsFlakeRef();
}
}

View File

@@ -1,50 +0,0 @@
#pragma once
#include "installable-value.hh"
namespace nix {
struct InstallableFlake : InstallableValue
{
FlakeRef flakeRef;
Strings attrPaths;
Strings prefixes;
ExtendedOutputsSpec extendedOutputsSpec;
const flake::LockFlags & lockFlags;
mutable std::shared_ptr<flake::LockedFlake> _lockedFlake;
InstallableFlake(
SourceExprCommand * cmd,
ref<EvalState> state,
FlakeRef && flakeRef,
std::string_view fragment,
ExtendedOutputsSpec extendedOutputsSpec,
Strings attrPaths,
Strings prefixes,
const flake::LockFlags & lockFlags);
std::string what() const override { return flakeRef.to_string() + "#" + *attrPaths.begin(); }
std::vector<std::string> getActualAttrPaths();
Value * getFlakeOutputs(EvalState & state, const flake::LockedFlake & lockedFlake);
DerivedPathsWithInfo toDerivedPaths() override;
std::pair<Value *, PosIdx> toValue(EvalState & state) override;
/* Get a cursor to every attrpath in getActualAttrPaths()
that exists. However if none exists, throw an exception. */
std::vector<ref<eval_cache::AttrCursor>>
getCursors(EvalState & state) override;
std::shared_ptr<flake::LockedFlake> getLockedFlake() const;
FlakeRef nixpkgsFlakeRef() const override;
};
ref<eval_cache::EvalCache> openEvalCache(
EvalState & state,
std::shared_ptr<flake::LockedFlake> lockedFlake);
}

View File

@@ -1,14 +0,0 @@
#pragma once
#include "installables.hh"
namespace nix {
struct InstallableValue : Installable
{
ref<EvalState> state;
InstallableValue(ref<EvalState> state) : state(state) {}
};
}

View File

@@ -1,8 +1,5 @@
#include "globals.hh"
#include "installables.hh"
#include "installable-derived-path.hh"
#include "installable-attr-path.hh"
#include "installable-flake.hh"
#include "outputs-spec.hh"
#include "util.hh"
#include "command.hh"
@@ -147,7 +144,7 @@ void MixFlakeOptions::completionHook()
completeFlakeInput(*prefix);
}
SourceExprCommand::SourceExprCommand()
SourceExprCommand::SourceExprCommand(bool supportReadOnlyMode)
{
addFlag({
.longName = "file",
@@ -169,18 +166,24 @@ SourceExprCommand::SourceExprCommand()
.labels = {"expr"},
.handler = {&expr}
});
}
MixReadOnlyOption::MixReadOnlyOption()
{
addFlag({
.longName = "read-only",
.description =
"Do not instantiate each evaluated derivation. "
"This improves performance, but can cause errors when accessing "
"store paths of derivations during evaluation.",
.handler = {&settings.readOnlyMode, true},
.longName = "derivation",
.description = "Operate on the [store derivation](../../glossary.md#gloss-store-derivation) rather than its outputs.",
.category = installablesCategory,
.handler = {&operateOn, OperateOn::Derivation},
});
if (supportReadOnlyMode) {
addFlag({
.longName = "read-only",
.description =
"Do not instantiate each evaluated derivation. "
"This improves performance, but can cause errors when accessing "
"store paths of derivations during evaluation.",
.handler = {&readOnlyMode, true},
});
}
}
Strings SourceExprCommand::getDefaultFlakeAttrPaths()
@@ -376,9 +379,10 @@ Installable::getCursors(EvalState & state)
ref<eval_cache::AttrCursor>
Installable::getCursor(EvalState & state)
{
/* Although getCursors should return at least one element, in case it doesn't,
bound check to avoid an undefined behavior for vector[0] */
return getCursors(state).at(0);
auto cursors = getCursors(state);
if (cursors.empty())
throw Error("cannot find flake attribute '%s'", what());
return cursors[0];
}
static StorePath getDeriver(
@@ -393,6 +397,143 @@ static StorePath getDeriver(
return *derivers.begin();
}
struct InstallableStorePath : Installable
{
ref<Store> store;
DerivedPath req;
InstallableStorePath(ref<Store> store, DerivedPath && req)
: store(store), req(std::move(req))
{ }
std::string what() const override
{
return req.to_string(*store);
}
DerivedPathsWithInfo toDerivedPaths() override
{
return {{.path = req, .info = {} }};
}
std::optional<StorePath> getStorePath() override
{
return std::visit(overloaded {
[&](const DerivedPath::Built & bfd) {
return bfd.drvPath;
},
[&](const DerivedPath::Opaque & bo) {
return bo.path;
},
}, req.raw());
}
};
struct InstallableAttrPath : InstallableValue
{
SourceExprCommand & cmd;
RootValue v;
std::string attrPath;
ExtendedOutputsSpec extendedOutputsSpec;
InstallableAttrPath(
ref<EvalState> state,
SourceExprCommand & cmd,
Value * v,
const std::string & attrPath,
ExtendedOutputsSpec extendedOutputsSpec)
: InstallableValue(state)
, cmd(cmd)
, v(allocRootValue(v))
, attrPath(attrPath)
, extendedOutputsSpec(std::move(extendedOutputsSpec))
{ }
std::string what() const override { return attrPath; }
std::pair<Value *, PosIdx> toValue(EvalState & state) override
{
auto [vRes, pos] = findAlongAttrPath(state, attrPath, *cmd.getAutoArgs(state), **v);
state.forceValue(*vRes, pos);
return {vRes, pos};
}
DerivedPathsWithInfo toDerivedPaths() override
{
auto v = toValue(*state).first;
Bindings & autoArgs = *cmd.getAutoArgs(*state);
DrvInfos drvInfos;
getDerivations(*state, *v, "", autoArgs, drvInfos, false);
// Backward compatibility hack: group results by drvPath. This
// helps keep .all output together.
std::map<StorePath, OutputsSpec> byDrvPath;
for (auto & drvInfo : drvInfos) {
auto drvPath = drvInfo.queryDrvPath();
if (!drvPath)
throw Error("'%s' is not a derivation", what());
auto newOutputs = std::visit(overloaded {
[&](const ExtendedOutputsSpec::Default & d) -> OutputsSpec {
std::set<std::string> outputsToInstall;
for (auto & output : drvInfo.queryOutputs(false, true))
outputsToInstall.insert(output.first);
return OutputsSpec::Names { std::move(outputsToInstall) };
},
[&](const ExtendedOutputsSpec::Explicit & e) -> OutputsSpec {
return e;
},
}, extendedOutputsSpec.raw());
auto [iter, didInsert] = byDrvPath.emplace(*drvPath, newOutputs);
if (!didInsert)
iter->second = iter->second.union_(newOutputs);
}
DerivedPathsWithInfo res;
for (auto & [drvPath, outputs] : byDrvPath)
res.push_back({
.path = DerivedPath::Built {
.drvPath = drvPath,
.outputs = outputs,
},
});
return res;
}
};
std::vector<std::string> InstallableFlake::getActualAttrPaths()
{
std::vector<std::string> res;
for (auto & prefix : prefixes)
res.push_back(prefix + *attrPaths.begin());
for (auto & s : attrPaths)
res.push_back(s);
return res;
}
Value * InstallableFlake::getFlakeOutputs(EvalState & state, const flake::LockedFlake & lockedFlake)
{
auto vFlake = state.allocValue();
callFlake(state, lockedFlake, *vFlake);
auto aOutputs = vFlake->attrs->get(state.symbols.create("outputs"));
assert(aOutputs);
state.forceValue(*aOutputs->value, [&]() { return aOutputs->value->determinePos(noPos); });
return aOutputs->value;
}
ref<eval_cache::EvalCache> openEvalCache(
EvalState & state,
std::shared_ptr<flake::LockedFlake> lockedFlake)
@@ -413,7 +554,7 @@ ref<eval_cache::EvalCache> openEvalCache(
auto vFlake = state.allocValue();
flake::callFlake(state, *lockedFlake, *vFlake);
state.forceAttrs(*vFlake, noPos, "while parsing cached flake data");
state.forceAttrs(*vFlake, noPos);
auto aOutputs = vFlake->attrs->get(state.symbols.create("outputs"));
assert(aOutputs);
@@ -422,11 +563,214 @@ ref<eval_cache::EvalCache> openEvalCache(
});
}
static std::string showAttrPaths(const std::vector<std::string> & paths)
{
std::string s;
for (const auto & [n, i] : enumerate(paths)) {
if (n > 0) s += n + 1 == paths.size() ? " or " : ", ";
s += '\''; s += i; s += '\'';
}
return s;
}
InstallableFlake::InstallableFlake(
SourceExprCommand * cmd,
ref<EvalState> state,
FlakeRef && flakeRef,
std::string_view fragment,
ExtendedOutputsSpec extendedOutputsSpec,
Strings attrPaths,
Strings prefixes,
const flake::LockFlags & lockFlags)
: InstallableValue(state),
flakeRef(flakeRef),
attrPaths(fragment == "" ? attrPaths : Strings{(std::string) fragment}),
prefixes(fragment == "" ? Strings{} : prefixes),
extendedOutputsSpec(std::move(extendedOutputsSpec)),
lockFlags(lockFlags)
{
if (cmd && cmd->getAutoArgs(*state)->size())
throw UsageError("'--arg' and '--argstr' are incompatible with flakes");
}
DerivedPathsWithInfo InstallableFlake::toDerivedPaths()
{
Activity act(*logger, lvlTalkative, actUnknown, fmt("evaluating derivation '%s'", what()));
auto attr = getCursor(*state);
auto attrPath = attr->getAttrPathStr();
if (!attr->isDerivation()) {
// FIXME: use eval cache?
auto v = attr->forceValue();
if (v.type() == nPath) {
PathSet context;
auto storePath = state->copyPathToStore(context, Path(v.path));
return {{
.path = DerivedPath::Opaque {
.path = std::move(storePath),
}
}};
}
else if (v.type() == nString) {
PathSet context;
auto s = state->forceString(v, context, noPos);
auto storePath = state->store->maybeParseStorePath(s);
if (storePath && context.count(std::string(s))) {
return {{
.path = DerivedPath::Opaque {
.path = std::move(*storePath),
}
}};
} else
throw Error("flake output attribute '%s' evaluates to the string '%s' which is not a store path", attrPath, s);
}
else
throw Error("flake output attribute '%s' is not a derivation or path", attrPath);
}
auto drvPath = attr->forceDerivation();
std::optional<NixInt> priority;
if (attr->maybeGetAttr(state->sOutputSpecified)) {
} else if (auto aMeta = attr->maybeGetAttr(state->sMeta)) {
if (auto aPriority = aMeta->maybeGetAttr("priority"))
priority = aPriority->getInt();
}
return {{
.path = DerivedPath::Built {
.drvPath = std::move(drvPath),
.outputs = std::visit(overloaded {
[&](const ExtendedOutputsSpec::Default & d) -> OutputsSpec {
std::set<std::string> outputsToInstall;
if (auto aOutputSpecified = attr->maybeGetAttr(state->sOutputSpecified)) {
if (aOutputSpecified->getBool()) {
if (auto aOutputName = attr->maybeGetAttr("outputName"))
outputsToInstall = { aOutputName->getString() };
}
} else if (auto aMeta = attr->maybeGetAttr(state->sMeta)) {
if (auto aOutputsToInstall = aMeta->maybeGetAttr("outputsToInstall"))
for (auto & s : aOutputsToInstall->getListOfStrings())
outputsToInstall.insert(s);
}
if (outputsToInstall.empty())
outputsToInstall.insert("out");
return OutputsSpec::Names { std::move(outputsToInstall) };
},
[&](const ExtendedOutputsSpec::Explicit & e) -> OutputsSpec {
return e;
},
}, extendedOutputsSpec.raw()),
},
.info = {
.priority = priority,
.originalRef = flakeRef,
.resolvedRef = getLockedFlake()->flake.lockedRef,
.attrPath = attrPath,
.extendedOutputsSpec = extendedOutputsSpec,
}
}};
}
std::pair<Value *, PosIdx> InstallableFlake::toValue(EvalState & state)
{
return {&getCursor(state)->forceValue(), noPos};
}
std::vector<ref<eval_cache::AttrCursor>>
InstallableFlake::getCursors(EvalState & state)
{
auto evalCache = openEvalCache(state,
std::make_shared<flake::LockedFlake>(lockFlake(state, flakeRef, lockFlags)));
auto root = evalCache->getRoot();
std::vector<ref<eval_cache::AttrCursor>> res;
for (auto & attrPath : getActualAttrPaths()) {
auto attr = root->findAlongAttrPath(parseAttrPath(state, attrPath));
if (attr) res.push_back(ref(*attr));
}
return res;
}
ref<eval_cache::AttrCursor> InstallableFlake::getCursor(EvalState & state)
{
auto lockedFlake = getLockedFlake();
auto cache = openEvalCache(state, lockedFlake);
auto root = cache->getRoot();
Suggestions suggestions;
auto attrPaths = getActualAttrPaths();
for (auto & attrPath : attrPaths) {
debug("trying flake output attribute '%s'", attrPath);
auto attrOrSuggestions = root->findAlongAttrPath(
parseAttrPath(state, attrPath),
true
);
if (!attrOrSuggestions) {
suggestions += attrOrSuggestions.getSuggestions();
continue;
}
return *attrOrSuggestions;
}
throw Error(
suggestions,
"flake '%s' does not provide attribute %s",
flakeRef,
showAttrPaths(attrPaths));
}
std::shared_ptr<flake::LockedFlake> InstallableFlake::getLockedFlake() const
{
if (!_lockedFlake) {
flake::LockFlags lockFlagsApplyConfig = lockFlags;
lockFlagsApplyConfig.applyNixConfig = true;
_lockedFlake = std::make_shared<flake::LockedFlake>(lockFlake(*state, flakeRef, lockFlagsApplyConfig));
}
return _lockedFlake;
}
FlakeRef InstallableFlake::nixpkgsFlakeRef() const
{
auto lockedFlake = getLockedFlake();
if (auto nixpkgsInput = lockedFlake->lockFile.findInput({"nixpkgs"})) {
if (auto lockedNode = std::dynamic_pointer_cast<const flake::LockedNode>(nixpkgsInput)) {
debug("using nixpkgs flake '%s'", lockedNode->lockedRef);
return std::move(lockedNode->lockedRef);
}
}
return Installable::nixpkgsFlakeRef();
}
std::vector<std::shared_ptr<Installable>> SourceExprCommand::parseInstallables(
ref<Store> store, std::vector<std::string> ss)
{
std::vector<std::shared_ptr<Installable>> result;
if (readOnlyMode) {
settings.readOnlyMode = true;
}
if (file || expr) {
if (file && expr)
throw UsageError("'--file' and '--expr' are exclusive");
@@ -452,8 +796,9 @@ std::vector<std::shared_ptr<Installable>> SourceExprCommand::parseInstallables(
auto [prefix, extendedOutputsSpec] = ExtendedOutputsSpec::parse(s);
result.push_back(
std::make_shared<InstallableAttrPath>(
InstallableAttrPath::parse(
state, *this, vFile, prefix, extendedOutputsSpec)));
state, *this, vFile,
prefix == "." ? "" : std::string { prefix },
extendedOutputsSpec));
}
} else {
@@ -466,10 +811,41 @@ std::vector<std::shared_ptr<Installable>> SourceExprCommand::parseInstallables(
auto prefix = std::move(prefix_);
auto extendedOutputsSpec = std::move(extendedOutputsSpec_);
if (prefix.find('/') != std::string::npos) {
auto found = prefix.find('/');
if (found != std::string::npos) {
try {
result.push_back(std::make_shared<InstallableDerivedPath>(
InstallableDerivedPath::parse(store, prefix, extendedOutputsSpec)));
auto derivedPath = std::visit(overloaded {
// If the user did not use ^, we treat the output more liberally.
[&](const ExtendedOutputsSpec::Default &) -> DerivedPath {
// First, we accept a symlink chain or an actual store path.
auto storePath = store->followLinksToStorePath(prefix);
// Second, we see if the store path ends in `.drv` to decide what sort
// of derived path they want.
//
// This handling predates the `^` syntax. The `^*` in
// `/nix/store/hash-foo.drv^*` unambiguously means "do the
// `DerivedPath::Built` case", so plain `/nix/store/hash-foo.drv` could
// also unambiguously mean "do the DerivedPath::Opaque` case".
//
// Issue #7261 tracks reconsidering this `.drv` dispatching.
return storePath.isDerivation()
? (DerivedPath) DerivedPath::Built {
.drvPath = std::move(storePath),
.outputs = OutputsSpec::All {},
}
: (DerivedPath) DerivedPath::Opaque {
.path = std::move(storePath),
};
},
// If the user did use ^, we just do exactly what is written.
[&](const ExtendedOutputsSpec::Explicit & outputSpec) -> DerivedPath {
return DerivedPath::Built {
.drvPath = store->parseStorePath(prefix),
.outputs = outputSpec,
};
},
}, extendedOutputsSpec.raw());
result.push_back(std::make_shared<InstallableStorePath>(store, std::move(derivedPath)));
continue;
} catch (BadStorePath &) {
} catch (...) {
@@ -705,8 +1081,8 @@ void InstallablesCommand::prepare()
installables = load();
}
Installables InstallablesCommand::load()
{
Installables InstallablesCommand::load() {
Installables installables;
if (_installables.empty() && useDefaultInstallables())
// FIXME: commands like "nix profile install" should not have a
// default, probably.
@@ -716,13 +1092,16 @@ Installables InstallablesCommand::load()
std::vector<std::string> InstallablesCommand::getFlakesForCompletion()
{
if (_installables.empty() && useDefaultInstallables())
return {"."};
if (_installables.empty()) {
if (useDefaultInstallables())
return {"."};
return {};
}
return _installables;
}
InstallableCommand::InstallableCommand()
: SourceExprCommand()
InstallableCommand::InstallableCommand(bool supportReadOnlyMode)
: SourceExprCommand(supportReadOnlyMode)
{
expectArgs({
.label = "installable",

View File

@@ -103,13 +103,9 @@ struct Installable
return {};
}
/* Get a cursor to each value this Installable could refer to. However
if none exists, throw exception instead of returning empty vector. */
virtual std::vector<ref<eval_cache::AttrCursor>>
getCursors(EvalState & state);
/* Get the first and most preferred cursor this Installable could refer
to, or throw an exception if none exists. */
virtual ref<eval_cache::AttrCursor>
getCursor(EvalState & state);
@@ -161,4 +157,58 @@ struct Installable
typedef std::vector<std::shared_ptr<Installable>> Installables;
struct InstallableValue : Installable
{
ref<EvalState> state;
InstallableValue(ref<EvalState> state) : state(state) {}
};
struct InstallableFlake : InstallableValue
{
FlakeRef flakeRef;
Strings attrPaths;
Strings prefixes;
ExtendedOutputsSpec extendedOutputsSpec;
const flake::LockFlags & lockFlags;
mutable std::shared_ptr<flake::LockedFlake> _lockedFlake;
InstallableFlake(
SourceExprCommand * cmd,
ref<EvalState> state,
FlakeRef && flakeRef,
std::string_view fragment,
ExtendedOutputsSpec extendedOutputsSpec,
Strings attrPaths,
Strings prefixes,
const flake::LockFlags & lockFlags);
std::string what() const override { return flakeRef.to_string() + "#" + *attrPaths.begin(); }
std::vector<std::string> getActualAttrPaths();
Value * getFlakeOutputs(EvalState & state, const flake::LockedFlake & lockedFlake);
DerivedPathsWithInfo toDerivedPaths() override;
std::pair<Value *, PosIdx> toValue(EvalState & state) override;
/* Get a cursor to every attrpath in getActualAttrPaths() that
exists. */
std::vector<ref<eval_cache::AttrCursor>>
getCursors(EvalState & state) override;
/* Get a cursor to the first attrpath in getActualAttrPaths() that
exists, or throw an exception with suggestions if none exists. */
ref<eval_cache::AttrCursor> getCursor(EvalState & state) override;
std::shared_ptr<flake::LockedFlake> getLockedFlake() const;
FlakeRef nixpkgsFlakeRef() const override;
};
ref<eval_cache::EvalCache> openEvalCache(
EvalState & state,
std::shared_ptr<flake::LockedFlake> lockedFlake);
}

View File

@@ -6,7 +6,7 @@ libcmd_DIR := $(d)
libcmd_SOURCES := $(wildcard $(d)/*.cc)
libcmd_CXXFLAGS += -I src/libutil -I src/libstore -I src/libexpr -I src/libmain -I src/libfetchers
libcmd_CXXFLAGS += -I src/libutil -I src/libstore -I src/libexpr -I src/libmain -I src/libfetchers -I src/nix
libcmd_LDFLAGS = $(EDITLINE_LIBS) $(LOWDOWN_LIBS) -pthread

View File

@@ -6,4 +6,4 @@ Name: Nix
Description: Nix Package Manager
Version: @PACKAGE_VERSION@
Libs: -L${libdir} -lnixcmd
Cflags: -I${includedir}/nix -std=c++2a
Cflags: -I${includedir}/nix -std=c++17

View File

@@ -19,8 +19,6 @@ extern "C" {
}
#endif
#include "repl.hh"
#include "ansicolor.hh"
#include "shared.hh"
#include "eval.hh"
@@ -33,9 +31,7 @@ extern "C" {
#include "get-drvs.hh"
#include "derivations.hh"
#include "globals.hh"
#include "flake/flake.hh"
#include "flake/lockfile.hh"
#include "editor-for.hh"
#include "command.hh"
#include "finally.hh"
#include "markdown.hh"
#include "local-fs-store.hh"
@@ -49,16 +45,18 @@ extern "C" {
namespace nix {
struct NixRepl
: AbstractNixRepl
#if HAVE_BOEHMGC
, gc
: gc
#endif
{
std::string curDir;
ref<EvalState> state;
Bindings * autoArgs;
size_t debugTraceIndex;
Strings loadedFiles;
typedef std::vector<std::pair<Value*,std::string>> AnnotatedValues;
std::function<AnnotatedValues()> getValues;
const static int envSize = 32768;
@@ -71,11 +69,8 @@ struct NixRepl
NixRepl(const Strings & searchPath, nix::ref<Store> store,ref<EvalState> state,
std::function<AnnotatedValues()> getValues);
virtual ~NixRepl();
void mainLoop() override;
void initEnv() override;
~NixRepl();
void mainLoop();
StringSet completePrefix(const std::string & prefix);
bool getLine(std::string & input, const std::string & prompt);
StorePath getDerivationPath(Value & v);
@@ -83,6 +78,7 @@ struct NixRepl
void loadFile(const Path & path);
void loadFlake(const std::string & flakeRef);
void initEnv();
void loadFiles();
void reloadFiles();
void addAttrsToScope(Value & attrs);
@@ -96,6 +92,7 @@ struct NixRepl
std::ostream & printValue(std::ostream & str, Value & v, unsigned int maxDepth, ValuesSeen & seen);
};
std::string removeWhitespace(std::string s)
{
s = chomp(s);
@@ -107,7 +104,7 @@ std::string removeWhitespace(std::string s)
NixRepl::NixRepl(const Strings & searchPath, nix::ref<Store> store, ref<EvalState> state,
std::function<NixRepl::AnnotatedValues()> getValues)
: AbstractNixRepl(state)
: state(state)
, debugTraceIndex(0)
, getValues(getValues)
, staticEnv(new StaticEnv(false, state->staticBaseEnv.get()))
@@ -400,7 +397,7 @@ StringSet NixRepl::completePrefix(const std::string & prefix)
Expr * e = parseString(expr);
Value v;
e->eval(*state, *env, v);
state->forceAttrs(v, noPos, "while evaluating an attrset for the purpose of completion (this error should not be displayed; file an issue?)");
state->forceAttrs(v, noPos);
for (auto & i : *v.attrs) {
std::string_view name = state->symbols[i.name];
@@ -593,7 +590,7 @@ bool NixRepl::processLine(std::string line)
const auto [path, line] = [&] () -> std::pair<Path, uint32_t> {
if (v.type() == nPath || v.type() == nString) {
PathSet context;
auto path = state->coerceToPath(noPos, v, context, "while evaluating the filename to edit");
auto path = state->coerceToPath(noPos, v, context);
return {path, 0};
} else if (v.isLambda()) {
auto pos = state->positions[v.lambda.fun->pos];
@@ -842,7 +839,7 @@ void NixRepl::loadFiles()
void NixRepl::addAttrsToScope(Value & attrs)
{
state->forceAttrs(attrs, [&]() { return attrs.determinePos(noPos); }, "while evaluating an attribute set to be merged in the global scope");
state->forceAttrs(attrs, [&]() { return attrs.determinePos(noPos); });
if (displ + attrs.attrs->size() >= envSize)
throw Error("environment full; cannot add more variables");
@@ -947,7 +944,7 @@ std::ostream & NixRepl::printValue(std::ostream & str, Value & v, unsigned int m
Bindings::iterator i = v.attrs->find(state->sDrvPath);
PathSet context;
if (i != v.attrs->end())
str << state->store->printStorePath(state->coerceToStorePath(i->pos, *i->value, context, "while evaluating the drvPath of a derivation"));
str << state->store->printStorePath(state->coerceToStorePath(i->pos, *i->value, context));
else
str << "???";
str << "»";
@@ -1032,22 +1029,8 @@ std::ostream & NixRepl::printValue(std::ostream & str, Value & v, unsigned int m
return str;
}
std::unique_ptr<AbstractNixRepl> AbstractNixRepl::create(
const Strings & searchPath, nix::ref<Store> store, ref<EvalState> state,
std::function<AnnotatedValues()> getValues)
{
return std::make_unique<NixRepl>(
searchPath,
openStore(),
state,
getValues
);
}
void AbstractNixRepl::runSimple(
ref<EvalState> evalState,
void runRepl(
ref<EvalState>evalState,
const ValMap & extraEnv)
{
auto getValues = [&]()->NixRepl::AnnotatedValues{
@@ -1071,4 +1054,91 @@ void AbstractNixRepl::runSimple(
repl->mainLoop();
}
struct CmdRepl : InstallablesCommand
{
CmdRepl() {
evalSettings.pureEval = false;
}
void prepare() override
{
if (!settings.isExperimentalFeatureEnabled(Xp::ReplFlake) && !(file) && this->_installables.size() >= 1) {
warn("future versions of Nix will require using `--file` to load a file");
if (this->_installables.size() > 1)
warn("more than one input file is not currently supported");
auto filePath = this->_installables[0].data();
file = std::optional(filePath);
_installables.front() = _installables.back();
_installables.pop_back();
}
installables = InstallablesCommand::load();
}
std::vector<std::string> files;
Strings getDefaultFlakeAttrPaths() override
{
return {""};
}
bool useDefaultInstallables() override
{
return file.has_value() or expr.has_value();
}
bool forceImpureByDefault() override
{
return true;
}
std::string description() override
{
return "start an interactive environment for evaluating Nix expressions";
}
std::string doc() override
{
return
#include "repl.md"
;
}
void run(ref<Store> store) override
{
auto state = getEvalState();
auto getValues = [&]()->NixRepl::AnnotatedValues{
auto installables = load();
NixRepl::AnnotatedValues values;
for (auto & installable: installables){
auto what = installable->what();
if (file){
auto [val, pos] = installable->toValue(*state);
auto what = installable->what();
state->forceValue(*val, pos);
auto autoArgs = getAutoArgs(*state);
auto valPost = state->allocValue();
state->autoCallFunction(*autoArgs, *val, *valPost);
state->forceValue(*valPost, pos);
values.push_back( {valPost, what });
} else {
auto [val, pos] = installable->toValue(*state);
values.push_back( {val, what} );
}
}
return values;
};
auto repl = std::make_unique<NixRepl>(
searchPath,
openStore(),
state,
getValues
);
repl->autoArgs = getAutoArgs(*repl->state);
repl->initEnv();
repl->mainLoop();
}
};
static auto rCmdRepl = registerCommand<CmdRepl>("repl");
}

View File

@@ -1,39 +0,0 @@
#pragma once
#include "eval.hh"
#if HAVE_BOEHMGC
#define GC_INCLUDE_NEW
#include <gc/gc_cpp.h>
#endif
namespace nix {
struct AbstractNixRepl
{
ref<EvalState> state;
Bindings * autoArgs;
AbstractNixRepl(ref<EvalState> state)
: state(state)
{ }
virtual ~AbstractNixRepl()
{ }
typedef std::vector<std::pair<Value*,std::string>> AnnotatedValues;
static std::unique_ptr<AbstractNixRepl> create(
const Strings & searchPath, nix::ref<Store> store, ref<EvalState> state,
std::function<AnnotatedValues()> getValues);
static void runSimple(
ref<EvalState> evalState,
const ValMap & extraEnv);
virtual void initEnv() = 0;
virtual void mainLoop() = 0;
};
}

View File

@@ -118,7 +118,7 @@ std::pair<std::string, uint32_t> findPackageFilename(EvalState & state, Value &
// FIXME: is it possible to extract the Pos object instead of doing this
// toString + parsing?
auto pos = state.forceString(*v2, noPos, "while evaluating the 'meta.position' attribute of a derivation");
auto pos = state.forceString(*v2);
auto colon = pos.rfind(':');
if (colon == std::string::npos)

View File

@@ -385,7 +385,7 @@ Value & AttrCursor::getValue()
if (!_value) {
if (parent) {
auto & vParent = parent->first->getValue();
root->state.forceAttrs(vParent, noPos, "while searching for an attribute");
root->state.forceAttrs(vParent, noPos);
auto attr = vParent.attrs->get(parent->second);
if (!attr)
throw Error("attribute '%s' is unexpectedly missing", getAttrPathStr());
@@ -571,14 +571,14 @@ std::string AttrCursor::getString()
debug("using cached string attribute '%s'", getAttrPathStr());
return s->first;
} else
root->state.error("'%s' is not a string", getAttrPathStr()).debugThrow<TypeError>();
root->state.debugThrowLastTrace(TypeError("'%s' is not a string", getAttrPathStr()));
}
}
auto & v = forceValue();
if (v.type() != nString && v.type() != nPath)
root->state.error("'%s' is not a string but %s", getAttrPathStr()).debugThrow<TypeError>();
root->state.debugThrowLastTrace(TypeError("'%s' is not a string but %s", getAttrPathStr(), showType(v.type())));
return v.type() == nString ? v.string.s : v.path;
}
@@ -613,7 +613,7 @@ string_t AttrCursor::getStringWithContext()
return *s;
}
} else
root->state.error("'%s' is not a string", getAttrPathStr()).debugThrow<TypeError>();
root->state.debugThrowLastTrace(TypeError("'%s' is not a string", getAttrPathStr()));
}
}
@@ -624,7 +624,7 @@ string_t AttrCursor::getStringWithContext()
else if (v.type() == nPath)
return {v.path, {}};
else
root->state.error("'%s' is not a string but %s", getAttrPathStr()).debugThrow<TypeError>();
root->state.debugThrowLastTrace(TypeError("'%s' is not a string but %s", getAttrPathStr(), showType(v.type())));
}
bool AttrCursor::getBool()
@@ -637,14 +637,14 @@ bool AttrCursor::getBool()
debug("using cached Boolean attribute '%s'", getAttrPathStr());
return *b;
} else
root->state.error("'%s' is not a Boolean", getAttrPathStr()).debugThrow<TypeError>();
root->state.debugThrowLastTrace(TypeError("'%s' is not a Boolean", getAttrPathStr()));
}
}
auto & v = forceValue();
if (v.type() != nBool)
root->state.error("'%s' is not a Boolean", getAttrPathStr()).debugThrow<TypeError>();
root->state.debugThrowLastTrace(TypeError("'%s' is not a Boolean", getAttrPathStr()));
return v.boolean;
}
@@ -696,7 +696,7 @@ std::vector<std::string> AttrCursor::getListOfStrings()
std::vector<std::string> res;
for (auto & elem : v.listItems())
res.push_back(std::string(root->state.forceStringNoCtx(*elem, noPos, "while evaluating an attribute for caching")));
res.push_back(std::string(root->state.forceStringNoCtx(*elem)));
if (root->db)
cachedValue = {root->db->setListOfStrings(getKey(), res), res};
@@ -714,14 +714,14 @@ std::vector<Symbol> AttrCursor::getAttrs()
debug("using cached attrset attribute '%s'", getAttrPathStr());
return *attrs;
} else
root->state.error("'%s' is not an attribute set", getAttrPathStr()).debugThrow<TypeError>();
root->state.debugThrowLastTrace(TypeError("'%s' is not an attribute set", getAttrPathStr()));
}
}
auto & v = forceValue();
if (v.type() != nAttrs)
root->state.error("'%s' is not an attribute set", getAttrPathStr()).debugThrow<TypeError>();
root->state.debugThrowLastTrace(TypeError("'%s' is not an attribute set", getAttrPathStr()));
std::vector<Symbol> attrs;
for (auto & attr : *getValue().attrs)

View File

@@ -103,36 +103,33 @@ void EvalState::forceValue(Value & v, Callable getPos)
else if (v.isApp())
callFunction(*v.app.left, *v.app.right, v, noPos);
else if (v.isBlackhole())
error("infinite recursion encountered").atPos(getPos()).template debugThrow<EvalError>();
throwEvalError(getPos(), "infinite recursion encountered");
}
[[gnu::always_inline]]
inline void EvalState::forceAttrs(Value & v, const PosIdx pos, std::string_view errorCtx)
inline void EvalState::forceAttrs(Value & v, const PosIdx pos)
{
forceAttrs(v, [&]() { return pos; }, errorCtx);
forceAttrs(v, [&]() { return pos; });
}
template <typename Callable>
[[gnu::always_inline]]
inline void EvalState::forceAttrs(Value & v, Callable getPos, std::string_view errorCtx)
inline void EvalState::forceAttrs(Value & v, Callable getPos)
{
forceValue(v, noPos);
if (v.type() != nAttrs) {
PosIdx pos = getPos();
error("value is %1% while a set was expected", showType(v)).withTrace(pos, errorCtx).debugThrow<TypeError>();
}
forceValue(v, getPos);
if (v.type() != nAttrs)
throwTypeError(getPos(), "value is %1% while a set was expected", v);
}
[[gnu::always_inline]]
inline void EvalState::forceList(Value & v, const PosIdx pos, std::string_view errorCtx)
inline void EvalState::forceList(Value & v, const PosIdx pos)
{
forceValue(v, noPos);
if (!v.isList()) {
error("value is %1% while a list was expected", showType(v)).withTrace(pos, errorCtx).debugThrow<TypeError>();
}
forceValue(v, pos);
if (!v.isList())
throwTypeError(pos, "value is %1% while a list was expected", v);
}

View File

@@ -11,9 +11,7 @@
#include <algorithm>
#include <chrono>
#include <iostream>
#include <cstring>
#include <optional>
#include <unistd.h>
#include <sys/time.h>
#include <sys/resource.h>
@@ -320,7 +318,7 @@ static Symbol getName(const AttrName & name, EvalState & state, Env & env)
} else {
Value nameValue;
name.expr->eval(state, env, nameValue);
state.forceStringNoCtx(nameValue, noPos, "while evaluating an attribute name");
state.forceStringNoCtx(nameValue);
return state.symbols.create(nameValue.string.s);
}
}
@@ -441,44 +439,6 @@ static Strings parseNixPath(const std::string & s)
return res;
}
ErrorBuilder & ErrorBuilder::atPos(PosIdx pos)
{
info.errPos = state.positions[pos];
return *this;
}
ErrorBuilder & ErrorBuilder::withTrace(PosIdx pos, const std::string_view text)
{
info.traces.push_front(Trace{ .pos = state.positions[pos], .hint = hintformat(std::string(text)), .frame = false });
return *this;
}
ErrorBuilder & ErrorBuilder::withFrameTrace(PosIdx pos, const std::string_view text)
{
info.traces.push_front(Trace{ .pos = state.positions[pos], .hint = hintformat(std::string(text)), .frame = true });
return *this;
}
ErrorBuilder & ErrorBuilder::withSuggestions(Suggestions & s)
{
info.suggestions = s;
return *this;
}
ErrorBuilder & ErrorBuilder::withFrame(const Env & env, const Expr & expr)
{
// NOTE: This is abusing side-effects.
// TODO: check compatibility with nested debugger calls.
state.debugTraces.push_front(DebugTrace {
.pos = nullptr,
.expr = expr,
.env = env,
.hint = hintformat("Fake frame for debugging purposes"),
.isError = true
});
return *this;
}
EvalState::EvalState(
const Strings & _searchPath,
@@ -711,7 +671,25 @@ void EvalState::addConstant(const std::string & name, Value * v)
Value * EvalState::addPrimOp(const std::string & name,
size_t arity, PrimOpFun primOp)
{
return addPrimOp(PrimOp { .fun = primOp, .arity = arity, .name = name });
auto name2 = name.substr(0, 2) == "__" ? name.substr(2) : name;
auto sym = symbols.create(name2);
/* Hack to make constants lazy: turn them into a application of
the primop to a dummy value. */
if (arity == 0) {
auto vPrimOp = allocValue();
vPrimOp->mkPrimOp(new PrimOp { .fun = primOp, .arity = 1, .name = name2 });
Value v;
v.mkApp(vPrimOp, vPrimOp);
return addConstant(name, v);
}
Value * v = allocValue();
v->mkPrimOp(new PrimOp { .fun = primOp, .arity = arity, .name = name2 });
staticBaseEnv->vars.emplace_back(symbols.create(name), baseEnvDispl);
baseEnv.values[baseEnvDispl++] = v;
baseEnv.values[0]->attrs->push_back(Attr(sym, v));
return v;
}
@@ -889,14 +867,176 @@ void EvalState::runDebugRepl(const Error * error, const Env & env, const Expr &
}
}
/* Every "format" object (even temporary) takes up a few hundred bytes
of stack space, which is a real killer in the recursive
evaluator. So here are some helper functions for throwing
exceptions. */
void EvalState::throwEvalError(const PosIdx pos, const char * s, Env & env, Expr & expr)
{
debugThrow(EvalError({
.msg = hintfmt(s),
.errPos = positions[pos]
}), env, expr);
}
void EvalState::throwEvalError(const PosIdx pos, const char * s)
{
debugThrowLastTrace(EvalError({
.msg = hintfmt(s),
.errPos = positions[pos]
}));
}
void EvalState::throwEvalError(const char * s, const std::string & s2)
{
debugThrowLastTrace(EvalError(s, s2));
}
void EvalState::throwEvalError(const PosIdx pos, const Suggestions & suggestions, const char * s,
const std::string & s2, Env & env, Expr & expr)
{
debugThrow(EvalError(ErrorInfo{
.msg = hintfmt(s, s2),
.errPos = positions[pos],
.suggestions = suggestions,
}), env, expr);
}
void EvalState::throwEvalError(const PosIdx pos, const char * s, const std::string & s2)
{
debugThrowLastTrace(EvalError({
.msg = hintfmt(s, s2),
.errPos = positions[pos]
}));
}
void EvalState::throwEvalError(const PosIdx pos, const char * s, const std::string & s2, Env & env, Expr & expr)
{
debugThrow(EvalError({
.msg = hintfmt(s, s2),
.errPos = positions[pos]
}), env, expr);
}
void EvalState::throwEvalError(const char * s, const std::string & s2,
const std::string & s3)
{
debugThrowLastTrace(EvalError({
.msg = hintfmt(s, s2, s3),
.errPos = positions[noPos]
}));
}
void EvalState::throwEvalError(const PosIdx pos, const char * s, const std::string & s2,
const std::string & s3)
{
debugThrowLastTrace(EvalError({
.msg = hintfmt(s, s2, s3),
.errPos = positions[pos]
}));
}
void EvalState::throwEvalError(const PosIdx pos, const char * s, const std::string & s2,
const std::string & s3, Env & env, Expr & expr)
{
debugThrow(EvalError({
.msg = hintfmt(s, s2, s3),
.errPos = positions[pos]
}), env, expr);
}
void EvalState::throwEvalError(const PosIdx p1, const char * s, const Symbol sym, const PosIdx p2, Env & env, Expr & expr)
{
// p1 is where the error occurred; p2 is a position mentioned in the message.
debugThrow(EvalError({
.msg = hintfmt(s, symbols[sym], positions[p2]),
.errPos = positions[p1]
}), env, expr);
}
void EvalState::throwTypeError(const PosIdx pos, const char * s, const Value & v)
{
debugThrowLastTrace(TypeError({
.msg = hintfmt(s, showType(v)),
.errPos = positions[pos]
}));
}
void EvalState::throwTypeError(const PosIdx pos, const char * s, const Value & v, Env & env, Expr & expr)
{
debugThrow(TypeError({
.msg = hintfmt(s, showType(v)),
.errPos = positions[pos]
}), env, expr);
}
void EvalState::throwTypeError(const PosIdx pos, const char * s)
{
debugThrowLastTrace(TypeError({
.msg = hintfmt(s),
.errPos = positions[pos]
}));
}
void EvalState::throwTypeError(const PosIdx pos, const char * s, const ExprLambda & fun,
const Symbol s2, Env & env, Expr &expr)
{
debugThrow(TypeError({
.msg = hintfmt(s, fun.showNamePos(*this), symbols[s2]),
.errPos = positions[pos]
}), env, expr);
}
void EvalState::throwTypeError(const PosIdx pos, const Suggestions & suggestions, const char * s,
const ExprLambda & fun, const Symbol s2, Env & env, Expr &expr)
{
debugThrow(TypeError(ErrorInfo {
.msg = hintfmt(s, fun.showNamePos(*this), symbols[s2]),
.errPos = positions[pos],
.suggestions = suggestions,
}), env, expr);
}
void EvalState::throwTypeError(const char * s, const Value & v, Env & env, Expr &expr)
{
debugThrow(TypeError({
.msg = hintfmt(s, showType(v)),
.errPos = positions[expr.getPos()],
}), env, expr);
}
void EvalState::throwAssertionError(const PosIdx pos, const char * s, const std::string & s1, Env & env, Expr &expr)
{
debugThrow(AssertionError({
.msg = hintfmt(s, s1),
.errPos = positions[pos]
}), env, expr);
}
void EvalState::throwUndefinedVarError(const PosIdx pos, const char * s, const std::string & s1, Env & env, Expr &expr)
{
debugThrow(UndefinedVarError({
.msg = hintfmt(s, s1),
.errPos = positions[pos]
}), env, expr);
}
void EvalState::throwMissingArgumentError(const PosIdx pos, const char * s, const std::string & s1, Env & env, Expr &expr)
{
debugThrow(MissingArgumentError({
.msg = hintfmt(s, s1),
.errPos = positions[pos]
}), env, expr);
}
void EvalState::addErrorTrace(Error & e, const char * s, const std::string & s2) const
{
e.addTrace(nullptr, s, s2);
}
void EvalState::addErrorTrace(Error & e, const PosIdx pos, const char * s, const std::string & s2, bool frame) const
void EvalState::addErrorTrace(Error & e, const PosIdx pos, const char * s, const std::string & s2) const
{
e.addTrace(positions[pos], hintfmt(s, s2), frame);
e.addTrace(positions[pos], s, s2);
}
static std::unique_ptr<DebugTraceStacker> makeDebugTraceStacker(
@@ -973,7 +1113,7 @@ inline Value * EvalState::lookupVar(Env * env, const ExprVar & var, bool noEval)
if (env->type == Env::HasWithExpr) {
if (noEval) return 0;
Value * v = allocValue();
evalAttrs(*env->up, (Expr *) env->values[0], *v, noPos, "<borked>");
evalAttrs(*env->up, (Expr *) env->values[0], *v);
env->values[0] = v;
env->type = Env::HasWithAttrs;
}
@@ -983,7 +1123,7 @@ inline Value * EvalState::lookupVar(Env * env, const ExprVar & var, bool noEval)
return j->value;
}
if (!env->prevWith)
error("undefined variable '%1%'", symbols[var.name]).atPos(var.pos).withFrame(*env, var).debugThrow<UndefinedVarError>();
throwUndefinedVarError(var.pos, "undefined variable '%1%'", symbols[var.name], *env, const_cast<ExprVar&>(var));
for (size_t l = env->prevWith; l; --l, env = env->up) ;
}
}
@@ -1133,7 +1273,7 @@ void EvalState::cacheFile(
// computation.
if (mustBeTrivial &&
!(dynamic_cast<ExprAttrs *>(e)))
error("file '%s' must be an attribute set", path).debugThrow<EvalError>();
throw EvalError("file '%s' must be an attribute set", path);
eval(e, v);
} catch (Error & e) {
addErrorTrace(e, "while evaluating the file '%1%':", resolvedPath);
@@ -1151,31 +1291,31 @@ void EvalState::eval(Expr * e, Value & v)
}
inline bool EvalState::evalBool(Env & env, Expr * e, const PosIdx pos, std::string_view errorCtx)
inline bool EvalState::evalBool(Env & env, Expr * e)
{
try {
Value v;
e->eval(*this, env, v);
if (v.type() != nBool)
error("value is %1% while a Boolean was expected", showType(v)).withFrame(env, *e).debugThrow<TypeError>();
return v.boolean;
} catch (Error & e) {
e.addTrace(positions[pos], errorCtx);
throw;
}
Value v;
e->eval(*this, env, v);
if (v.type() != nBool)
throwTypeError(noPos, "value is %1% while a Boolean was expected", v, env, *e);
return v.boolean;
}
inline void EvalState::evalAttrs(Env & env, Expr * e, Value & v, const PosIdx pos, std::string_view errorCtx)
inline bool EvalState::evalBool(Env & env, Expr * e, const PosIdx pos)
{
try {
e->eval(*this, env, v);
if (v.type() != nAttrs)
error("value is %1% while a set was expected", showType(v)).withFrame(env, *e).debugThrow<TypeError>();
} catch (Error & e) {
e.addTrace(positions[pos], errorCtx);
throw;
}
Value v;
e->eval(*this, env, v);
if (v.type() != nBool)
throwTypeError(pos, "value is %1% while a Boolean was expected", v, env, *e);
return v.boolean;
}
inline void EvalState::evalAttrs(Env & env, Expr * e, Value & v)
{
e->eval(*this, env, v);
if (v.type() != nAttrs)
throwTypeError(noPos, "value is %1% while a set was expected", v, env, *e);
}
@@ -1248,7 +1388,7 @@ void ExprAttrs::eval(EvalState & state, Env & env, Value & v)
Hence we need __overrides.) */
if (hasOverrides) {
Value * vOverrides = (*v.attrs)[overrides->second.displ].value;
state.forceAttrs(*vOverrides, [&]() { return vOverrides->determinePos(noPos); }, "while evaluating the `__overrides` attribute");
state.forceAttrs(*vOverrides, [&]() { return vOverrides->determinePos(noPos); });
Bindings * newBnds = state.allocBindings(v.attrs->capacity() + vOverrides->attrs->size());
for (auto & i : *v.attrs)
newBnds->push_back(i);
@@ -1276,11 +1416,11 @@ void ExprAttrs::eval(EvalState & state, Env & env, Value & v)
state.forceValue(nameVal, i.pos);
if (nameVal.type() == nNull)
continue;
state.forceStringNoCtx(nameVal, i.pos, "while evaluating the name of a dynamic attribute");
state.forceStringNoCtx(nameVal);
auto nameSym = state.symbols.create(nameVal.string.s);
Bindings::iterator j = v.attrs->find(nameSym);
if (j != v.attrs->end())
state.error("dynamic attribute '%1%' already defined at %2%", state.symbols[nameSym], state.positions[j->pos]).atPos(i.pos).withFrame(env, *this).debugThrow<EvalError>();
state.throwEvalError(i.pos, "dynamic attribute '%1%' already defined at %2%", nameSym, j->pos, env, *this);
i.valueExpr->setName(nameSym);
/* Keep sorted order so find can catch duplicates */
@@ -1377,14 +1517,15 @@ void ExprSelect::eval(EvalState & state, Env & env, Value & v)
return;
}
} else {
state.forceAttrs(*vAttrs, pos, "while selecting an attribute");
state.forceAttrs(*vAttrs, pos);
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("attribute '%1%' missing", state.symbols[name])
.atPos(pos).withSuggestions(suggestions).withFrame(env, *this).debugThrow<EvalError>();
state.throwEvalError(
pos,
Suggestions::bestMatches(allAttrNames, state.symbols[name]),
"attribute '%1%' missing", state.symbols[name], env, *this);
}
}
vAttrs = j->value;
@@ -1479,12 +1620,7 @@ void EvalState::callFunction(Value & fun, size_t nrArgs, Value * * args, Value &
if (!lambda.hasFormals())
env2.values[displ++] = args[0];
else {
try {
forceAttrs(*args[0], lambda.pos, "while evaluating the value passed for the lambda argument");
} catch (Error & e) {
if (pos) e.addTrace(positions[pos], "from call site");
throw;
}
forceAttrs(*args[0], pos);
if (lambda.arg)
env2.values[displ++] = args[0];
@@ -1496,15 +1632,8 @@ void EvalState::callFunction(Value & fun, size_t nrArgs, Value * * args, Value &
for (auto & i : lambda.formals->formals) {
auto j = args[0]->attrs->get(i.name);
if (!j) {
if (!i.def) {
error("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<TypeError>();
}
if (!i.def) throwTypeError(pos, "%1% called without required argument '%2%'",
lambda, i.name, *fun.lambda.env, lambda);
env2.values[displ++] = i.def->maybeThunk(*this, env2);
} else {
attrsUsed++;
@@ -1522,15 +1651,11 @@ void EvalState::callFunction(Value & fun, size_t nrArgs, Value * * args, Value &
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("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<TypeError>();
throwTypeError(
pos,
Suggestions::bestMatches(formalNames, symbols[i.name]),
"%1% called with unexpected argument '%2%'",
lambda, i.name, *fun.lambda.env, lambda);
}
abort(); // can't happen
}
@@ -1553,15 +1678,11 @@ void EvalState::callFunction(Value & fun, size_t nrArgs, Value * * args, Value &
lambda.body->eval(*this, env2, vCur);
} catch (Error & e) {
if (loggerSettings.showTrace.get()) {
addErrorTrace(
e,
lambda.pos,
"while calling %s",
lambda.name
? concatStrings("'", symbols[lambda.name], "'")
: "anonymous lambda",
true);
if (pos) addErrorTrace(e, pos, "from call site%s", "", true);
addErrorTrace(e, lambda.pos, "while calling %s",
(lambda.name
? concatStrings("'", symbols[lambda.name], "'")
: "anonymous lambda"));
addErrorTrace(e, pos, "while evaluating call site%s", "");
}
throw;
}
@@ -1580,17 +1701,9 @@ void EvalState::callFunction(Value & fun, size_t nrArgs, Value * * args, Value &
return;
} else {
/* We have all the arguments, so call the primop. */
auto name = vCur.primOp->name;
nrPrimOpCalls++;
if (countCalls) primOpCalls[name]++;
try {
vCur.primOp->fun(*this, noPos, args, vCur);
} catch (Error & e) {
addErrorTrace(e, pos, "while calling the '%1%' builtin", name);
throw;
}
if (countCalls) primOpCalls[vCur.primOp->name]++;
vCur.primOp->fun(*this, pos, args, vCur);
nrArgs -= argsLeft;
args += argsLeft;
@@ -1625,20 +1738,9 @@ void EvalState::callFunction(Value & fun, size_t nrArgs, Value * * args, Value &
for (size_t i = 0; i < argsLeft; ++i)
vArgs[argsDone + i] = args[i];
auto name = primOp->primOp->name;
nrPrimOpCalls++;
if (countCalls) primOpCalls[name]++;
try {
// TODO:
// 1. Unify this and above code. Heavily redundant.
// 2. Create a fake env (arg1, arg2, etc.) and a fake expr (arg1: arg2: etc: builtins.name arg1 arg2 etc)
// so the debugger allows to inspect the wrong parameters passed to the builtin.
primOp->primOp->fun(*this, noPos, vArgs, vCur);
} catch (Error & e) {
addErrorTrace(e, pos, "while calling the '%1%' builtin", name);
throw;
}
if (countCalls) primOpCalls[primOp->primOp->name]++;
primOp->primOp->fun(*this, pos, vArgs, vCur);
nrArgs -= argsLeft;
args += argsLeft;
@@ -1651,18 +1753,14 @@ void EvalState::callFunction(Value & fun, size_t nrArgs, Value * * args, Value &
heap-allocate a copy and use that instead. */
Value * args2[] = {allocValue(), args[0]};
*args2[0] = vCur;
try {
callFunction(*functor->value, 2, args2, vCur, functor->pos);
} catch (Error & e) {
e.addTrace(positions[pos], "while calling a functor (an attribute set with a '__functor' attribute)");
throw;
}
/* !!! Should we use the attr pos here? */
callFunction(*functor->value, 2, args2, vCur, pos);
nrArgs--;
args++;
}
else
error("attempt to call something which is not a function but %1%", showType(vCur)).atPos(pos).debugThrow<TypeError>();
throwTypeError(pos, "attempt to call something which is not a function but %1%", vCur);
}
vRes = vCur;
@@ -1726,12 +1824,13 @@ void EvalState::autoCallFunction(Bindings & args, Value & fun, Value & res)
if (j != args.end()) {
attrs.insert(*j);
} else if (!i.def) {
error(R"(cannot evaluate a function that has an argument without a value ('%1%')
throwMissingArgumentError(i.pos, R"(cannot evaluate a function that has an argument without a value ('%1%')
Nix attempted to evaluate a function as a top level expression; in
this case it must have its arguments supplied either by default
values, or passed explicitly with '--arg' or '--argstr'. See
https://nixos.org/manual/nix/stable/language/constructs.html#functions.)", symbols[i.name])
.atPos(i.pos).withFrame(*fun.lambda.env, *fun.lambda.fun).debugThrow<MissingArgumentError>();
https://nixos.org/manual/nix/stable/language/constructs.html#functions.)", symbols[i.name],
*fun.lambda.env, *fun.lambda.fun);
}
}
}
@@ -1754,17 +1853,16 @@ 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) ? 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)) {
std::ostringstream out;
cond->show(state.symbols, out);
state.error("assertion '%1%' failed", out.str()).atPos(pos).withFrame(env, *this).debugThrow<AssertionError>();
state.throwAssertionError(pos, "assertion '%1%' failed", out.str(), env, *this);
}
body->eval(state, env, v);
}
@@ -1772,7 +1870,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, noPos, "in the argument of the not operator")); // XXX: FIXME: !
v.mkBool(!state.evalBool(env, e));
}
@@ -1780,7 +1878,7 @@ void ExprOpEq::eval(EvalState & state, Env & env, Value & v)
{
Value v1; e1->eval(state, env, v1);
Value v2; e2->eval(state, env, v2);
v.mkBool(state.eqValues(v1, v2, pos, "while testing two values for equality"));
v.mkBool(state.eqValues(v1, v2));
}
@@ -1788,33 +1886,33 @@ void ExprOpNEq::eval(EvalState & state, Env & env, Value & v)
{
Value v1; e1->eval(state, env, v1);
Value v2; e2->eval(state, env, v2);
v.mkBool(!state.eqValues(v1, v2, pos, "while testing two values for inequality"));
v.mkBool(!state.eqValues(v1, v2));
}
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) && state.evalBool(env, e2, pos));
}
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) || state.evalBool(env, e2, pos));
}
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) || state.evalBool(env, e2, pos));
}
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);
state.evalAttrs(env, e2, v2);
state.nrOpUpdates++;
@@ -1853,18 +1951,18 @@ void ExprOpConcatLists::eval(EvalState & state, Env & env, Value & v)
Value v1; e1->eval(state, env, v1);
Value v2; e2->eval(state, env, v2);
Value * lists[2] = { &v1, &v2 };
state.concatLists(v, 2, lists, pos, "while evaluating one of the elements to concatenate");
state.concatLists(v, 2, lists, pos);
}
void EvalState::concatLists(Value & v, size_t nrLists, Value * * lists, const PosIdx pos, std::string_view errorCtx)
void EvalState::concatLists(Value & v, size_t nrLists, Value * * lists, const PosIdx pos)
{
nrListConcats++;
Value * nonEmpty = 0;
size_t len = 0;
for (size_t n = 0; n < nrLists; ++n) {
forceList(*lists[n], pos, errorCtx);
forceList(*lists[n], pos);
auto l = lists[n]->listSize();
len += l;
if (l) nonEmpty = lists[n];
@@ -1941,22 +2039,20 @@ void ExprConcatStrings::eval(EvalState & state, Env & env, Value & v)
nf = n;
nf += vTmp.fpoint;
} else
state.error("cannot add %1% to an integer", showType(vTmp)).atPos(i_pos).withFrame(env, *this).debugThrow<EvalError>();
state.throwEvalError(i_pos, "cannot add %1% to an integer", showType(vTmp), env, *this);
} else if (firstType == nFloat) {
if (vTmp.type() == nInt) {
nf += vTmp.integer;
} else if (vTmp.type() == nFloat) {
nf += vTmp.fpoint;
} else
state.error("cannot add %1% to a float", showType(vTmp)).atPos(i_pos).withFrame(env, *this).debugThrow<EvalError>();
state.throwEvalError(i_pos, "cannot add %1% to a float", showType(vTmp), env, *this);
} else {
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 */
auto part = state.coerceToString(i_pos, vTmp, context,
"while evaluating a path segment",
false, firstType == nString, !first);
auto part = state.coerceToString(i_pos, vTmp, context, false, firstType == nString, !first);
sSize += part->size();
s.emplace_back(std::move(part));
}
@@ -1970,7 +2066,7 @@ void ExprConcatStrings::eval(EvalState & state, Env & env, Value & v)
v.mkFloat(nf);
else if (firstType == nPath) {
if (!context.empty())
state.error("a string that refers to a store path cannot be appended to a path").atPos(pos).withFrame(env, *this).debugThrow<EvalError>();
state.throwEvalError(pos, "a string that refers to a store path cannot be appended to a path", env, *this);
v.mkPath(canonPath(str()));
} else
v.mkStringMove(c_str(), context);
@@ -2020,47 +2116,33 @@ void EvalState::forceValueDeep(Value & v)
}
NixInt EvalState::forceInt(Value & v, const PosIdx pos, std::string_view errorCtx)
NixInt EvalState::forceInt(Value & v, const PosIdx pos)
{
try {
forceValue(v, pos);
if (v.type() != nInt)
error("value is %1% while an integer was expected", showType(v)).debugThrow<TypeError>();
forceValue(v, pos);
if (v.type() != nInt)
throwTypeError(pos, "value is %1% while an integer was expected", v);
return v.integer;
}
NixFloat EvalState::forceFloat(Value & v, const PosIdx pos)
{
forceValue(v, pos);
if (v.type() == nInt)
return v.integer;
} catch (Error & e) {
e.addTrace(positions[pos], errorCtx);
throw;
}
else if (v.type() != nFloat)
throwTypeError(pos, "value is %1% while a float was expected", v);
return v.fpoint;
}
NixFloat EvalState::forceFloat(Value & v, const PosIdx pos, std::string_view errorCtx)
bool EvalState::forceBool(Value & v, const PosIdx pos)
{
try {
forceValue(v, pos);
if (v.type() == nInt)
return v.integer;
else if (v.type() != nFloat)
error("value is %1% while a float was expected", showType(v)).debugThrow<TypeError>();
return v.fpoint;
} catch (Error & e) {
e.addTrace(positions[pos], errorCtx);
throw;
}
}
bool EvalState::forceBool(Value & v, const PosIdx pos, std::string_view errorCtx)
{
try {
forceValue(v, pos);
if (v.type() != nBool)
error("value is %1% while a Boolean was expected", showType(v)).debugThrow<TypeError>();
return v.boolean;
} catch (Error & e) {
e.addTrace(positions[pos], errorCtx);
throw;
}
forceValue(v, pos);
if (v.type() != nBool)
throwTypeError(pos, "value is %1% while a Boolean was expected", v);
return v.boolean;
}
@@ -2070,30 +2152,21 @@ bool EvalState::isFunctor(Value & fun)
}
void EvalState::forceFunction(Value & v, const PosIdx pos, std::string_view errorCtx)
void EvalState::forceFunction(Value & v, const PosIdx pos)
{
try {
forceValue(v, pos);
if (v.type() != nFunction && !isFunctor(v))
error("value is %1% while a function was expected", showType(v)).debugThrow<TypeError>();
} catch (Error & e) {
e.addTrace(positions[pos], errorCtx);
throw;
}
forceValue(v, pos);
if (v.type() != nFunction && !isFunctor(v))
throwTypeError(pos, "value is %1% while a function was expected", v);
}
std::string_view EvalState::forceString(Value & v, const PosIdx pos, std::string_view errorCtx)
std::string_view EvalState::forceString(Value & v, const PosIdx pos)
{
try {
forceValue(v, pos);
if (v.type() != nString)
error("value is %1% while a string was expected", showType(v)).debugThrow<TypeError>();
return v.string.s;
} catch (Error & e) {
e.addTrace(positions[pos], errorCtx);
throw;
forceValue(v, pos);
if (v.type() != nString) {
throwTypeError(pos, "value is %1% while a string was expected", v);
}
return v.string.s;
}
@@ -2116,19 +2189,24 @@ NixStringContext Value::getContext(const Store & store)
}
std::string_view EvalState::forceString(Value & v, PathSet & context, const PosIdx pos, std::string_view errorCtx)
std::string_view EvalState::forceString(Value & v, PathSet & context, const PosIdx pos)
{
auto s = forceString(v, pos, errorCtx);
auto s = forceString(v, pos);
copyContext(v, context);
return s;
}
std::string_view EvalState::forceStringNoCtx(Value & v, const PosIdx pos, std::string_view errorCtx)
std::string_view EvalState::forceStringNoCtx(Value & v, const PosIdx pos)
{
auto s = forceString(v, pos, errorCtx);
auto s = forceString(v, pos);
if (v.string.context) {
error("the string '%1%' is not allowed to refer to a store path (such as '%2%')", v.string.s, v.string.context[0]).withTrace(pos, errorCtx).debugThrow<EvalError>();
if (pos)
throwEvalError(pos, "the string '%1%' is not allowed to refer to a store path (such as '%2%')",
v.string.s, v.string.context[0]);
else
throwEvalError("the string '%1%' is not allowed to refer to a store path (such as '%2%')",
v.string.s, v.string.context[0]);
}
return s;
}
@@ -2152,16 +2230,14 @@ std::optional<std::string> EvalState::tryAttrsToString(const PosIdx pos, Value &
if (i != v.attrs->end()) {
Value v1;
callFunction(*i->value, v, v1, pos);
return coerceToString(pos, v1, context,
"while evaluating the result of the `__toString` attribute",
coerceMore, copyToStore).toOwned();
return coerceToString(pos, v1, context, coerceMore, copyToStore).toOwned();
}
return {};
}
BackedStringView EvalState::coerceToString(const PosIdx pos, Value &v, PathSet &context,
std::string_view errorCtx, bool coerceMore, bool copyToStore, bool canonicalizePath)
BackedStringView EvalState::coerceToString(const PosIdx pos, Value & v, PathSet & context,
bool coerceMore, bool copyToStore, bool canonicalizePath)
{
forceValue(v, pos);
@@ -2184,23 +2260,13 @@ BackedStringView EvalState::coerceToString(const PosIdx pos, Value &v, PathSet &
if (maybeString)
return std::move(*maybeString);
auto i = v.attrs->find(sOutPath);
if (i == v.attrs->end()) {
error("cannot coerce %1% to a string", showType(v))
.withTrace(pos, errorCtx)
.debugThrow<TypeError>();
}
return coerceToString(pos, *i->value, context, errorCtx,
coerceMore, copyToStore, canonicalizePath);
if (i == v.attrs->end())
throwTypeError(pos, "cannot coerce a set to a string");
return coerceToString(pos, *i->value, context, coerceMore, copyToStore);
}
if (v.type() == nExternal) {
try {
return v.external->coerceToString(positions[pos], context, coerceMore, copyToStore);
} catch (Error & e) {
e.addTrace(nullptr, errorCtx);
throw;
}
}
if (v.type() == nExternal)
return v.external->coerceToString(positions[pos], context, coerceMore, copyToStore);
if (coerceMore) {
/* Note that `false' is represented as an empty string for
@@ -2214,14 +2280,7 @@ BackedStringView EvalState::coerceToString(const PosIdx pos, Value &v, PathSet &
if (v.isList()) {
std::string result;
for (auto [n, v2] : enumerate(v.listItems())) {
try {
result += *coerceToString(noPos, *v2, context,
"while evaluating one element of the list",
coerceMore, copyToStore, canonicalizePath);
} catch (Error & e) {
e.addTrace(positions[pos], errorCtx);
throw;
}
result += *coerceToString(pos, *v2, context, coerceMore, copyToStore);
if (n < v.listSize() - 1
/* !!! not quite correct */
&& (!v2->isList() || v2->listSize() != 0))
@@ -2231,16 +2290,14 @@ BackedStringView EvalState::coerceToString(const PosIdx pos, Value &v, PathSet &
}
}
error("cannot coerce %1% to a string", showType(v))
.withTrace(pos, errorCtx)
.debugThrow<TypeError>();
throwTypeError(pos, "cannot coerce %1% to a string", v);
}
StorePath EvalState::copyPathToStore(PathSet & context, const Path & path)
{
if (nix::isDerivation(path))
error("file names are not allowed to end in '%1%'", drvExtension).debugThrow<EvalError>();
throwEvalError("file names are not allowed to end in '%1%'", drvExtension);
auto dstPath = [&]() -> StorePath
{
@@ -2261,25 +2318,28 @@ StorePath EvalState::copyPathToStore(PathSet & context, const Path & path)
}
Path EvalState::coerceToPath(const PosIdx pos, Value & v, PathSet & context, std::string_view errorCtx)
Path EvalState::coerceToPath(const PosIdx pos, Value & v, PathSet & context)
{
auto path = coerceToString(pos, v, context, errorCtx, false, false, true).toOwned();
auto path = coerceToString(pos, v, context, false, false).toOwned();
if (path == "" || path[0] != '/')
error("string '%1%' doesn't represent an absolute path", path).withTrace(pos, errorCtx).debugThrow<EvalError>();
throwEvalError(pos, "string '%1%' doesn't represent an absolute path", path);
return path;
}
StorePath EvalState::coerceToStorePath(const PosIdx pos, Value & v, PathSet & context, std::string_view errorCtx)
StorePath EvalState::coerceToStorePath(const PosIdx pos, Value & v, PathSet & context)
{
auto path = coerceToString(pos, v, context, errorCtx, false, false, true).toOwned();
auto path = coerceToString(pos, v, context, false, false).toOwned();
if (auto storePath = store->maybeParseStorePath(path))
return *storePath;
error("path '%1%' is not in the Nix store", path).withTrace(pos, errorCtx).debugThrow<EvalError>();
throw EvalError({
.msg = hintfmt("path '%1%' is not in the Nix store", path),
.errPos = positions[pos]
});
}
bool EvalState::eqValues(Value & v1, Value & v2, const PosIdx pos, std::string_view errorCtx)
bool EvalState::eqValues(Value & v1, Value & v2)
{
forceValue(v1, noPos);
forceValue(v2, noPos);
@@ -2299,6 +2359,7 @@ bool EvalState::eqValues(Value & v1, Value & v2, const PosIdx pos, std::string_v
if (v1.type() != v2.type()) return false;
switch (v1.type()) {
case nInt:
return v1.integer == v2.integer;
@@ -2317,7 +2378,7 @@ bool EvalState::eqValues(Value & v1, Value & v2, const PosIdx pos, std::string_v
case nList:
if (v1.listSize() != v2.listSize()) return false;
for (size_t n = 0; n < v1.listSize(); ++n)
if (!eqValues(*v1.listElems()[n], *v2.listElems()[n], pos, errorCtx)) return false;
if (!eqValues(*v1.listElems()[n], *v2.listElems()[n])) return false;
return true;
case nAttrs: {
@@ -2327,7 +2388,7 @@ bool EvalState::eqValues(Value & v1, Value & v2, const PosIdx pos, std::string_v
Bindings::iterator i = v1.attrs->find(sOutPath);
Bindings::iterator j = v2.attrs->find(sOutPath);
if (i != v1.attrs->end() && j != v2.attrs->end())
return eqValues(*i->value, *j->value, pos, errorCtx);
return eqValues(*i->value, *j->value);
}
if (v1.attrs->size() != v2.attrs->size()) return false;
@@ -2335,7 +2396,7 @@ bool EvalState::eqValues(Value & v1, Value & v2, const PosIdx pos, std::string_v
/* Otherwise, compare the attributes one by one. */
Bindings::iterator i, j;
for (i = v1.attrs->begin(), j = v2.attrs->begin(); i != v1.attrs->end(); ++i, ++j)
if (i->name != j->name || !eqValues(*i->value, *j->value, pos, errorCtx))
if (i->name != j->name || !eqValues(*i->value, *j->value))
return false;
return true;
@@ -2352,7 +2413,9 @@ bool EvalState::eqValues(Value & v1, Value & v2, const PosIdx pos, std::string_v
return v1.fpoint == v2.fpoint;
default:
error("cannot compare %1% with %2%", showType(v1), showType(v2)).withTrace(pos, errorCtx).debugThrow<EvalError>();
throwEvalError("cannot compare %1% with %2%",
showType(v1),
showType(v2));
}
}
@@ -2479,7 +2542,8 @@ void EvalState::printStats()
std::string ExternalValueBase::coerceToString(const Pos & pos, PathSet & context, bool copyMore, bool copyToStore) const
{
throw TypeError({
.msg = hintfmt("cannot coerce %1% to a string", showType())
.msg = hintfmt("cannot coerce %1% to a string", showType()),
.errPos = pos
});
}
@@ -2515,7 +2579,7 @@ Strings EvalSettings::getDefaultNixPath()
};
if (!evalSettings.restrictEval && !evalSettings.pureEval) {
add(settings.useXDGBaseDirectories ? getStateDir() + "/nix/defexpr/channels" : getHome() + "/.nix-defexpr/channels");
add(getHome() + "/.nix-defexpr/channels");
add(settings.nixStateDir + "/profiles/per-user/root/channels/nixpkgs", "nixpkgs");
add(settings.nixStateDir + "/profiles/per-user/root/channels");
}

View File

@@ -86,43 +86,6 @@ struct DebugTrace {
void debugError(Error * e, Env & env, Expr & expr);
class ErrorBuilder
{
private:
EvalState & state;
ErrorInfo info;
ErrorBuilder(EvalState & s, ErrorInfo && i): state(s), info(i) { }
public:
template<typename... Args>
[[nodiscard, gnu::noinline]]
static ErrorBuilder * create(EvalState & s, const Args & ... args)
{
return new ErrorBuilder(s, ErrorInfo { .msg = hintfmt(args...) });
}
[[nodiscard, gnu::noinline]]
ErrorBuilder & atPos(PosIdx pos);
[[nodiscard, gnu::noinline]]
ErrorBuilder & withTrace(PosIdx pos, const std::string_view text);
[[nodiscard, gnu::noinline]]
ErrorBuilder & withFrameTrace(PosIdx pos, const std::string_view text);
[[nodiscard, gnu::noinline]]
ErrorBuilder & withSuggestions(Suggestions & s);
[[nodiscard, gnu::noinline]]
ErrorBuilder & withFrame(const Env & e, const Expr & ex);
template<class ErrorType>
[[gnu::noinline, gnu::noreturn]]
void debugThrow();
};
class EvalState : public std::enable_shared_from_this<EvalState>
{
public:
@@ -182,39 +145,30 @@ public:
template<class E>
[[gnu::noinline, gnu::noreturn]]
void debugThrowLastTrace(E && error)
void debugThrow(E && error, const Env & env, const Expr & expr)
{
debugThrow(error, nullptr, nullptr);
}
template<class E>
[[gnu::noinline, gnu::noreturn]]
void debugThrow(E && error, const Env * env, const Expr * expr)
{
if (debugRepl && ((env && expr) || !debugTraces.empty())) {
if (!env || !expr) {
const DebugTrace & last = debugTraces.front();
env = &last.env;
expr = &last.expr;
}
runDebugRepl(&error, *env, *expr);
}
if (debugRepl)
runDebugRepl(&error, env, expr);
throw std::move(error);
}
// This is dangerous, but gets in line with the idea that error creation and
// throwing should not allocate on the stack of hot functions.
// as long as errors are immediately thrown, it works.
ErrorBuilder * errorBuilder;
template<class E>
[[gnu::noinline, gnu::noreturn]]
void debugThrowLastTrace(E && e)
{
// Call this in the situation where Expr and Env are inaccessible.
// The debugger will start in the last context that's in the
// DebugTrace stack.
if (debugRepl && !debugTraces.empty()) {
const DebugTrace & last = debugTraces.front();
runDebugRepl(&e, last.env, last.expr);
}
template<typename... Args>
[[nodiscard, gnu::noinline]]
ErrorBuilder & error(const Args & ... args) {
errorBuilder = ErrorBuilder::create(*this, args...);
return *errorBuilder;
throw std::move(e);
}
private:
SrcToStore srcToStore;
@@ -328,8 +282,8 @@ public:
/* 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, const PosIdx pos);
inline void evalAttrs(Env & env, Expr * e, Value & v);
/* If `v' is a thunk, enter it and overwrite `v' with the result
of the evaluation of the thunk. If `v' is a delayed function
@@ -345,25 +299,89 @@ public:
void forceValueDeep(Value & v);
/* Force `v', and then verify that it has the expected type. */
NixInt forceInt(Value & v, const PosIdx pos, std::string_view errorCtx);
NixFloat forceFloat(Value & v, const PosIdx pos, std::string_view errorCtx);
bool forceBool(Value & v, const PosIdx pos, std::string_view errorCtx);
NixInt forceInt(Value & v, const PosIdx pos);
NixFloat forceFloat(Value & v, const PosIdx pos);
bool forceBool(Value & v, const PosIdx pos);
void forceAttrs(Value & v, const PosIdx pos, std::string_view errorCtx);
void forceAttrs(Value & v, const PosIdx pos);
template <typename Callable>
inline void forceAttrs(Value & v, Callable getPos, std::string_view errorCtx);
inline void forceAttrs(Value & v, Callable getPos);
inline void forceList(Value & v, const PosIdx pos, std::string_view errorCtx);
void forceFunction(Value & v, const PosIdx pos, std::string_view errorCtx); // either lambda or primop
std::string_view forceString(Value & v, const PosIdx pos, std::string_view errorCtx);
std::string_view forceString(Value & v, PathSet & context, const PosIdx pos, std::string_view errorCtx);
std::string_view forceStringNoCtx(Value & v, const PosIdx pos, std::string_view errorCtx);
inline void forceList(Value & v, const PosIdx pos);
void forceFunction(Value & v, const PosIdx pos); // either lambda or primop
std::string_view forceString(Value & v, const PosIdx pos = noPos);
std::string_view forceString(Value & v, PathSet & context, const PosIdx pos = noPos);
std::string_view forceStringNoCtx(Value & v, const PosIdx pos = noPos);
[[gnu::noinline, gnu::noreturn]]
void throwEvalError(const PosIdx pos, const char * s);
[[gnu::noinline, gnu::noreturn]]
void throwEvalError(const PosIdx pos, const char * s,
Env & env, Expr & expr);
[[gnu::noinline, gnu::noreturn]]
void throwEvalError(const char * s, const std::string & s2);
[[gnu::noinline, gnu::noreturn]]
void throwEvalError(const PosIdx pos, const char * s, const std::string & s2);
[[gnu::noinline, gnu::noreturn]]
void throwEvalError(const char * s, const std::string & s2,
Env & env, Expr & expr);
[[gnu::noinline, gnu::noreturn]]
void throwEvalError(const PosIdx pos, const char * s, const std::string & s2,
Env & env, Expr & expr);
[[gnu::noinline, gnu::noreturn]]
void throwEvalError(const char * s, const std::string & s2, const std::string & s3,
Env & env, Expr & expr);
[[gnu::noinline, gnu::noreturn]]
void throwEvalError(const PosIdx pos, const char * s, const std::string & s2, const std::string & s3,
Env & env, Expr & expr);
[[gnu::noinline, gnu::noreturn]]
void throwEvalError(const PosIdx pos, const char * s, const std::string & s2, const std::string & s3);
[[gnu::noinline, gnu::noreturn]]
void throwEvalError(const char * s, const std::string & s2, const std::string & s3);
[[gnu::noinline, gnu::noreturn]]
void throwEvalError(const PosIdx pos, const Suggestions & suggestions, const char * s, const std::string & s2,
Env & env, Expr & expr);
[[gnu::noinline, gnu::noreturn]]
void throwEvalError(const PosIdx p1, const char * s, const Symbol sym, const PosIdx p2,
Env & env, Expr & expr);
[[gnu::noinline, gnu::noreturn]]
void throwTypeError(const PosIdx pos, const char * s, const Value & v);
[[gnu::noinline, gnu::noreturn]]
void throwTypeError(const PosIdx pos, const char * s, const Value & v,
Env & env, Expr & expr);
[[gnu::noinline, gnu::noreturn]]
void throwTypeError(const PosIdx pos, const char * s);
[[gnu::noinline, gnu::noreturn]]
void throwTypeError(const PosIdx pos, const char * s,
Env & env, Expr & expr);
[[gnu::noinline, gnu::noreturn]]
void throwTypeError(const PosIdx pos, const char * s, const ExprLambda & fun, const Symbol s2,
Env & env, Expr & expr);
[[gnu::noinline, gnu::noreturn]]
void throwTypeError(const PosIdx pos, const Suggestions & suggestions, const char * s, const ExprLambda & fun, const Symbol s2,
Env & env, Expr & expr);
[[gnu::noinline, gnu::noreturn]]
void throwTypeError(const char * s, const Value & v,
Env & env, Expr & expr);
[[gnu::noinline, gnu::noreturn]]
void throwAssertionError(const PosIdx pos, const char * s, const std::string & s1,
Env & env, Expr & expr);
[[gnu::noinline, gnu::noreturn]]
void throwUndefinedVarError(const PosIdx pos, const char * s, const std::string & s1,
Env & env, Expr & expr);
[[gnu::noinline, gnu::noreturn]]
void throwMissingArgumentError(const PosIdx pos, const char * s, const std::string & s1,
Env & env, Expr & expr);
[[gnu::noinline]]
void addErrorTrace(Error & e, const char * s, const std::string & s2) const;
[[gnu::noinline]]
void addErrorTrace(Error & e, const PosIdx pos, const char * s, const std::string & s2, bool frame = false) const;
void addErrorTrace(Error & e, const PosIdx pos, const char * s, const std::string & s2) const;
public:
/* Return true iff the value `v' denotes a derivation (i.e. a
@@ -378,7 +396,6 @@ public:
booleans and lists to a string. If `copyToStore' is set,
referenced paths are copied to the Nix store as a side effect. */
BackedStringView coerceToString(const PosIdx pos, Value & v, PathSet & context,
std::string_view errorCtx,
bool coerceMore = false, bool copyToStore = true,
bool canonicalizePath = true);
@@ -387,10 +404,10 @@ public:
/* Path coercion. Converts strings, paths and derivations to a
path. The result is guaranteed to be a canonicalised, absolute
path. Nothing is copied to the store. */
Path coerceToPath(const PosIdx pos, Value & v, PathSet & context, std::string_view errorCtx);
Path coerceToPath(const PosIdx pos, Value & v, PathSet & context);
/* Like coerceToPath, but the result must be a store path. */
StorePath coerceToStorePath(const PosIdx pos, Value & v, PathSet & context, std::string_view errorCtx);
StorePath coerceToStorePath(const PosIdx pos, Value & v, PathSet & context);
public:
@@ -450,7 +467,7 @@ public:
/* Do a deep equality test between two values. That is, list
elements and attributes are compared recursively. */
bool eqValues(Value & v1, Value & v2, const PosIdx pos, std::string_view errorCtx);
bool eqValues(Value & v1, Value & v2);
bool isFunctor(Value & fun);
@@ -485,7 +502,7 @@ public:
void mkThunk_(Value & v, Expr * expr);
void mkPos(Value & v, PosIdx pos);
void concatLists(Value & v, size_t nrLists, Value * * lists, const PosIdx pos, std::string_view errorCtx);
void concatLists(Value & v, size_t nrLists, Value * * lists, const PosIdx pos);
/* Print statistics. */
void printStats();
@@ -648,13 +665,6 @@ extern EvalSettings evalSettings;
static const std::string corepkgsPrefix{"/__corepkgs__/"};
template<class ErrorType>
void ErrorBuilder::debugThrow()
{
// NOTE: We always use the -LastTrace version as we push the new trace in withFrame()
state.debugThrowLastTrace(ErrorType(info));
}
}
#include "eval-inline.hh"

View File

@@ -16,9 +16,7 @@ let
subdir = if key == lockFile.root then rootSubdir else node.locked.dir or "";
outPath = sourceInfo + ((if subdir == "" then "" else "/") + subdir);
flake = import (outPath + "/flake.nix");
flake = import (sourceInfo + (if subdir != "" then "/" else "") + subdir + "/flake.nix");
inputs = builtins.mapAttrs
(inputName: inputSpec: allNodes.${resolveInput inputSpec})
@@ -45,21 +43,7 @@ let
outputs = flake.outputs (inputs // { self = result; });
result =
outputs
# We add the sourceInfo attribute for its metadata, as they are
# relevant metadata for the flake. However, the outPath of the
# sourceInfo does not necessarily match the outPath of the flake,
# as the flake may be in a subdirectory of a source.
# This is shadowed in the next //
// sourceInfo
// {
# This shadows the sourceInfo.outPath
inherit outPath;
inherit inputs; inherit outputs; inherit sourceInfo; _type = "flake";
};
result = outputs // sourceInfo // { inherit inputs; inherit outputs; inherit sourceInfo; _type = "flake"; };
in
if node.flake or true then
assert builtins.isFunction flake.outputs;

View File

@@ -259,28 +259,28 @@ static Flake getFlake(
if (setting.value->type() == nString)
flake.config.settings.emplace(
state.symbols[setting.name],
std::string(state.forceStringNoCtx(*setting.value, setting.pos, "")));
std::string(state.forceStringNoCtx(*setting.value, setting.pos)));
else if (setting.value->type() == nPath) {
PathSet emptyContext = {};
flake.config.settings.emplace(
state.symbols[setting.name],
state.coerceToString(setting.pos, *setting.value, emptyContext, "", false, true, true) .toOwned());
state.coerceToString(setting.pos, *setting.value, emptyContext, false, true, true) .toOwned());
}
else if (setting.value->type() == nInt)
flake.config.settings.emplace(
state.symbols[setting.name],
state.forceInt(*setting.value, setting.pos, ""));
state.forceInt(*setting.value, setting.pos));
else if (setting.value->type() == nBool)
flake.config.settings.emplace(
state.symbols[setting.name],
Explicit<bool> { state.forceBool(*setting.value, setting.pos, "") });
Explicit<bool> { state.forceBool(*setting.value, setting.pos) });
else if (setting.value->type() == nList) {
std::vector<std::string> ss;
for (auto elem : setting.value->listItems()) {
if (elem->type() != nString)
throw TypeError("list element in flake configuration setting '%s' is %s while a string is expected",
state.symbols[setting.name], showType(*setting.value));
ss.emplace_back(state.forceStringNoCtx(*elem, setting.pos, ""));
ss.emplace_back(state.forceStringNoCtx(*elem, setting.pos));
}
flake.config.settings.emplace(state.symbols[setting.name], ss);
}
@@ -741,7 +741,7 @@ void callFlake(EvalState & state,
static void prim_getFlake(EvalState & state, const PosIdx pos, Value * * args, Value & v)
{
std::string flakeRefS(state.forceStringNoCtx(*args[0], pos, "while evaluating the argument passed to builtins.getFlake"));
std::string flakeRefS(state.forceStringNoCtx(*args[0], pos));
auto flakeRef = parseFlakeRef(flakeRefS, {}, true);
if (evalSettings.pureEval && !flakeRef.input.isLocked())
throw Error("cannot call 'getFlake' on unlocked flake reference '%s', at %s (use --impure to override)", flakeRefS, state.positions[pos]);

View File

@@ -89,7 +89,7 @@ LockFile::LockFile(const nlohmann::json & json, const Path & path)
std::string inputKey = i.value();
auto k = nodeMap.find(inputKey);
if (k == nodeMap.end()) {
auto & nodes = json["nodes"];
auto nodes = json["nodes"];
auto jsonNode2 = nodes.find(inputKey);
if (jsonNode2 == nodes.end())
throw Error("lock file references missing node '%s'", inputKey);
@@ -219,7 +219,7 @@ std::optional<FlakeRef> LockFile::isUnlocked() const
visit(root);
for (auto & i : nodes) {
if (i == ref<const Node>(root)) continue;
if (i == root) continue;
auto node = i.dynamic_pointer_cast<const LockedNode>();
if (node && !node->lockedRef.input.isLocked())
return node->lockedRef;

View File

@@ -51,7 +51,7 @@ std::string DrvInfo::queryName() const
if (name == "" && attrs) {
auto i = attrs->find(state->sName);
if (i == attrs->end()) throw TypeError("derivation name missing");
name = state->forceStringNoCtx(*i->value, noPos, "while evaluating the 'name' attribute of a derivation");
name = state->forceStringNoCtx(*i->value);
}
return name;
}
@@ -61,7 +61,7 @@ std::string DrvInfo::querySystem() const
{
if (system == "" && attrs) {
auto i = attrs->find(state->sSystem);
system = i == attrs->end() ? "unknown" : state->forceStringNoCtx(*i->value, i->pos, "while evaluating the 'system' attribute of a derivation");
system = i == attrs->end() ? "unknown" : state->forceStringNoCtx(*i->value, i->pos);
}
return system;
}
@@ -75,7 +75,7 @@ std::optional<StorePath> DrvInfo::queryDrvPath() const
if (i == attrs->end())
drvPath = {std::nullopt};
else
drvPath = {state->coerceToStorePath(i->pos, *i->value, context, "while evaluating the 'drvPath' attribute of a derivation")};
drvPath = {state->coerceToStorePath(i->pos, *i->value, context)};
}
return drvPath.value_or(std::nullopt);
}
@@ -95,7 +95,7 @@ StorePath DrvInfo::queryOutPath() const
Bindings::iterator i = attrs->find(state->sOutPath);
PathSet context;
if (i != attrs->end())
outPath = state->coerceToStorePath(i->pos, *i->value, context, "while evaluating the output path of a derivation");
outPath = state->coerceToStorePath(i->pos, *i->value, context);
}
if (!outPath)
throw UnimplementedError("CA derivations are not yet supported");
@@ -109,23 +109,23 @@ DrvInfo::Outputs DrvInfo::queryOutputs(bool withPaths, bool onlyOutputsToInstall
/* Get the outputs list. */
Bindings::iterator i;
if (attrs && (i = attrs->find(state->sOutputs)) != attrs->end()) {
state->forceList(*i->value, i->pos, "while evaluating the 'outputs' attribute of a derivation");
state->forceList(*i->value, i->pos);
/* For each output... */
for (auto elem : i->value->listItems()) {
std::string output(state->forceStringNoCtx(*elem, i->pos, "while evaluating the name of an output of a derivation"));
std::string output(state->forceStringNoCtx(*elem, i->pos));
if (withPaths) {
/* Evaluate the corresponding set. */
Bindings::iterator out = attrs->find(state->symbols.create(output));
if (out == attrs->end()) continue; // FIXME: throw error?
state->forceAttrs(*out->value, i->pos, "while evaluating an output of a derivation");
state->forceAttrs(*out->value, i->pos);
/* And evaluate its outPath attribute. */
Bindings::iterator outPath = out->value->attrs->find(state->sOutPath);
if (outPath == out->value->attrs->end()) continue; // FIXME: throw error?
PathSet context;
outputs.emplace(output, state->coerceToStorePath(outPath->pos, *outPath->value, context, "while evaluating an output path of a derivation"));
outputs.emplace(output, state->coerceToStorePath(outPath->pos, *outPath->value, context));
} else
outputs.emplace(output, std::nullopt);
}
@@ -137,7 +137,7 @@ DrvInfo::Outputs DrvInfo::queryOutputs(bool withPaths, bool onlyOutputsToInstall
return outputs;
Bindings::iterator i;
if (attrs && (i = attrs->find(state->sOutputSpecified)) != attrs->end() && state->forceBool(*i->value, i->pos, "while evaluating the 'outputSpecified' attribute of a derivation")) {
if (attrs && (i = attrs->find(state->sOutputSpecified)) != attrs->end() && state->forceBool(*i->value, i->pos)) {
Outputs result;
auto out = outputs.find(queryOutputName());
if (out == outputs.end())
@@ -169,7 +169,7 @@ std::string DrvInfo::queryOutputName() const
{
if (outputName == "" && attrs) {
Bindings::iterator i = attrs->find(state->sOutputName);
outputName = i != attrs->end() ? state->forceStringNoCtx(*i->value, noPos, "while evaluating the output name of a derivation") : "";
outputName = i != attrs->end() ? state->forceStringNoCtx(*i->value) : "";
}
return outputName;
}
@@ -181,7 +181,7 @@ Bindings * DrvInfo::getMeta()
if (!attrs) return 0;
Bindings::iterator a = attrs->find(state->sMeta);
if (a == attrs->end()) return 0;
state->forceAttrs(*a->value, a->pos, "while evaluating the 'meta' attribute of a derivation");
state->forceAttrs(*a->value, a->pos);
meta = a->value->attrs;
return meta;
}
@@ -382,7 +382,7 @@ static void getDerivations(EvalState & state, Value & vIn,
`recurseForDerivations = true' attribute. */
if (i->value->type() == nAttrs) {
Bindings::iterator j = i->value->attrs->find(state.sRecurseForDerivations);
if (j != i->value->attrs->end() && state.forceBool(*j->value, j->pos, "while evaluating the attribute `recurseForDerivations`"))
if (j != i->value->attrs->end() && state.forceBool(*j->value, j->pos))
getDerivations(state, *i->value, pathPrefix2, autoArgs, drvs, done, ignoreAssertionFailures);
}
}

View File

@@ -7,4 +7,4 @@ Description: Nix Package Manager
Version: @PACKAGE_VERSION@
Requires: nix-store bdw-gc
Libs: -L${libdir} -lnixexpr
Cflags: -I${includedir}/nix -std=c++2a
Cflags: -I${includedir}/nix -std=c++17

View File

@@ -8,6 +8,7 @@
#include "error.hh"
#include "chunked-vector.hh"
namespace nix {
@@ -186,7 +187,7 @@ struct ExprString : Expr
{
std::string s;
Value v;
ExprString(std::string &&s) : s(std::move(s)) { v.mkString(this->s.data()); };
ExprString(std::string s) : s(std::move(s)) { v.mkString(this->s.data()); };
Value * maybeThunk(EvalState & state, Env & env) override;
COMMON_METHODS
};
@@ -233,7 +234,7 @@ struct ExprSelect : Expr
PosIdx pos;
Expr * e, * def;
AttrPath attrPath;
ExprSelect(const PosIdx & pos, Expr * e, const AttrPath && attrPath, Expr * def) : pos(pos), e(e), def(def), attrPath(std::move(attrPath)) { };
ExprSelect(const PosIdx & pos, Expr * e, const AttrPath & attrPath, Expr * def) : pos(pos), e(e), def(def), attrPath(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
@@ -243,7 +244,7 @@ struct ExprOpHasAttr : Expr
{
Expr * e;
AttrPath attrPath;
ExprOpHasAttr(Expr * e, const AttrPath && attrPath) : e(e), attrPath(std::move(attrPath)) { };
ExprOpHasAttr(Expr * e, const AttrPath & attrPath) : e(e), attrPath(attrPath) { };
PosIdx getPos() const override { return e->getPos(); }
COMMON_METHODS
};

View File

@@ -90,7 +90,7 @@ static void dupAttr(const EvalState & state, Symbol attr, const PosIdx pos, cons
}
static void addAttr(ExprAttrs * attrs, AttrPath && attrPath,
static void addAttr(ExprAttrs * attrs, AttrPath & attrPath,
Expr * e, const PosIdx pos, const nix::EvalState & state)
{
AttrPath::iterator i;
@@ -188,7 +188,7 @@ static Formals * toFormals(ParseData & data, ParserFormals * formals,
static Expr * stripIndentation(const PosIdx pos, SymbolTable & symbols,
std::vector<std::pair<PosIdx, std::variant<Expr *, StringToken>>> && es)
std::vector<std::pair<PosIdx, std::variant<Expr *, StringToken>>> & es)
{
if (es.empty()) return new ExprString("");
@@ -268,7 +268,7 @@ static Expr * stripIndentation(const PosIdx pos, SymbolTable & symbols,
s2 = std::string(s2, 0, p + 1);
}
es2->emplace_back(i->first, new ExprString(std::move(s2)));
es2->emplace_back(i->first, new ExprString(s2));
};
for (; i != es.end(); ++i, --n) {
std::visit(overloaded { trimExpr, trimString }, i->second);
@@ -400,21 +400,21 @@ expr_op
| '-' expr_op %prec NEGATE { $$ = new ExprCall(CUR_POS, new ExprVar(data->symbols.create("__sub")), {new ExprInt(0), $2}); }
| expr_op EQ expr_op { $$ = new ExprOpEq($1, $3); }
| expr_op NEQ expr_op { $$ = new ExprOpNEq($1, $3); }
| expr_op '<' expr_op { $$ = new ExprCall(makeCurPos(@2, data), new ExprVar(data->symbols.create("__lessThan")), {$1, $3}); }
| expr_op LEQ expr_op { $$ = new ExprOpNot(new ExprCall(makeCurPos(@2, data), new ExprVar(data->symbols.create("__lessThan")), {$3, $1})); }
| expr_op '>' expr_op { $$ = new ExprCall(makeCurPos(@2, data), new ExprVar(data->symbols.create("__lessThan")), {$3, $1}); }
| expr_op GEQ expr_op { $$ = new ExprOpNot(new ExprCall(makeCurPos(@2, data), new ExprVar(data->symbols.create("__lessThan")), {$1, $3})); }
| expr_op AND expr_op { $$ = new ExprOpAnd(makeCurPos(@2, data), $1, $3); }
| expr_op OR expr_op { $$ = new ExprOpOr(makeCurPos(@2, data), $1, $3); }
| expr_op IMPL expr_op { $$ = new ExprOpImpl(makeCurPos(@2, data), $1, $3); }
| expr_op UPDATE expr_op { $$ = new ExprOpUpdate(makeCurPos(@2, data), $1, $3); }
| expr_op '?' attrpath { $$ = new ExprOpHasAttr($1, std::move(*$3)); delete $3; }
| expr_op '<' expr_op { $$ = new ExprCall(CUR_POS, new ExprVar(data->symbols.create("__lessThan")), {$1, $3}); }
| expr_op LEQ expr_op { $$ = new ExprOpNot(new ExprCall(CUR_POS, new ExprVar(data->symbols.create("__lessThan")), {$3, $1})); }
| expr_op '>' expr_op { $$ = new ExprCall(CUR_POS, new ExprVar(data->symbols.create("__lessThan")), {$3, $1}); }
| expr_op GEQ expr_op { $$ = new ExprOpNot(new ExprCall(CUR_POS, new ExprVar(data->symbols.create("__lessThan")), {$1, $3})); }
| expr_op AND expr_op { $$ = new ExprOpAnd(CUR_POS, $1, $3); }
| expr_op OR expr_op { $$ = new ExprOpOr(CUR_POS, $1, $3); }
| expr_op IMPL expr_op { $$ = new ExprOpImpl(CUR_POS, $1, $3); }
| expr_op UPDATE expr_op { $$ = new ExprOpUpdate(CUR_POS, $1, $3); }
| expr_op '?' attrpath { $$ = new ExprOpHasAttr($1, *$3); }
| expr_op '+' expr_op
{ $$ = new ExprConcatStrings(makeCurPos(@2, data), false, new std::vector<std::pair<PosIdx, Expr *> >({{makeCurPos(@1, data), $1}, {makeCurPos(@3, data), $3}})); }
| expr_op '-' expr_op { $$ = new ExprCall(makeCurPos(@2, data), new ExprVar(data->symbols.create("__sub")), {$1, $3}); }
| expr_op '*' expr_op { $$ = new ExprCall(makeCurPos(@2, data), new ExprVar(data->symbols.create("__mul")), {$1, $3}); }
| expr_op '/' expr_op { $$ = new ExprCall(makeCurPos(@2, data), new ExprVar(data->symbols.create("__div")), {$1, $3}); }
| expr_op CONCAT expr_op { $$ = new ExprOpConcatLists(makeCurPos(@2, data), $1, $3); }
{ $$ = new ExprConcatStrings(CUR_POS, false, new std::vector<std::pair<PosIdx, Expr *>>({{makeCurPos(@1, data), $1}, {makeCurPos(@3, data), $3}})); }
| expr_op '-' expr_op { $$ = new ExprCall(CUR_POS, new ExprVar(data->symbols.create("__sub")), {$1, $3}); }
| expr_op '*' expr_op { $$ = new ExprCall(CUR_POS, new ExprVar(data->symbols.create("__mul")), {$1, $3}); }
| expr_op '/' expr_op { $$ = new ExprCall(CUR_POS, new ExprVar(data->symbols.create("__div")), {$1, $3}); }
| expr_op CONCAT expr_op { $$ = new ExprOpConcatLists(CUR_POS, $1, $3); }
| expr_app
;
@@ -431,14 +431,14 @@ expr_app
expr_select
: expr_simple '.' attrpath
{ $$ = new ExprSelect(CUR_POS, $1, std::move(*$3), nullptr); delete $3; }
{ $$ = new ExprSelect(CUR_POS, $1, *$3, 0); }
| expr_simple '.' attrpath OR_KW expr_select
{ $$ = new ExprSelect(CUR_POS, $1, std::move(*$3), $5); delete $3; }
{ $$ = new ExprSelect(CUR_POS, $1, *$3, $5); }
| /* Backwards compatibility: because Nixpkgs has a rarely used
function named or, allow stuff like map or [...]. */
expr_simple OR_KW
{ $$ = new ExprCall(CUR_POS, $1, {new ExprVar(CUR_POS, data->symbols.create("or"))}); }
| expr_simple
| expr_simple { $$ = $1; }
;
expr_simple
@@ -453,10 +453,9 @@ expr_simple
| FLOAT { $$ = new ExprFloat($1); }
| '"' string_parts '"' { $$ = $2; }
| IND_STRING_OPEN ind_string_parts IND_STRING_CLOSE {
$$ = stripIndentation(CUR_POS, data->symbols, std::move(*$2));
delete $2;
$$ = stripIndentation(CUR_POS, data->symbols, *$2);
}
| path_start PATH_END
| path_start PATH_END { $$ = $1; }
| path_start string_parts_interpolated PATH_END {
$2->insert($2->begin(), {makeCurPos(@1, data), $1});
$$ = new ExprConcatStrings(CUR_POS, false, $2);
@@ -466,7 +465,7 @@ expr_simple
$$ = new ExprCall(CUR_POS,
new ExprVar(data->symbols.create("__findFile")),
{new ExprVar(data->symbols.create("__nixPath")),
new ExprString(std::move(path))});
new ExprString(path)});
}
| URI {
static bool noURLLiterals = settings.isExperimentalFeatureEnabled(Xp::NoUrlLiterals);
@@ -534,7 +533,7 @@ ind_string_parts
;
binds
: binds attrpath '=' expr ';' { $$ = $1; addAttr($$, std::move(*$2), $4, makeCurPos(@2, data), data->state); delete $2; }
: binds attrpath '=' expr ';' { $$ = $1; addAttr($$, *$2, $4, makeCurPos(@2, data), data->state); }
| binds INHERIT attrs ';'
{ $$ = $1;
for (auto & i : *$3) {
@@ -543,7 +542,6 @@ binds
auto pos = makeCurPos(@3, data);
$$->attrs.emplace(i.symbol, ExprAttrs::AttrDef(new ExprVar(CUR_POS, i.symbol), pos, true));
}
delete $3;
}
| binds INHERIT '(' expr ')' attrs ';'
{ $$ = $1;
@@ -553,7 +551,6 @@ binds
dupAttr(data->state, i.symbol, makeCurPos(@6, data), $$->attrs[i.symbol].pos);
$$->attrs.emplace(i.symbol, ExprAttrs::AttrDef(new ExprSelect(CUR_POS, $4, i.symbol), makeCurPos(@6, data)));
}
delete $6;
}
| { $$ = new ExprAttrs(makeCurPos(@0, data)); }
;
@@ -599,7 +596,7 @@ attrpath
;
attr
: ID
: ID { $$ = $1; }
| OR_KW { $$ = {"or", 2}; }
;
@@ -615,9 +612,9 @@ expr_list
formals
: formal ',' formals
{ $$ = $3; $$->formals.emplace_back(*$1); delete $1; }
{ $$ = $3; $$->formals.push_back(*$1); }
| formal
{ $$ = new ParserFormals; $$->formals.emplace_back(*$1); $$->ellipsis = false; delete $1; }
{ $$ = new ParserFormals; $$->formals.push_back(*$1); $$->ellipsis = false; }
|
{ $$ = new ParserFormals; $$->ellipsis = false; }
| ELLIPSIS
@@ -785,13 +782,13 @@ Path EvalState::findFile(SearchPath & searchPath, const std::string_view path, c
if (hasPrefix(path, "nix/"))
return concatStrings(corepkgsPrefix, path.substr(4));
debugThrow(ThrownError({
debugThrowLastTrace(ThrownError({
.msg = hintfmt(evalSettings.pureEval
? "cannot look up '<%s>' in pure evaluation mode (use '--impure' to override)"
: "file '%s' was not found in the Nix search path (add it using $NIX_PATH or -I)",
path),
.errPos = positions[pos]
}), 0, 0);
}));
}
@@ -805,7 +802,7 @@ std::pair<bool, std::string> EvalState::resolveSearchPathElem(const SearchPathEl
if (EvalSettings::isPseudoUrl(elem.second)) {
try {
auto storePath = fetchers::downloadTarball(
store, EvalSettings::resolvePseudoUrl(elem.second), "source", false).first.storePath;
store, EvalSettings::resolvePseudoUrl(elem.second), "source", false).tree.storePath;
res = { true, store->toRealPath(storePath) };
} catch (FileTransferError & e) {
logWarning({

File diff suppressed because it is too large Load Diff

View File

@@ -8,7 +8,7 @@ namespace nix {
static void prim_unsafeDiscardStringContext(EvalState & state, const PosIdx pos, Value * * args, Value & v)
{
PathSet context;
auto s = state.coerceToString(pos, *args[0], context, "while evaluating the argument passed to builtins.unsafeDiscardStringContext");
auto s = state.coerceToString(pos, *args[0], context);
v.mkString(*s);
}
@@ -18,7 +18,7 @@ static RegisterPrimOp primop_unsafeDiscardStringContext("__unsafeDiscardStringCo
static void prim_hasContext(EvalState & state, const PosIdx pos, Value * * args, Value & v)
{
PathSet context;
state.forceString(*args[0], context, pos, "while evaluating the argument passed to builtins.hasContext");
state.forceString(*args[0], context, pos);
v.mkBool(!context.empty());
}
@@ -34,7 +34,7 @@ static RegisterPrimOp primop_hasContext("__hasContext", 1, prim_hasContext);
static void prim_unsafeDiscardOutputDependency(EvalState & state, const PosIdx pos, Value * * args, Value & v)
{
PathSet context;
auto s = state.coerceToString(pos, *args[0], context, "while evaluating the argument passed to builtins.unsafeDiscardOutputDependency");
auto s = state.coerceToString(pos, *args[0], context);
PathSet context2;
for (auto && p : context) {
@@ -80,7 +80,7 @@ static void prim_getContext(EvalState & state, const PosIdx pos, Value * * args,
Strings outputs;
};
PathSet context;
state.forceString(*args[0], context, pos, "while evaluating the argument passed to builtins.getContext");
state.forceString(*args[0], context, pos);
auto contextInfos = std::map<StorePath, ContextInfo>();
for (const auto & p : context) {
NixStringContextElem ctx = NixStringContextElem::parse(*state.store, p);
@@ -130,9 +130,9 @@ static RegisterPrimOp primop_getContext("__getContext", 1, prim_getContext);
static void prim_appendContext(EvalState & state, const PosIdx pos, Value * * args, Value & v)
{
PathSet context;
auto orig = state.forceString(*args[0], context, noPos, "while evaluating the first argument passed to builtins.appendContext");
auto orig = state.forceString(*args[0], context, pos);
state.forceAttrs(*args[1], pos, "while evaluating the second argument passed to builtins.appendContext");
state.forceAttrs(*args[1], pos);
auto sPath = state.symbols.create("path");
auto sAllOutputs = state.symbols.create("allOutputs");
@@ -140,24 +140,24 @@ static void prim_appendContext(EvalState & state, const PosIdx pos, Value * * ar
const auto & name = state.symbols[i.name];
if (!state.store->isStorePath(name))
throw EvalError({
.msg = hintfmt("context key '%s' is not a store path", name),
.msg = hintfmt("Context key '%s' is not a store path", name),
.errPos = state.positions[i.pos]
});
if (!settings.readOnlyMode)
state.store->ensurePath(state.store->parseStorePath(name));
state.forceAttrs(*i.value, i.pos, "while evaluating the value of a string context");
state.forceAttrs(*i.value, i.pos);
auto iter = i.value->attrs->find(sPath);
if (iter != i.value->attrs->end()) {
if (state.forceBool(*iter->value, iter->pos, "while evaluating the `path` attribute of a string context"))
if (state.forceBool(*iter->value, iter->pos))
context.emplace(name);
}
iter = i.value->attrs->find(sAllOutputs);
if (iter != i.value->attrs->end()) {
if (state.forceBool(*iter->value, iter->pos, "while evaluating the `allOutputs` attribute of a string context")) {
if (state.forceBool(*iter->value, iter->pos)) {
if (!isDerivation(name)) {
throw EvalError({
.msg = hintfmt("tried to add all-outputs context of %s, which is not a derivation, to a string", name),
.msg = hintfmt("Tried to add all-outputs context of %s, which is not a derivation, to a string", name),
.errPos = state.positions[i.pos]
});
}
@@ -167,15 +167,15 @@ static void prim_appendContext(EvalState & state, const PosIdx pos, Value * * ar
iter = i.value->attrs->find(state.sOutputs);
if (iter != i.value->attrs->end()) {
state.forceList(*iter->value, iter->pos, "while evaluating the `outputs` attribute of a string context");
state.forceList(*iter->value, iter->pos);
if (iter->value->listSize() && !isDerivation(name)) {
throw EvalError({
.msg = hintfmt("tried to add derivation output context of %s, which is not a derivation, to a string", name),
.msg = hintfmt("Tried to add derivation output context of %s, which is not a derivation, to a string", name),
.errPos = state.positions[i.pos]
});
}
for (auto elem : iter->value->listItems()) {
auto outputName = state.forceStringNoCtx(*elem, iter->pos, "while evaluating an output name within a string context");
auto outputName = state.forceStringNoCtx(*elem, iter->pos);
context.insert(concatStrings("!", outputName, "!", name));
}
}

View File

@@ -7,7 +7,7 @@ namespace nix {
static void prim_fetchClosure(EvalState & state, const PosIdx pos, Value * * args, Value & v)
{
state.forceAttrs(*args[0], pos, "while evaluating the argument passed to builtins.fetchClosure");
state.forceAttrs(*args[0], pos);
std::optional<std::string> fromStoreUrl;
std::optional<StorePath> fromPath;
@@ -19,8 +19,7 @@ static void prim_fetchClosure(EvalState & state, const PosIdx pos, Value * * arg
if (attrName == "fromPath") {
PathSet context;
fromPath = state.coerceToStorePath(attr.pos, *attr.value, context,
"while evaluating the 'fromPath' attribute passed to builtins.fetchClosure");
fromPath = state.coerceToStorePath(attr.pos, *attr.value, context);
}
else if (attrName == "toPath") {
@@ -28,14 +27,12 @@ static void prim_fetchClosure(EvalState & state, const PosIdx pos, Value * * arg
toCA = true;
if (attr.value->type() != nString || attr.value->string.s != std::string("")) {
PathSet context;
toPath = state.coerceToStorePath(attr.pos, *attr.value, context,
"while evaluating the 'toPath' attribute passed to builtins.fetchClosure");
toPath = state.coerceToStorePath(attr.pos, *attr.value, context);
}
}
else if (attrName == "fromStore")
fromStoreUrl = state.forceStringNoCtx(*attr.value, attr.pos,
"while evaluating the 'fromStore' attribute passed to builtins.fetchClosure");
fromStoreUrl = state.forceStringNoCtx(*attr.value, attr.pos);
else
throw Error({

View File

@@ -19,23 +19,23 @@ static void prim_fetchMercurial(EvalState & state, const PosIdx pos, Value * * a
if (args[0]->type() == nAttrs) {
state.forceAttrs(*args[0], pos);
for (auto & attr : *args[0]->attrs) {
std::string_view n(state.symbols[attr.name]);
if (n == "url")
url = state.coerceToString(attr.pos, *attr.value, context,
"while evaluating the `url` attribute passed to builtins.fetchMercurial",
false, false).toOwned();
url = state.coerceToString(attr.pos, *attr.value, context, false, false).toOwned();
else if (n == "rev") {
// Ugly: unlike fetchGit, here the "rev" attribute can
// be both a revision or a branch/tag name.
auto value = state.forceStringNoCtx(*attr.value, attr.pos, "while evaluating the `rev` attribute passed to builtins.fetchMercurial");
auto value = state.forceStringNoCtx(*attr.value, attr.pos);
if (std::regex_match(value.begin(), value.end(), revRegex))
rev = Hash::parseAny(value, htSHA1);
else
ref = value;
}
else if (n == "name")
name = state.forceStringNoCtx(*attr.value, attr.pos, "while evaluating the `name` attribute passed to builtins.fetchMercurial");
name = state.forceStringNoCtx(*attr.value, attr.pos);
else
throw EvalError({
.msg = hintfmt("unsupported argument '%s' to 'fetchMercurial'", state.symbols[attr.name]),
@@ -50,9 +50,7 @@ static void prim_fetchMercurial(EvalState & state, const PosIdx pos, Value * * a
});
} else
url = state.coerceToString(pos, *args[0], context,
"while evaluating the first argument passed to builtins.fetchMercurial",
false, false).toOwned();
url = state.coerceToString(pos, *args[0], context, false, false).toOwned();
// FIXME: git externals probably can be used to bypass the URI
// whitelist. Ah well.

View File

@@ -4,7 +4,6 @@
#include "fetchers.hh"
#include "filetransfer.hh"
#include "registry.hh"
#include "url.hh"
#include <ctime>
#include <iomanip>
@@ -69,16 +68,7 @@ void emitTreeAttrs(
std::string fixURI(std::string uri, EvalState & state, const std::string & defaultScheme = "file")
{
state.checkURI(uri);
if (uri.find("://") == std::string::npos) {
const auto p = ParsedURL {
.scheme = defaultScheme,
.authority = "",
.path = uri
};
return p.to_string();
} else {
return uri;
}
return uri.find("://") != std::string::npos ? uri : defaultScheme + "://" + uri;
}
std::string fixURIForGit(std::string uri, EvalState & state)
@@ -112,7 +102,7 @@ static void fetchTree(
state.forceValue(*args[0], pos);
if (args[0]->type() == nAttrs) {
state.forceAttrs(*args[0], pos, "while evaluating the argument passed to builtins.fetchTree");
state.forceAttrs(*args[0], pos);
fetchers::Attrs attrs;
@@ -122,7 +112,7 @@ static void fetchTree(
.msg = hintfmt("unexpected attribute 'type'"),
.errPos = state.positions[pos]
}));
type = state.forceStringNoCtx(*aType->value, aType->pos, "while evaluating the `type` attribute passed to builtins.fetchTree");
type = state.forceStringNoCtx(*aType->value, aType->pos);
} else if (!type)
state.debugThrowLastTrace(EvalError({
.msg = hintfmt("attribute 'type' is missing in call to 'fetchTree'"),
@@ -135,7 +125,7 @@ static void fetchTree(
if (attr.name == state.sType) continue;
state.forceValue(*attr.value, attr.pos);
if (attr.value->type() == nPath || attr.value->type() == nString) {
auto s = state.coerceToString(attr.pos, *attr.value, context, "", false, false).toOwned();
auto s = state.coerceToString(attr.pos, *attr.value, context, false, false).toOwned();
attrs.emplace(state.symbols[attr.name],
state.symbols[attr.name] == "url"
? type == "git"
@@ -161,9 +151,7 @@ static void fetchTree(
input = fetchers::Input::fromAttrs(std::move(attrs));
} else {
auto url = state.coerceToString(pos, *args[0], context,
"while evaluating the first argument passed to the fetcher",
false, false).toOwned();
auto url = state.coerceToString(pos, *args[0], context, false, false).toOwned();
if (type == "git") {
fetchers::Attrs attrs;
@@ -207,14 +195,16 @@ static void fetch(EvalState & state, const PosIdx pos, Value * * args, Value & v
if (args[0]->type() == nAttrs) {
state.forceAttrs(*args[0], pos);
for (auto & attr : *args[0]->attrs) {
std::string_view n(state.symbols[attr.name]);
if (n == "url")
url = state.forceStringNoCtx(*attr.value, attr.pos, "while evaluating the url we should fetch");
url = state.forceStringNoCtx(*attr.value, attr.pos);
else if (n == "sha256")
expectedHash = newHashAllowEmpty(state.forceStringNoCtx(*attr.value, attr.pos, "while evaluating the sha256 of the content we should fetch"), htSHA256);
expectedHash = newHashAllowEmpty(state.forceStringNoCtx(*attr.value, attr.pos), htSHA256);
else if (n == "name")
name = state.forceStringNoCtx(*attr.value, attr.pos, "while evaluating the name of the content we should fetch");
name = state.forceStringNoCtx(*attr.value, attr.pos);
else
state.debugThrowLastTrace(EvalError({
.msg = hintfmt("unsupported argument '%s' to '%s'", n, who),
@@ -228,7 +218,7 @@ static void fetch(EvalState & state, const PosIdx pos, Value * * args, Value & v
.errPos = state.positions[pos]
}));
} else
url = state.forceStringNoCtx(*args[0], pos, "while evaluating the url we should fetch");
url = state.forceStringNoCtx(*args[0], pos);
if (who == "fetchTarball")
url = evalSettings.resolvePseudoUrl(*url);
@@ -258,7 +248,7 @@ static void fetch(EvalState & state, const PosIdx pos, Value * * args, Value & v
// https://github.com/NixOS/nix/issues/4313
auto storePath =
unpack
? fetchers::downloadTarball(state.store, *url, name, (bool) expectedHash).first.storePath
? fetchers::downloadTarball(state.store, *url, name, (bool) expectedHash).tree.storePath
: fetchers::downloadFile(state.store, *url, name, (bool) expectedHash).storePath;
if (expectedHash) {
@@ -466,17 +456,6 @@ static RegisterPrimOp primop_fetchGit({
> **Note**
>
> This behavior is disabled in *Pure evaluation mode*.
- To fetch the content of a checked-out work directory:
```nix
builtins.fetchGit ./work-dir
```
If the URL points to a local directory, and no `ref` or `rev` is
given, `fetchGit` will use the current content of the checked-out
files, even if they are not committed or added to Git's index. It will
only consider files added to the Git repository, as listed by `git ls-files`.
)",
.fun = prim_fetchGit,
});

View File

@@ -7,7 +7,7 @@ namespace nix {
static void prim_fromTOML(EvalState & state, const PosIdx pos, Value * * args, Value & val)
{
auto toml = state.forceStringNoCtx(*args[0], pos, "while evaluating the argument passed to builtins.fromTOML");
auto toml = state.forceStringNoCtx(*args[0], pos);
std::istringstream tomlStream(std::string{toml});

File diff suppressed because it is too large Load Diff

View File

@@ -1,4 +1,4 @@
#include "tests/libexpr.hh"
#include "libexprtests.hh"
#include "value-to-json.hh"
namespace nix {

View File

@@ -7,19 +7,18 @@
#include "eval-inline.hh"
#include "store-api.hh"
#include "tests/libstore.hh"
namespace nix {
class LibExprTest : public LibStoreTest {
class LibExprTest : public ::testing::Test {
public:
static void SetUpTestSuite() {
LibStoreTest::SetUpTestSuite();
initLibStore();
initGC();
}
protected:
LibExprTest()
: LibStoreTest()
: store(openStore("dummy://"))
, state({}, store)
{
}
@@ -37,6 +36,7 @@ namespace nix {
return state.symbols.create(value);
}
ref<Store> store;
EvalState state;
};

View File

@@ -2,8 +2,6 @@ check: libexpr-tests_RUN
programs += libexpr-tests
libexpr-tests_NAME := libnixexpr-tests
libexpr-tests_DIR := $(d)
libexpr-tests_INSTALL_DIR :=
@@ -14,6 +12,6 @@ libexpr-tests_SOURCES := \
libexpr-tests_CXXFLAGS += -I src/libexpr -I src/libutil -I src/libstore -I src/libexpr/tests
libexpr-tests_LIBS = libstore-tests libutils-tests libexpr libutil libstore libfetchers
libexpr-tests_LIBS = libexpr libutil libstore libfetchers
libexpr-tests_LDFLAGS := -lrapidcheck $(GTEST_LIBS) -lgmock
libexpr-tests_LDFLAGS := $(GTEST_LIBS) -lgmock

View File

@@ -1,7 +1,7 @@
#include <gmock/gmock.h>
#include <gtest/gtest.h>
#include "tests/libexpr.hh"
#include "libexprtests.hh"
namespace nix {
class CaptureLogger : public Logger
@@ -823,10 +823,4 @@ namespace nix {
for (const auto [n, elem] : enumerate(v.listItems()))
ASSERT_THAT(*elem, IsStringEq(expected[n]));
}
TEST_F(PrimOpTest, genericClosure_not_strict) {
// Operator should not be used when startSet is empty
auto v = eval("builtins.genericClosure { startSet = []; }");
ASSERT_THAT(v, IsListOfSize(0));
}
} /* namespace nix */

View File

@@ -1,4 +1,4 @@
#include "tests/libexpr.hh"
#include "libexprtests.hh"
namespace nix {
// Testing of trivial expressions

View File

@@ -1,10 +1,6 @@
#include <nlohmann/json.hpp>
#include <gtest/gtest.h>
#include <rapidcheck/gtest.h>
#include "value/context.hh"
#include "tests/path.hh"
#include "tests/libexpr.hh"
#include "tests/value/context.hh"
#include "libexprtests.hh"
namespace nix {
@@ -74,54 +70,3 @@ TEST_F(NixStringContextElemTest, built) {
}
}
namespace rc {
using namespace nix;
Gen<NixStringContextElem::Opaque> Arbitrary<NixStringContextElem::Opaque>::arbitrary()
{
return gen::just(NixStringContextElem::Opaque {
.path = *gen::arbitrary<StorePath>(),
});
}
Gen<NixStringContextElem::DrvDeep> Arbitrary<NixStringContextElem::DrvDeep>::arbitrary()
{
return gen::just(NixStringContextElem::DrvDeep {
.drvPath = *gen::arbitrary<StorePath>(),
});
}
Gen<NixStringContextElem::Built> Arbitrary<NixStringContextElem::Built>::arbitrary()
{
return gen::just(NixStringContextElem::Built {
.drvPath = *gen::arbitrary<StorePath>(),
.output = (*gen::arbitrary<StorePathName>()).name,
});
}
Gen<NixStringContextElem> Arbitrary<NixStringContextElem>::arbitrary()
{
switch (*gen::inRange<uint8_t>(0, 2)) {
case 0:
return gen::just<NixStringContextElem>(*gen::arbitrary<NixStringContextElem::Opaque>());
case 1:
return gen::just<NixStringContextElem>(*gen::arbitrary<NixStringContextElem::DrvDeep>());
default:
return gen::just<NixStringContextElem>(*gen::arbitrary<NixStringContextElem::Built>());
}
}
}
namespace nix {
RC_GTEST_FIXTURE_PROP(
NixStringContextElemTest,
prop_round_rip,
(const NixStringContextElem & o))
{
RC_ASSERT(o == NixStringContextElem::parse(store(), o.to_string(store())));
}
}

View File

@@ -1,30 +0,0 @@
#pragma once
#include <rapidcheck/gen/Arbitrary.h>
#include <value/context.hh>
namespace rc {
using namespace nix;
template<>
struct Arbitrary<NixStringContextElem::Opaque> {
static Gen<NixStringContextElem::Opaque> arbitrary();
};
template<>
struct Arbitrary<NixStringContextElem::Built> {
static Gen<NixStringContextElem::Built> arbitrary();
};
template<>
struct Arbitrary<NixStringContextElem::DrvDeep> {
static Gen<NixStringContextElem::DrvDeep> arbitrary();
};
template<>
struct Arbitrary<NixStringContextElem> {
static Gen<NixStringContextElem> arbitrary();
};
}

View File

@@ -1,10 +1,9 @@
#pragma once
#include "util.hh"
#include "comparator.hh"
#include "path.hh"
#include <variant>
#include <optional>
#include <nlohmann/json_fwd.hpp>
@@ -32,9 +31,7 @@ class Store;
Encoded as just the path: <path>.
*/
struct NixStringContextElem_Opaque {
StorePath path;
GENERATE_CMP(NixStringContextElem_Opaque, me->path);
StorePath path;
};
/* Path to a derivation and its entire build closure.
@@ -46,9 +43,7 @@ struct NixStringContextElem_Opaque {
Encoded in the form =<drvPath>.
*/
struct NixStringContextElem_DrvDeep {
StorePath drvPath;
GENERATE_CMP(NixStringContextElem_DrvDeep, me->drvPath);
StorePath drvPath;
};
/* Derivation output.
@@ -56,10 +51,8 @@ struct NixStringContextElem_DrvDeep {
Encoded in the form !<output>!<drvPath>.
*/
struct NixStringContextElem_Built {
StorePath drvPath;
std::string output;
GENERATE_CMP(NixStringContextElem_Built, me->drvPath, me->output);
StorePath drvPath;
std::string output;
};
using _NixStringContextElem_Raw = std::variant<

View File

@@ -1,6 +1,7 @@
#pragma once
#include "types.hh"
#include "hash.hh"
#include <variant>

View File

@@ -57,7 +57,7 @@ struct FetchSettings : public Config
```
This example specifies three tokens, one each for accessing
github.com, gitlab.mycompany.com, and gitlab.com.
github.com, gitlab.mycompany.com, and sourceforge.net.
The `input.foo` uses the "gitlab" fetcher, which might
requires specifying the token type along with the token

View File

@@ -159,6 +159,12 @@ std::pair<Tree, Input> Input::fetch(ref<Store> store) const
input.to_string(), *prevLastModified);
}
if (auto prevRev = getRev()) {
if (input.getRev() != prevRev)
throw Error("'rev' attribute mismatch in input '%s', expected %s",
input.to_string(), prevRev->gitRev());
}
if (auto prevRevCount = getRevCount()) {
if (input.getRevCount() != prevRevCount)
throw Error("'revCount' attribute mismatch in input '%s', expected %d",

View File

@@ -141,6 +141,7 @@ struct DownloadFileResult
StorePath storePath;
std::string etag;
std::string effectiveUrl;
std::optional<std::string> immutableUrl;
};
DownloadFileResult downloadFile(
@@ -150,7 +151,14 @@ DownloadFileResult downloadFile(
bool locked,
const Headers & headers = {});
std::pair<Tree, time_t> downloadTarball(
struct DownloadTarballResult
{
Tree tree;
time_t lastModified;
std::optional<std::string> immutableUrl;
};
DownloadTarballResult downloadTarball(
ref<Store> store,
const std::string & url,
const std::string & name,

View File

@@ -615,42 +615,15 @@ struct GitInputScheme : InputScheme
AutoDelete delTmpGitDir(tmpGitDir, true);
runProgram("git", true, { "-c", "init.defaultBranch=" + gitInitialBranch, "init", tmpDir, "--separate-git-dir", tmpGitDir });
{
// TODO: repoDir might lack the ref (it only checks if rev
// exists, see FIXME above) so use a big hammer and fetch
// everything to ensure we get the rev.
Activity act(*logger, lvlTalkative, actUnknown, fmt("making temporary clone of '%s'", repoDir));
runProgram("git", true, { "-C", tmpDir, "fetch", "--quiet", "--force",
"--update-head-ok", "--", repoDir, "refs/*:refs/*" });
}
// TODO: repoDir might lack the ref (it only checks if rev
// exists, see FIXME above) so use a big hammer and fetch
// everything to ensure we get the rev.
runProgram("git", true, { "-C", tmpDir, "fetch", "--quiet", "--force",
"--update-head-ok", "--", repoDir, "refs/*:refs/*" });
runProgram("git", true, { "-C", tmpDir, "checkout", "--quiet", input.getRev()->gitRev() });
/* Ensure that we use the correct origin for fetching
submodules. This matters for submodules with relative
URLs. */
if (isLocal) {
writeFile(tmpGitDir + "/config", readFile(repoDir + "/" + gitDir + "/config"));
/* Restore the config.bare setting we may have just
copied erroneously from the user's repo. */
runProgram("git", true, { "-C", tmpDir, "config", "core.bare", "false" });
} else
runProgram("git", true, { "-C", tmpDir, "config", "remote.origin.url", actualUrl });
/* As an optimisation, copy the modules directory of the
source repo if it exists. */
auto modulesPath = repoDir + "/" + gitDir + "/modules";
if (pathExists(modulesPath)) {
Activity act(*logger, lvlTalkative, actUnknown, fmt("copying submodules of '%s'", actualUrl));
runProgram("cp", true, { "-R", "--", modulesPath, tmpGitDir + "/modules" });
}
{
Activity act(*logger, lvlTalkative, actUnknown, fmt("fetching submodules of '%s'", actualUrl));
runProgram("git", true, { "-C", tmpDir, "submodule", "--quiet", "update", "--init", "--recursive" });
}
runProgram("git", true, { "-C", tmpDir, "remote", "add", "origin", actualUrl });
runProgram("git", true, { "-C", tmpDir, "submodule", "--quiet", "update", "--init", "--recursive" });
filter = isNotDotGitDirectory;
} else {

View File

@@ -207,21 +207,21 @@ struct GitArchiveInputScheme : InputScheme
auto url = getDownloadUrl(input);
auto [tree, lastModified] = downloadTarball(store, url.url, input.getName(), true, url.headers);
auto result = downloadTarball(store, url.url, input.getName(), true, url.headers);
input.attrs.insert_or_assign("lastModified", uint64_t(lastModified));
input.attrs.insert_or_assign("lastModified", uint64_t(result.lastModified));
getCache()->add(
store,
lockedAttrs,
{
{"rev", rev->gitRev()},
{"lastModified", uint64_t(lastModified)}
{"lastModified", uint64_t(result.lastModified)}
},
tree.storePath,
result.tree.storePath,
true);
return {std::move(tree.storePath), input};
return {result.tree.storePath, input};
}
};

View File

@@ -32,7 +32,8 @@ DownloadFileResult downloadFile(
return {
.storePath = std::move(cached->storePath),
.etag = getStrAttr(cached->infoAttrs, "etag"),
.effectiveUrl = getStrAttr(cached->infoAttrs, "url")
.effectiveUrl = getStrAttr(cached->infoAttrs, "url"),
.immutableUrl = maybeGetStrAttr(cached->infoAttrs, "immutableUrl"),
};
};
@@ -55,12 +56,14 @@ DownloadFileResult downloadFile(
}
// FIXME: write to temporary file.
Attrs infoAttrs({
{"etag", res.etag},
{"url", res.effectiveUri},
});
if (res.immutableUrl)
infoAttrs.emplace("immutableUrl", *res.immutableUrl);
std::optional<StorePath> storePath;
if (res.cached) {
@@ -107,10 +110,11 @@ DownloadFileResult downloadFile(
.storePath = std::move(*storePath),
.etag = res.etag,
.effectiveUrl = res.effectiveUri,
.immutableUrl = res.immutableUrl,
};
}
std::pair<Tree, time_t> downloadTarball(
DownloadTarballResult downloadTarball(
ref<Store> store,
const std::string & url,
const std::string & name,
@@ -127,8 +131,9 @@ std::pair<Tree, time_t> downloadTarball(
if (cached && !cached->expired)
return {
Tree { .actualPath = store->toRealPath(cached->storePath), .storePath = std::move(cached->storePath) },
getIntAttr(cached->infoAttrs, "lastModified")
.tree = Tree { .actualPath = store->toRealPath(cached->storePath), .storePath = std::move(cached->storePath) },
.lastModified = (time_t) getIntAttr(cached->infoAttrs, "lastModified"),
.immutableUrl = maybeGetStrAttr(cached->infoAttrs, "immutableUrl"),
};
auto res = downloadFile(store, url, name, locked, headers);
@@ -156,6 +161,9 @@ std::pair<Tree, time_t> downloadTarball(
{"etag", res.etag},
});
if (res.immutableUrl)
infoAttrs.emplace("immutableUrl", *res.immutableUrl);
getCache()->add(
store,
inAttrs,
@@ -164,8 +172,9 @@ std::pair<Tree, time_t> downloadTarball(
locked);
return {
Tree { .actualPath = store->toRealPath(*unpackedStorePath), .storePath = std::move(*unpackedStorePath) },
lastModified,
.tree = Tree { .actualPath = store->toRealPath(*unpackedStorePath), .storePath = std::move(*unpackedStorePath) },
.lastModified = lastModified,
.immutableUrl = res.immutableUrl,
};
}
@@ -185,21 +194,33 @@ struct CurlInputScheme : InputScheme
virtual bool isValidURL(const ParsedURL & url) const = 0;
std::optional<Input> inputFromURL(const ParsedURL & url) const override
std::optional<Input> inputFromURL(const ParsedURL & _url) const override
{
if (!isValidURL(url))
if (!isValidURL(_url))
return std::nullopt;
Input input;
auto urlWithoutApplicationScheme = url;
urlWithoutApplicationScheme.scheme = parseUrlScheme(url.scheme).transport;
auto url = _url;
url.scheme = parseUrlScheme(url.scheme).transport;
input.attrs.insert_or_assign("type", inputType());
input.attrs.insert_or_assign("url", urlWithoutApplicationScheme.to_string());
auto narHash = url.query.find("narHash");
if (narHash != url.query.end())
input.attrs.insert_or_assign("narHash", narHash->second);
if (auto i = get(url.query, "rev"))
input.attrs.insert_or_assign("rev", *i);
if (auto i = get(url.query, "revCount"))
if (auto n = string2Int<uint64_t>(*i))
input.attrs.insert_or_assign("revCount", *n);
url.query.erase("rev");
url.query.erase("revCount");
input.attrs.insert_or_assign("type", inputType());
input.attrs.insert_or_assign("url", url.to_string());
return input;
}
@@ -208,7 +229,8 @@ struct CurlInputScheme : InputScheme
auto type = maybeGetStrAttr(attrs, "type");
if (type != inputType()) return {};
std::set<std::string> allowedNames = {"type", "url", "narHash", "name", "unpack"};
// FIXME: some of these only apply to TarballInputScheme.
std::set<std::string> allowedNames = {"type", "url", "narHash", "name", "unpack", "rev", "revCount", "lastModified"};
for (auto & [name, value] : attrs)
if (!allowedNames.count(name))
throw Error("unsupported %s input attribute '%s'", *type, name);
@@ -271,10 +293,25 @@ struct TarballInputScheme : CurlInputScheme
: hasTarballExtension(url.path));
}
std::pair<StorePath, Input> fetch(ref<Store> store, const Input & input) override
std::pair<StorePath, Input> fetch(ref<Store> store, const Input & _input) override
{
auto tree = downloadTarball(store, getStrAttr(input.attrs, "url"), input.getName(), false).first;
return {std::move(tree.storePath), input};
Input input(_input);
auto url = getStrAttr(input.attrs, "url");
auto result = downloadTarball(store, url, input.getName(), false);
if (result.immutableUrl) {
auto immutableInput = Input::fromURL(*result.immutableUrl);
// FIXME: would be nice to support arbitrary flakerefs
// here, e.g. git flakes.
if (immutableInput.getType() != "tarball")
throw Error("tarball 'Link' headers that redirect to non-tarball URLs are not supported");
input = immutableInput;
}
if (result.lastModified && !input.attrs.count("lastModified"))
input.attrs.insert_or_assign("lastModified", uint64_t(result.lastModified));
return {result.tree.storePath, std::move(input)};
}
};

View File

@@ -6,4 +6,4 @@ Name: Nix
Description: Nix Package Manager
Version: @PACKAGE_VERSION@
Libs: -L${libdir} -lnixmain
Cflags: -I${includedir}/nix -std=c++2a
Cflags: -I${includedir}/nix -std=c++17

View File

@@ -404,6 +404,8 @@ int handleExceptions(const std::string & programName, std::function<void()> fun)
return 1;
} catch (BaseError & e) {
logError(e.info());
if (e.hasTrace() && !loggerSettings.showTrace.get())
printError("(use '--show-trace' to show detailed location information)");
return e.status;
} catch (std::bad_alloc & e) {
printError(error + "out of memory");

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