Compare commits

...

219 Commits

Author SHA1 Message Date
Befator
6f3067817c Small derivation builder fixes 2026-03-06 17:05:41 +01:00
Befator
604d3ab8d2 Merge branch 'windows-derivation-builder' 2026-03-05 16:51:05 +01:00
Befator
dd76afbf60 Merge remote-tracking branch 'upstream/master' 2026-03-05 16:32:55 +01:00
Befator
2192406fe1 Small windows fixes 2026-03-05 16:25:28 +01:00
Sergei Zimmerman
124b277764 Merge pull request #15411 from NixOS/self-pipe-trick-reaping-children
libcmd,daemon: Reap children via a self-pipe trick
2026-03-05 08:01:21 +00:00
Sergei Zimmerman
c28203f097 libcmd,daemon: Reap children via a self-pipe trick
Somewhat better solution for https://github.com/NixOS/nix/issues/15394. Instead
of reaping immediately in the signal handler we instead punt that at the poll loop.
This way Pid destructor has a chance to properly run instead of being rudely interrupted
by the signal handler.

See: https://man7.org/tlpi/code/online/dist/altio/self_pipe.c.html
2026-03-05 04:08:34 +03:00
Sergei Zimmerman
116af62b8e Merge pull request #15402 from sheeeng/patch-1
fix: typographical error
2026-03-04 13:09:24 +00:00
Leonard Sheng Sheng Lee
6be774a9ed fix: typographical error
Modify `derivatons` to `derivations`.
2026-03-03 22:15:07 +01:00
John Ericson
9a30578624 Merge pull request #15401 from NixOS/more-nofollow
Expose FinalSymlink argument in more places, sprinkle more O_NOFOLLOW in various places
2026-03-03 21:13:27 +00:00
Sergei Zimmerman
ae955594d6 libutil: Use Source::drainInto with explicit size parameter in PosixSourceAccessor::readFile
Slightly cleaner. We dont need any of the NONBLOCK logic from drainFD
here since the source is a regular file + this would allow to stuff copy_file_range/sendfile
into the FdSource for more efficient copying to a file descriptor.
2026-03-03 22:22:50 +03:00
Sergei Zimmerman
26679828f7 libutil,libstore: Expose FinalSymlink in writeFile arguments, use in LocalStore
This exposes the knob for controlling final symlink following in
writeFile with O_TRUNC (which I've previously marked with FIXMEs, since that was
quite suspicious looking). Use DontFollow in restorePath for `flat` serialisation
case. Callers already have the invariant that a regular file will be written to
the destination path (which has been deleted recursively via deletePath for good measure).
This just adds for guardrails against any possible SNAFUs.
2026-03-03 22:09:38 +03:00
Sergei Zimmerman
a8d51a0c59 libutil: Address TODO O_NOFOLLOW in recursiveSync 2026-03-03 21:49:06 +03:00
Sergei Zimmerman
17ced6f80c libutil: Add finalSymlink argument to openFileReadonly
We have a couple of places where this would end up being useful.
2026-03-03 21:46:46 +03:00
John Ericson
b516291bea Merge pull request #15399 from NixOS/fixed-length-drain-into
Introduce `Source::drainInto` with explicit length
2026-03-03 17:44:58 +00:00
John Ericson
57e0ad01e7 Merge pull request #15397 from NixOS/fix-various-error-messages-warnings
Improve formatting of various errors and warnings
2026-03-03 17:34:57 +00:00
John Ericson
5e84b57594 Introduce Source::drainInto with explicit length
This is used to deduplicate two spots in the code:

- `git::parseBlob`

- `parseContents` in `archive.cc`

Implementation is a bit more hardened than either of the two originals.
2026-03-03 11:40:45 -05:00
Sergei Zimmerman
6bedefea9e Improve formatting of various errors and warnings
Removes unnecessary `error: warning:` prefixes by using .message() and not .msg().
Also includes attempt counts in the filetransfer warnings. Removes a redundant trace
from the filetransfer errors with FileTransferError (which is already descriptive enough)
and contains the same information basically.
2026-03-03 18:50:17 +03:00
Jörg Thalheim
fa1a370c6f Merge pull request #15386 from NixOS/relative-url-tests
Relative url tests
2026-03-03 08:37:30 +00:00
Jörg Thalheim
c0b4227573 Merge pull request #15387 from NixOS/dependabot/github_actions/actions/download-artifact-8.0.0
build(deps): bump actions/download-artifact from 7.0.0 to 8.0.0
2026-03-03 08:36:38 +00:00
Jörg Thalheim
a2f73abde3 Merge pull request #15388 from NixOS/dependabot/github_actions/actions/upload-artifact-7
build(deps): bump actions/upload-artifact from 6 to 7
2026-03-03 08:36:12 +00:00
John Ericson
c6562fd938 Merge pull request #15391 from obsidiansystems/fix-unix-test-assumptions
Fix unix test assumptions
2026-03-03 06:48:17 +00:00
Jörg Thalheim
0bb0f69f6c Merge pull request #15365 from DigitalBrewStudios/shortname-bundle-bundler
feat(nix): add shortname to bundle bundler arg
2026-03-03 06:37:51 +00:00
Amaan Qureshi
5bedf61b83 tests: fix two assertion crashes during Windows test teardown 2026-03-03 01:01:55 -05:00
Amaan Qureshi
70c6e0a399 tests: use Windows-absolute paths in LocalStore/LocalOverlayStore config tests 2026-03-03 01:01:55 -05:00
Amaan Qureshi
f17d9dee9e tests: use empty temp file instead of /dev/null in machines test 2026-03-03 01:01:55 -05:00
Amaan Qureshi
9534543860 tests: skip and fix nix_api_store tests for Windows/Wine 2026-03-03 01:01:55 -05:00
Amaan Qureshi
08efb2b454 tests: skip symlink-dependent nix_api tests on Windows 2026-03-03 00:06:02 -05:00
Amaan Qureshi
72abbc4164 libstore: use urlPathToPath for store URL path parsing
`store-registration.hh` constructed a `std::filesystem::path` directly
from `percentDecode(uri)`, which broke on Windows because file:// URLs
like `/C:/foo` lack a drive-letter root. Replace with `splitString` +
`urlPathToPath`, which handles Windows drive-letter stripping.
2026-03-03 00:06:02 -05:00
John Ericson
8f6c05ee7a Merge pull request #15390 from obsidiansystems/portable-dup
libutil: Add dupDescriptor function
2026-03-03 04:30:44 +00:00
John Ericson
3df8dbc07c libutil: Add dupDescriptor function
Add a cross-platform function for duplicating file descriptors:
- Unix: uses fcntl(F_DUPFD_CLOEXEC)
- Windows: uses DuplicateHandle

This is useful when code needs to take ownership of a borrowed
descriptor.
2026-03-02 22:39:02 -05:00
John Ericson
05855f5d2a Merge pull request #15009 from amaanq/signature-type
protocol: update JSON output with structured `Signature` type
2026-03-03 02:10:44 +00:00
Amaan Qureshi
d7245ff8ac protocol: update JSON output with structured Signature type
This commit updates the JSON output with a Signature type containing
keyName and sig fields. JSON parsing accepts both formats for backwards
compatibility.

Co-authored-by: John Ericson <John.Ericson@Obsidian.Systems>
2026-03-02 20:18:43 -05:00
dependabot[bot]
539c6a1aaf build(deps): bump actions/upload-artifact from 6 to 7
Bumps [actions/upload-artifact](https://github.com/actions/upload-artifact) from 6 to 7.
- [Release notes](https://github.com/actions/upload-artifact/releases)
- [Commits](https://github.com/actions/upload-artifact/compare/v6...v7)

---
updated-dependencies:
- dependency-name: actions/upload-artifact
  dependency-version: '7'
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-03-02 23:39:12 +00:00
dependabot[bot]
faa16841b6 build(deps): bump actions/download-artifact from 7.0.0 to 8.0.0
Bumps [actions/download-artifact](https://github.com/actions/download-artifact) from 7.0.0 to 8.0.0.
- [Release notes](https://github.com/actions/download-artifact/releases)
- [Commits](37930b1c2a...70fc10c6e5)

---
updated-dependencies:
- dependency-name: actions/download-artifact
  dependency-version: 8.0.0
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-03-02 23:39:07 +00:00
Jörg Thalheim
2d0c5421a5 Merge pull request #15383 from roberth/alt-15300
fix(libexpr-c): pass valid EvalState to primop callback
2026-03-02 21:01:34 +00:00
John Ericson
800d92449f Merge pull request #15376 from obsidiansystems/filesystem-cleanups
Some filesystem cleanups
2026-03-02 20:21:00 +00:00
Robert Hensing
6633c55f8a fix(libexpr-c): pass valid EvalState to primop callback
Due to an erroneous cast, the wrong pointer was passed to these
callbacks, leading to a crash.

We now create a lightweight temporary EvalState wrapper on the stack in each
callback bridge. This also eliminates the need for unsafe_new_with_self
for EvalState construction.

Co-authored-by: Jörg Thalheim <joerg@thalheim.io>
2026-03-02 21:11:20 +01:00
Sergei Zimmerman
5f5b043524 Merge pull request #15382 from roberth/extra-release-notes-2.34
Extra release notes 2.34
2026-03-02 20:03:34 +00:00
John Ericson
4f8f581830 libutil: Reimplement *stat functions on Windows for symlink support
Reimplement `lstat` and `maybeLstat` from first principles on Windows
so they work with symlinks. Properly define `S_IFLNK` and `S_ISLNK`.

Use `GetFileAttributesExW` instead of `_wstat64` since the latter
doesn't properly detect symlinks (reparse points) on Windows.

Key changes:
- Add `S_IFLNK` (0120000) and proper `S_ISLNK` macro for Windows
- Add `windows::fileTimeToUnixTime` to convert FILETIME to Unix time_t
- Add `windows::statFromFileInfo` to populate PosixStat from Windows
  file attributes
- Move `lstat` and `maybeLstat` to platform-specific files
  (unix/file-system.cc and windows/file-system.cc)
- Move `fstat` from file-system.hh to file-system-at.hh, changing
  signature from `int fd` to `Descriptor fd` for cross-platform support
- Windows `lstat` now detects symlinks via FILE_ATTRIBUTE_REPARSE_POINT
  and sets S_IFLNK in st_mode
- Windows `fstat` uses GetFileInformationByHandle

Co-authored-by: Sergei Zimmerman <sergei@zimmerman.foo>
2026-03-02 14:30:32 -05:00
Sergei Zimmerman
5f666eff14 Add release note for GHCR Docker images
(cherry picked from commit 5d054fe91ef95650ba7a7925b15ed9acfc0e7006)
2026-03-02 20:20:45 +01:00
Robert Hensing
62a78c86b0 release notes 2.34: add entries for additional PRs 2026-03-02 20:20:45 +01:00
Jörg Thalheim
35be9ef560 Merge pull request #15322 from obsidiansystems/build-trace-rework
Realisations use regular drv paths again
2026-03-02 19:16:24 +00:00
Sergei Zimmerman
94dbeec833 Merge pull request #15379 from NixOS/bump-version
Bump version
2026-03-02 19:13:22 +00:00
John Ericson
3ed992a3fd Merge pull request #15377 from obsidiansystems/fix-windows-url-paths
Fix windows url paths
2026-03-02 18:38:17 +00:00
Jörg Thalheim
855208ba24 docs: add release notes for build trace rework
The previous commit changed CA derivation realisations to be keyed by
store path instead of hash modulo, affecting binary cache and wire
protocols. Users and tool authors need to understand the impact on
binary cache layout, protocol negotiation, and what migration looks
like.

This documents the key changes: the new build-trace-v2/ cache directory,
the split key/value JSON format, worker protocol feature negotiation,
serve protocol version bump, and the fact that non-CA users are
unaffected.
2026-03-02 13:28:17 -05:00
Sergei Zimmerman
ca42db38a3 Bump version 2026-03-02 21:24:05 +03:00
John Ericson
faca7db633 Revert "Reapply "Use the hash modulo in the derivation outputs""
This reverts commit 100e7cc337.

Unlike the last version, this fixes `--print-out-paths` for old clients

When the client lacks `featureRealisationWithPath` (worker) or version <
2.8 (serve), the daemon was sending an empty `StringMap` for
`builtOutputs`, causing `--print-out-paths` to print nothing. This
commit constructs the old wire format instead, using a dummy `sha256:`
hash since the derivation hash no longer exists. Old clients only need
the output name and path from the JSON.
2026-03-02 13:11:21 -05:00
John Ericson
450c1850c9 libutil: Add finalSymlink parameter to openDirectory
Add a type-safe boolean enum `FinalSymlink` to control whether
`openDirectory` follows symlinks on the final path component.

- `FinalSymlink::Follow` (default): follow symlinks (current behavior)
- `FinalSymlink::DontFollow`: fail if the path is a symlink

On Unix, this uses `O_NOFOLLOW`. On Windows, this uses
`FILE_FLAG_OPEN_REPARSE_POINT`.

Update all call sites with explicit `FinalSymlink` values.
2026-03-02 12:57:06 -05:00
Amaan Qureshi
0d8ca7a888 fetchers: use pathToUrlPath in PathInputScheme::toURL 2026-03-02 12:46:16 -05:00
Amaan Qureshi
9c59f62890 url: handle Windows drive letters in fixGitURL 2026-03-02 12:45:36 -05:00
Amaan Qureshi
f3792cdad5 flakeref: use portable root detection in directory walk loops 2026-03-02 12:45:36 -05:00
Amaan Qureshi
b26f2ca3e6 tests: clear NIX_STORE env in fetchers, expr, and flake test environments 2026-03-02 12:45:36 -05:00
Amaan Qureshi
62d275d7c0 tests: use pathToUrlPath for file:// URL construction in git test 2026-03-02 12:45:36 -05:00
Sergei Zimmerman
ee5381a376 Merge pull request #15331 from lisanna-dettwyler/github-fetcher-url-param-check
Error on invalid URL param in github fetcher
2026-03-02 17:35:39 +00:00
John Ericson
e19c0a5a14 Add test cases for relative ? and # empty explicit params vs none at all
Unlike with absolute (complete) URLs, with relative URLs, this does make
a difference -- explicit empty overrides, implicit empty does not.
2026-03-02 11:51:25 -05:00
John Ericson
30b6bba0fe Convert parseURLRelative tests to parameters test suite
This will allow extending it further in the next commit
2026-03-02 11:49:16 -05:00
Sergei Zimmerman
39e6f66775 Merge pull request #15372 from lisanna-dettwyler/uninstall-add-dirs
Add missing directories to uninstall.md
2026-03-02 16:16:38 +00:00
Sergei Zimmerman
116cfbc221 Merge pull request #15373 from NixOS/value-alloc-cache-thread-local
libexpr: Make Boehm allocation cache thread_local
2026-03-02 16:13:13 +00:00
Sergei Zimmerman
c806a241b8 Merge pull request #15363 from NixOS/release-notes
Release notes for 2.34.0
2026-03-02 15:29:17 +00:00
Lisanna Dettwyler
cdab2b7a36 Add missing directories to uninstall.md
Resolves #15317

Signed-off-by: Lisanna Dettwyler <lisanna.dettwyler@gmail.com>
2026-03-02 10:24:41 -05:00
Lisanna Dettwyler
c88e517f5b Formatting fixes for uninstall.md
Signed-off-by: Lisanna Dettwyler <lisanna.dettwyler@gmail.com>
2026-03-02 10:22:20 -05:00
John Ericson
8999af2236 Merge pull request #15375 from NixOS/read-little-endian-bswap-tests
libutil: More efficient `readLittleEndian`
2026-03-02 15:00:14 +00:00
Sergei Zimmerman
756ea54470 Merge pull request #15374 from NixOS/windows-system-headers
Use #include <...> for Windows system headers
2026-03-02 13:23:49 +00:00
Sergei Zimmerman
9533aef459 libutil: More efficient readLittleEndian
Because unsigned char * can alias anything, the compiler has no choice
but to actually iterate byte-by-byte in readNum and readLittleEndian:

        │      movzbl -0x2f(%rbp),%eax
  12.54 │      movzbl -0x2e(%rbp),%edx
        │      shl    $0x8,%rax
   1.88 │      shl    $0x10,%rdx
        │      or     %rdx,%rax
        │      movzbl -0x30(%rbp),%edx
   5.09 │      or     %rdx,%rax
   2.37 │      movzbl -0x2d(%rbp),%edx
        │      shl    $0x18,%rdx
   3.95 │      or     %rdx,%rax
        │      movzbl -0x2c(%rbp),%edx
        │      shl    $0x20,%rdx
        │      or     %rax,%rdx
   5.59 │      movzbl -0x2b(%rbp),%eax
   3.29 │      shl    $0x28,%rax
        │      or     %rdx,%rax
   7.83 │      movzbl -0x2a(%rbp),%edx
        │      shl    $0x30,%rdx
        │      or     %rax,%rdx
   8.22 │      movzbl -0x29(%rbp),%eax
        │      shl    $0x38,%rax
        │      or     %rdx,%rax
   6.42 │      mov    %rax,%rcx
        │      mov    %rax,-0x60(%rbp)
   1.35 │      shr    $0x20,%rcxA

Which now compiles down to:

   2.20 │      mov  -0x30(%rbp),%rax
   3.12 │      mov  %rax,%rcx
        │      mov  %rax,-0x60(%rbp)
        │      shr  $0x20,%rcx
2026-03-02 15:56:44 +03:00
Eelco Dolstra
ca07011ed2 Use #include <...> for Windows system headers 2026-03-02 13:31:57 +01:00
Sergei Zimmerman
eba81abab9 libexpr: Make Boehm allocation cache thread_local
This seems quite natural to me, since boehm is necessarily a global
resource. I still think that it would be best if we don't make it a
requirement to only have a single instance of EvalState per thread, but
specifically for this use-case thread_local is a perfect fit.
2026-03-02 13:45:58 +03:00
Sergei Zimmerman
088df89457 Merge pull request #15371 from NixOS/sync-base-cond-var-template
libutil: Template SyncBase over `condition_variable` implementation
2026-03-02 09:43:26 +00:00
John Ericson
0aa4879aaa Merge pull request #15370 from puffnfresh/windows/remove-profiles-symlink
LocalStore: stop creating outdated profiles symlink
2026-03-02 03:29:50 +00:00
Sergei Zimmerman
decc061939 libutil: Template SyncBase over condition_variable implementation
This will allow us to use boost::fibers with the SyncBase class.
2026-03-02 00:58:18 +03:00
Brian McKenna
6244e5fcc1 LocalStore: stop creating outdated profiles symlink
This was done in 9799023545 but
accidentally reverted during a merge.
2026-03-02 07:56:50 +11:00
eveeifyeve
26c1c8fb4a feat(nix): add shortname to bundle bundler arg 2026-03-01 05:52:48 +11:00
Befator
f5890f6d5e Unintentional Symlink changes 2026-02-28 17:46:11 +01:00
Befator
20b9bccb92 Initial Befator Inc Import 2026-02-28 17:10:01 +01:00
Sergei Zimmerman
5a3e65826a Add rootless daemon and nix-nswrapper release note
Co-authored-by: Artemis Tosini <artemis.tosini@obsidian.systems>
2026-02-28 01:12:39 +03:00
Sergei Zimmerman
74ec331a46 Indent lint infrastructure more 2026-02-28 00:20:55 +03:00
John Ericson
0acd0566e8 Merge pull request #15360 from roberth/fun
Have `fun`: `std::function` without null
2026-02-27 20:42:23 +00:00
Sergei Zimmerman
2a94f4d782 Add more release notes 2026-02-27 23:35:36 +03:00
Sergei Zimmerman
16f92c6547 Edit release notes 2026-02-27 22:28:54 +03:00
Sergei Zimmerman
cbca7dd47c release notes: 2.34.0 2026-02-27 22:03:28 +03:00
John Ericson
5d695d4af7 Merge pull request #15361 from NixOS/fix-i686-changelog-d
manual: Skip changelog-d on i686
2026-02-27 16:58:39 +00:00
Robert Hensing
152d7a9b48 refactor: use fun
- `fun` was used for variable/field names in a number of places.
  These have been replaced by similar or more descriptive names,
  to avoid name shadowing, which is not allowed in the GCC-based build.

- Use in-place construction instead of assignment.

- `FilteringSourceAccessor::checkAccess` had a fallback for when
  `makeNotAllowedError` was null. Since `MakeNotAllowedError` is now
  `fun<>`, we've proven the null branch is dead code and have removed it.

- `src/nix/flake.cc`, `src/nix/search.cc`, `src/nix/ls.cc` are left
  as-is — the self-referential lambdas there cause too much
  reindentation for too little benefit.
2026-02-27 15:58:12 +01:00
Robert Hensing
4f5117f791 feat(libutil): add fun<Sig>, a non-nullable std::function wrapper
Like `ref<T>` guarantees a non-null pointer, `fun<Sig>` guarantees a
non-null callable.

When a callable is nullable, use `std::function` directly — not
`optional<fun<>>`. The two types serve complementary roles:
`fun<Sig>` for non-null, `std::function<Sig>` for nullable.

Construction from callables (lambdas, function pointers) is implicit
since these are inherently non-null. Construction from `std::function`
is explicit, since it may be empty. `fun(nullptr)` is a deleted
overload.

A moved-from `fun<>` violates its non-null invariant, matching the
precedent set by `ref<T>`. Deleting moves was considered but deemed
unnecessary: the goal of this type is to document and enforce an
invariant, not to force non-null at all costs including performance.

`get_fn()` exposes the underlying `std::function` for API that only
exists there, such as `target<>()`.
2026-02-27 15:58:12 +01:00
Sergei Zimmerman
5207c2e4bd manual: Skip changelog-d on i686 2026-02-27 16:46:49 +03:00
Sergei Zimmerman
2b5a1a9730 Merge pull request #15349 from roberth/fix-cli-messages
Fix package-related CLI messages
2026-02-27 10:30:04 +00:00
John Ericson
b0c932d591 Merge pull request #15354 from obsidiansystems/convert-path-setting
types: remove Path* typedefs
2026-02-27 06:37:48 +00:00
Amaan Qureshi
fc08c86a07 types: remove Path* typedefs
This commit replaces all usages with their underlying types, that being `std::string` for store
paths, `std::filesystem::path` for filesystem paths, `StringSet` for
path sets, and `std::string_view` for non-owning references.
2026-02-27 00:54:28 -05:00
John Ericson
3b003b7245 Merge pull request #15359 from NixOS/error-create-from-lambda
Add `SysError`/`WinError` constructors that take a `HintFmt`-producing function
2026-02-27 02:05:11 +00:00
John Ericson
718e4dbc02 Add SysError/WinError constructors that take a HintFmt-producing function
This allows capturing the current value/result of
`errno`/`GetLastError()` before constructing the error message. Useful
when the error message construction itself might clobber the error code
(e.g., calling `descriptorToPath()`).

Usage:
```
throw NativeSysError([&] { return HintFmt("msg %s", foo()); });
```

The error code is captured when the exception is constructed, then the
lambda is called to produce the `HintFmt`.

Also fix the Windows build
2026-02-26 20:15:21 -05:00
John Ericson
c522f58947 Merge pull request #15357 from obsidiansystems/remove-path-setting
Remove path setting
2026-02-26 23:26:03 +00:00
Lisanna Dettwyler
6cae299bd9 Error on invalid URL param in github fetcher
Resolves #15304

Signed-off-by: Lisanna Dettwyler <lisanna.dettwyler@gmail.com>
2026-02-26 18:17:04 -05:00
Amaan Qureshi
c165ae939f libstore: introduce StoreDirSetting for store directory 2026-02-26 17:31:53 -05:00
Amaan Qureshi
9ab4740d44 libutil: remove unused OptionalPathSetting 2026-02-26 17:14:44 -05:00
John Ericson
65617395c6 Merge pull request #15345 from obsidiansystems/no-abs-paths-in-eval-2
Create lint for absolute path literals
2026-02-26 22:11:58 +00:00
John Ericson
69d86ce2e4 Merge pull request #15356 from NixOS/fix-i686-unsigned-promotion-narinfo-disk-cache
Fix NarInfoDiskCache::queryCacheRaw on 32 bit platforms
2026-02-26 22:02:43 +00:00
Sergei Zimmerman
796f5cd724 treewide: Get rid of confusing time(0) and use proper nullptr literal
Use proper nullptr literal for NULL pointer instead of the rather confusing 0
literal.
2026-02-27 00:18:12 +03:00
Sergei Zimmerman
bcf2cd4feb Fix NarInfoDiskCache::queryCacheRaw on 32 bit platforms
Bug has existed for a long time, but was only recently surfaced by
6733f2e5ce. time_t was being implicitly promoted
to unsigned. Apparently time_t is still 32 bit for i686-linux in nixpkgs.

Fixes https://hydra.nixos.org/build/322992746/nixlog/1
2026-02-27 00:16:39 +03:00
John Ericson
ed494f00ca Create lint for absolute path literals
Fixes #8738
2026-02-26 15:06:18 -05:00
Robert Hensing
eda9c014c5 fix(eval): render store path context in user-facing format
forceStringNoCtx error messages leaked internal string context
representation (e.g. '!out!<hash>-name.drv'). Use DerivedPath
syntax instead: '<store-path>^out' for built, plain path for
opaque, and '<drv-path> (deep)' for deep derivation references.
2026-02-26 18:06:51 +01:00
Robert Hensing
1442133f1e fix(get-drvs): correct misleading "CA derivations" error in queryOutPath
The error had nothing to do with content-addressed derivations; it
triggers when a supposed derivation attrset simply lacks an outPath
attribute.
2026-02-26 18:06:51 +01:00
Robert Hensing
fd984a3e9d tests: add CLI characterisation tests
Improve coverage ahead of changes to get-drvs.cc and related code.
Each fixture is a separate file to avoid line-number churn in error
expectations.
2026-02-26 18:06:51 +01:00
Sergei Zimmerman
128688db63 Merge pull request #15280 from NixOS/local-store-filesystem
libstore: Fix pct-encoding issues in store references
2026-02-26 15:37:50 +00:00
Sergei Zimmerman
224c8182df libstore: Fix pct-encoding issues in store references
When the common pattern for store config constructors is to accept
an arbitrary string it's unclear whether it needs pct-decoding or not.
Prior to c436b7a32a the string passed to
the constructors was a mix of encoded authority and decoded path concatenated
with a `/`. After that commit it accidentally started accepting pct-encoded
result of renderAuthorityAndPath, but only in some code paths. This lead to
file:///tmp/a+b to be created on disk in /tmp/a%2Bb directory. Similar issue
affected the less-known variant with local:///tmp/a+b. Regular store references
that are local paths were not affected.

This patch changes the constructors to accept different types to signify what
is actually needed to let the factory method handle this in a consistent way:

- std::filesystem::path - local binary cache store and local store
- ParsedURL::Authority - for ssh stores
- ParsedURL - for http stores

(Some MinGW build fixes by Amaan Qureshi <git@amaanq.com>)
2026-02-26 16:40:49 +03:00
Sergei Zimmerman
db06ac411c Merge pull request #15347 from obsidiansystems/unlink-wrapper
Create two wrappers for `unlink`
2026-02-26 09:20:16 +00:00
John Ericson
d9dd677448 Create two wrappers for unlink
See b9ef088e80 for why
`std::filesystem::remove` was no good.
2026-02-25 22:27:59 -05:00
John Ericson
ee955b3206 Merge pull request #15346 from obsidiansystems/return-owned-fd
Return `AutoCloseFD` in open-like functions
2026-02-26 02:09:43 +00:00
John Ericson
89dd96efbf Return AutoCloseFD in open-like functions
This reflects the fact that it is returning a new, "owned" file
descriptor, that it is the caller's responsibility to close.

Co-authored-by: Amaan Qureshi <git@amaanq.com>
2026-02-25 20:17:30 -05:00
John Ericson
6481e75283 Merge pull request #15344 from NixOS/no-filesystem-remove
Revert "libstore: replace unlink() with std::filesystem::remove()"
2026-02-26 00:50:16 +00:00
John Ericson
b9ef088e80 Revert "libstore: replace unlink() with std::filesystem::remove()"
This reverts commit b8caabe25f.

`std::filesystem::remove` will actually do a `rmdir` for directories.
Per https://en.cppreference.com/w/cpp/filesystem/remove.html :

> The file or empty directory identified by the path p is deleted as if
> by the POSIX remove. Symlinks are not followed (symlink is removed,
> not its target).

See
https://pubs.opengroup.org/onlinepubs/9699919799/functions/remove.html

This is a behavior change that we can't be sure is fine, so let's revert
for now. We can do an unlink wrapper later to avoid the `.string()`.
2026-02-25 18:59:28 -05:00
John Ericson
02bb3d032d Merge pull request #15336 from NixOS/libcurl-content-encoding
filetransfer: Use libcurl for Content-Encoding/Transfer-Encoding transparent decompression
2026-02-25 23:19:34 +00:00
John Ericson
89a4412673 Merge pull request #15326 from obsidiansystems/no-abs-paths-in-eval
New diagnostics infra
2026-02-25 23:18:21 +00:00
Sergei Zimmerman
06a1511bff Merge pull request #15082 from roberth/issue-15053
repl: support inherit statements and multiple bindings
2026-02-25 23:09:12 +00:00
Sergei Zimmerman
f02bc896ef Merge pull request #15254 from roberth/fix-test-nix-shell-in-pwd-with-plus
fix(tests): use glob instead of regex in nix-shell test
2026-02-25 22:59:33 +00:00
Sergei Zimmerman
4cf6843acf Merge pull request #15341 from NixOS/fix-flake-regressions
Fix the nix-community/patsh/0.2.1 flake regression test (again)
2026-02-25 22:47:55 +00:00
John Ericson
5184f844bb New diagnostics infra
- Convert `no-url-literals` from experimental feature to `Diagnose`
  setting

  Replace `Xp::NoUrlLiterals` with a new `lint-url-literals` setting
  that accepts `ignore`, `warn`, or `fatal`. This provides more
  flexibility than the binary experimental feature.

- Convert `warn-short-path-literals` to use new lint infra

  We now have `lint-short-path-literals = ignore | warn | fatal`
  instead.

- Convert some of the tests to lang tests

Fix #10048
Progress on #10281
2026-02-25 17:29:56 -05:00
Sergei Zimmerman
fa07d9d055 filetransfer: Use libcurl for Content-Encoding/Transfer-Encoding transparent decompression
Get rid of manual decompression entirely. We can support zstd, br,
deflate (which was broken previously) and gzip/x-gzip encodings via curl
without manual work on our side. This is the same approach that was taken by lix.

Also adds some tests for x-gzip support.
2026-02-26 01:28:46 +03:00
Robert Hensing
ca0f40f29e parseReplBindingsFromBuf: assert non-null result 2026-02-25 23:19:37 +01:00
John Ericson
ae33d09589 Merge pull request #15339 from obsidiansystems/convert-some-more-path
libstore: convert more `Path` to `std::filesystem::path`
2026-02-25 22:15:32 +00:00
Amaan Qureshi
d4b9a81956 libstore: convert more Path to std::filesystem::path
This commit converts local filesystem paths throughout libstore from the
string-based `Path` typedef to `std::filesystem::path`.
2026-02-25 16:26:01 -05:00
Amaan Qureshi
91688d4a34 libstore: use OsStrings for SSH args 2026-02-25 16:25:32 -05:00
Amaan Qureshi
b8caabe25f libstore: replace unlink() with std::filesystem::remove() 2026-02-25 16:25:32 -05:00
Sergei Zimmerman
76eca8fef3 Merge pull request #15272 from roberth/eval-warnings
Eval warnings
2026-02-25 21:05:56 +00:00
Eelco Dolstra
2334977863 Fix the nix-community/patsh/0.2.1 flake regression test (again) 2026-02-25 21:49:30 +01:00
Sergei Zimmerman
c8b02e6ff3 Merge pull request #15335 from dramforever/jsonlogger-no-interrupts
Don't throw Interrupted from JSONLogger::write
2026-02-25 19:03:42 +00:00
dramforever
1210100421 libutil/logging: Use writeFullLogging in JSONLogger::write
Logging should not check for interrupts. Use the new writeFullLogging
function to write JSON output to get similar behavior to SimpleLogger.

This avoids the logger itself throwing Interrupted exceptions in
handleExceptions.
2026-02-26 01:36:54 +08:00
dramforever
473d54ed5f libutil/logging: Generalize writeToStderr into writeFullLogging
Generalize writeToStderr into writeFullLogging, with similar behavior
but taking an arbitrary fd, and reimplement writeToStderr with it as a
convenient wrapper.
2026-02-26 00:51:31 +08:00
John Ericson
937a076844 Merge pull request #15332 from obsidiansystems/windows-libutil-ci
ci: add Windows nix-util-tests job
2026-02-25 16:35:38 +00:00
John Ericson
a2d1346852 Merge pull request #15333 from NixOS/canon-path-from-filename
Introduce `CanonPath::fromFilename`
2026-02-25 15:59:09 +00:00
John Ericson
7275556d61 Introduce CanonPath::fromFilename
See docs for what it is. Use in DerivationBuilderImpl::writeBuilderFile
and deletePath. Also adds O_DIRECTORY via openDirectory in deletePath.

Co-authored-by: Sergei Zimmerman <sergei@zimmerman.foo>
2026-02-25 13:36:42 +03:00
Amaan Qureshi
2c6c3864ce libutil: fix unix-domain-socket unlink for Windows (use _wunlink)
Originally by Brian, narrowed to just libutil by Amaan.

Co-Authored-By: Brian McKenna <brian@brianmckenna.org>
2026-02-25 01:44:38 -05:00
Amaan Qureshi
82f471f48e ci: add Windows nix-util-tests job
Originally by Brian, narrowed to just libutil by Amaan.

Co-Authored-By: Brian McKenna <brian@brianmckenna.org>
2026-02-25 01:41:11 -05:00
John Ericson
16f10c1321 Merge pull request #15329 from obsidiansystems/path-review-followup
file-system: drop redundant quotes around `PathFmt` and assert relative paths
2026-02-25 04:43:04 +00:00
Amaan Qureshi
d5dafb35c3 local-fs-store: make toRealPath accept StorePath only 2026-02-24 22:25:28 -05:00
Amaan Qureshi
f4dfbca04d Fix issues from #15313
- file-system: drop redundant quotes around `PathFmt` and assert relative paths
- libutil, libstore: fix mingw cross-compilation breakages
2026-02-24 19:00:25 -05:00
John Ericson
cf1ead7872 Merge pull request #15330 from NixOS/file-system-get-rid-of-fs-to-string
libutil: Get rid of double-quoting and .string() calls in file-system.cc
2026-02-24 22:23:26 +00:00
John Ericson
56735e9d0f Merge pull request #15328 from NixOS/more-hardening-file-system-at-builders
DerivationBuilderImpl: Add more hardening for writeBuilderFile
2026-02-24 21:57:44 +00:00
Sergei Zimmerman
5b9c41f1b7 libutil: Get rid of double-quoting and .string() calls in file-system.cc
Significantly reduces the amount of churny .string() conversions when
we don't need them and gets rid of annoying double-quoting issues once again.
2026-02-25 00:34:11 +03:00
John Ericson
63845dd9b3 Merge pull request #15313 from obsidiansystems/no-path-libutil
libutil: replace string-based Path with std::filesystem::path across core libraries
2026-02-24 18:56:19 +00:00
John Ericson
b84a42f4cc libutil: replace string-based Path with std::filesystem::path across core libraries
This takes the `std::filesystem::path` migration from the CLI layer into the core libraries (libutil, libstore, libexpr, libfetchers, libflake), converting function signatures, settings fields, and locals throughout `file-system.hh`, `archive.hh`, `configuration.hh`, and their implementations. `PathSetting` becomes `Setting<std::filesystem::path>` in `local-overlay-store.hh` and `local-store.hh` with `.get()` calls at use sites, and several `writeFile` overloads collapse now that the `Path`-based wrappers in `file-system.hh` are gone.

Co-authored-by: Amaan Qureshi <git@amaanq.com>
2026-02-24 13:05:16 -05:00
John Ericson
003b64476e Merge pull request #15312 from obsidiansystems/convert-more-end-exes
cli: continue converting `Path` to `std::filesystem::path` in CLI commands
2026-02-24 17:24:26 +00:00
John Ericson
1e499e616a Merge pull request #15325 from obsidiansystems/rootless-daemon-nixpkgs
tests/functional/unprivileged-daemon: Use nixpkgs unprivileged support
2026-02-24 16:19:36 +00:00
Sergei Zimmerman
246c3fe8d3 DerivationBuilderImpl: Add more hardening for writeBuilderFile
Adds additional layers of hardening for various file-writing utility
functions in the derivation builder. Bad names in non-structured attrs
case are already checked against `[A-Za-z_][A-Za-z0-9_.-]*` regex, so
this isn't an issue, but adding more hardening is good regardless.
2026-02-24 19:15:19 +03:00
Artemis Tosini
ade94a5c0e tests/functional/unprivileged-daemon: Use nixpkgs unprivileged support
Nixpkgs recently added support for running the daemon as unprivileged.
Instead of duplicating the test setup, use the upstream module.
2026-02-24 10:35:37 -05:00
siddhantCodes
a5a256265f cli: continue converting Path to std::filesystem::path in CLI commands
Follows previous work by converting more string-based `Path` fields and
locals to `std::filesystem::path` across the CLI layer:
`MixOutLinkBase::outLink`, `CmdAddToStore::path`, `CmdBundle::outLink`,
`CmdDevelop` redirects and shell, `nix-build`, `nix-channel`, `nix-env`,
`nix-instantiate`, `nix-store`, and `upgrade-nix`.

Also, change `runProgram` and friends to use `OsString` for argument
lists, since those will be native strings. This allows us to avoid
(potentially lossy) `std::filesystem::path` (wide chars on windows) ->
`std::string` (narrow) -> `OsString` (i.e. `std::wstring` on Windows,
wide again) round trips. For CLIs with no path arguments, there is a
`toOsStrings` function for convenience, however.

Progress on #9205

Co-authored-by: Amaan Qureshi <git@amaanq.com>
2026-02-23 15:46:31 -05:00
John Ericson
be7b9a33ed Replace renderUrlPathEnsureLegal with urlPathToPath
We make `urlPathToPath` support relative paths for of Flakes.

The mercurial fetching got a bit larger of a rework with this, by taking
advantage of `std::variant`.
2026-02-23 15:41:03 -05:00
John Ericson
5ce241cbfd Merge pull request #15307 from nix-windows/windows-local-shorthand
windows: add a separate local_shorthand_path test
2026-02-23 19:42:48 +00:00
John Ericson
3bfd64c3cd Merge pull request #15323 from obsidiansystems/fix-eio-readline
libutil: treat EIO as EOF in `readLine`
2026-02-23 19:30:19 +00:00
Brian McKenna
3f419cfa4e ParsedURL::path <-> std::filesystem::path, use in StoreReference
This missing URL functionality allow us to properly fix the path
shorthand for local store URLs test case.
2026-02-23 13:32:07 -05:00
Amaan Qureshi
994137dcf7 libutil: treat EIO as EOF in readLine
Reading from a pty master returns `EIO` once the slave side closes, however, `readLine` lets it propagate as an uncaught `SysError`, which causes spurious build failures in gvisor and similar sandboxed environments where pty teardown races differently. This commit catches `EIO` inside the read lambda and maps it to a zero-length read, reusing the existing EOF path.
2026-02-23 13:30:05 -05:00
Sergei Zimmerman
9242d74bc1 Merge pull request #15321 from obsidiansystems/writefull-interrupt-test
tests: add `writeFull` interrupt-handling regression test
2026-02-23 18:29:27 +00:00
Amaan Qureshi
6cddf03b5a tests: add writeFull interrupt-handling regression test
This commit verifies that `writeFull` with `allowInterrupts=false` completes
successfully when the interrupt flag is set. This prevents regressions
like the one fixed by #15255 where `write()` called `checkInterrupt()`
unconditionally.
2026-02-23 12:20:54 -05:00
Bernardo Meurer
afccf1d2d3 Merge pull request #15256 from NixOS/dependabot/github_actions/cachix/install-nix-action-31.9.1
build(deps): bump cachix/install-nix-action from 31.9.0 to 31.9.1
2026-02-23 14:09:16 +00:00
Bernardo Meurer
de16ef8be6 Merge pull request #15257 from NixOS/dependabot/github_actions/korthout/backport-action-4.1.0
build(deps): bump korthout/backport-action from 4.0.1 to 4.1.0
2026-02-23 14:08:51 +00:00
Sergei Zimmerman
7cd7930344 Merge pull request #15319 from xokdvium/fix-interrupts-write-full
libutil: Fix writeFull to respect allowInterrupts
2026-02-23 14:03:30 +00:00
Sergei Zimmerman
d89400d052 libstore/filetransfer: Use bitfields for boolean flags in TransferItem
Wastes slightly less memory. Also gets the doc-comments in order.
2026-02-23 15:48:49 +03:00
Sergei Zimmerman
658c775f01 libutil: Fix writeFull to respect allowInterrupts
c0e849b696 broke interrupt handling since
writeFull started throwing Interrupted even when allowInterrupts was false.
This would lead to exceptions leaking out when they must not (for example during
progress bar shutdown).
2026-02-23 15:34:18 +03:00
Sergei Zimmerman
b1ad42e6d5 Merge pull request #15242 from obsidiansystems/fix-prefetch-segfault
libstore: guard against empty archive in unpack paths
2026-02-22 19:14:11 +00:00
John Ericson
761139f31c Merge pull request #15314 from obsidiansystems/fix-registry-symlink
registry: fix symlinked flake registry files broken by convert-end-exes
2026-02-21 20:06:31 +00:00
Amaan Qureshi
c6d93828bd registry: fix symlinked flake registry files broken by convert-end-exes
This commit reverts to using `getFSSourceAccessor()` so absolute symlink targets resolve correctly, since `makeFSSourceAccessor(path)` roots the accessor at `path` and can't follow symlinks that escape it.
2026-02-21 12:14:42 -05:00
Sergei Zimmerman
614072adcb Merge pull request #15286 from NixOS/failed-values-v2
Introduce a "failed" value type (v2)
2026-02-21 09:05:15 +00:00
John Ericson
42f6e9933d Merge pull request #15311 from obsidiansystems/convert-end-exes
libutil: return `std::filesystem::path` from XDG directory helpers
2026-02-21 04:22:26 +00:00
siddhantCodes
6808bfab92 libutil: return std::filesystem::path from XDG directory helpers
`getCacheDir`, `getConfigDir`, `getDataDir`, `getStateDir`, and related functions now return `std::filesystem::path` and use `getEnvOs` for native OS string handling, letting callsites replace string concatenation with `operator/` and drop the ad-hoc `namespace nix::fs` alias from six CLI files. `expandTilde` is fixed to strip both `~` and `/` before joining with `operator/` (an absolute right-hand operand silently replaces the left-hand side), and `ExecutablePath` gains `parseAppend` for incremental `PATH` construction.

Co-authored-by: Amaan Qureshi <git@amaanq.com>
2026-02-20 22:35:47 -05:00
John Ericson
92942071c7 Merge pull request #15310 from obsidiansystems/pathlocks-wine
`PathLocks` allow to fail on Wine
2026-02-21 03:32:18 +00:00
John Ericson
8899af09c1 Merge pull request #15308 from obsidiansystems/fix-ioport-init
Fix initialization of `ioport` in `Worker` on Windows
2026-02-21 02:48:26 +00:00
John Ericson
6363c1bf00 PathLocks allow to fail on Wine
It needs functionality Wine doesn't implement yet. So let's just have a
wine-only warn-and-keep-going.
2026-02-20 21:45:04 -05:00
Brian McKenna
7e41983c06 windows: add a derivation builder 2026-02-21 12:35:20 +11:00
John Ericson
5adb6a36b6 Fix initialization of ioport in Worker on Windows 2026-02-20 18:56:00 -05:00
John Ericson
b10cb6596e Merge pull request #15305 from obsidiansystems/file-system-prep
Centralize I/O error handling and make read/write functions portable
2026-02-20 17:31:21 +00:00
John Ericson
c0e849b696 Centralize I/O error handling and make read/write functions portable
- Improve existing `read` and `readOffset` wrappers:
  - Unix: Add `EINTR` retry handling and `checkInterrupt`
  - Windows: Handle `ERROR_BROKEN_PIPE` as EOF, add `checkInterrupt`

- Add `write` wrapper with same treatment (`EINTR` on Unix, `checkInterrupt`)

- Improve many `windows/file-descriptor.cc` error messages with
  `descriptorToPath`

- Move `readFull`, `readLine`, `writeFull` to common file, using the
  platform wrappers instead of duplicating platform-specific logic.
  These ones don't need any `EINTR` or interrupt checking either. They
  only have `EGAIN` checking as Unix-specific code, but this is a temp
  hack to be removed per #12688, so its fine that it goes in the
  platform-agnostic file for now.

- Add `retryOnBlock` helper to abstract `EAGAIN`/`EWOULDBLOCK` poll-and-retry
  logic (Unix only, needed for buildhook workaround #12688)

- Simplify `FdSource::readUnbuffered` to just call `nix::read`

- Remove now-dead `EINTR` handling from `drainFD` and `copyFdRange`

- `writeErr` better impl on Windows
2026-02-20 11:39:05 -05:00
John Ericson
2470b7981a Merge pull request #15277 from puffnfresh/windows/remove-profiles-symlink
LocalStore: stop creating outdated profiles symlink
2026-02-20 05:52:49 +00:00
John Ericson
5c1939e315 Merge pull request #15301 from obsidiansystems/file-system-prep
Misc file system fixes, especially for windows
2026-02-20 01:28:58 +00:00
Amaan Qureshi
3a4600cb0a derivation-builder: clean up interfaces after common code extraction
Inlines single-use fields (`outputRewrites`, `inputRewrites`, `env`,
`tmpDirFd`, `pathsInChroot`, `userNamespaceSync`) into local variables.
Moves `homeDir` to a single `extern const` in `derivation-builder-common`.
Converts `computeScratchOutputs` to return a tuple instead of using out
params, `initEnv` to return `StringMap`, and internalizes `outputRewrites`
inside `registerOutputs`. Inlines the constant `needsHashRewrite()` method
at call sites, removing dead code in the chroot builder where it was `false`.
2026-02-19 20:21:07 -05:00
Amaan Qureshi
a6fe37964a derivation-builder: extract common code blocks into free functions
The five builder files (`generic-unix`, `linux`, `linux-chroot`, `darwin`,
`external`) contained massive duplication: identical `computeScratchOutputs`
loops, `stopDaemon` methods, `processSandboxSetupMessages` blocks,
`setupRecursiveNixDaemon` code, PTY setup, AWS credential resolution,
builtin builder dispatch, privilege dropping, `execve` wrappers, disk
space checks, `unprepareBuild` preambles, cleanup logic, impure path
validation, and pre-build hook parsing.

This commit extracts all of these into free functions in
`derivation-builder-common.{cc,hh}`. Each builder now calls the common
functions, keeping only platform-specific logic inline. The `inputRewrites`,
`outputRewrites`, and `tmpDirFd` locals are promoted to fields so that
the extracted functions can access them without excessive parameter counts.
2026-02-19 20:21:07 -05:00
Amaan Qureshi
e19c685a7e derivation-builder: inline remaining single-use private methods, remove dead code 2026-02-19 20:21:07 -05:00
John Ericson
42e17bd476 derivation-builder: Convert some single-method fields to local variables
Convert `tmpDirFd`, `inputRewrites`, and `outputRewrites` from member
fields to local variables since they are only used within a single method:

- `tmpDirFd` is only used in `startBuild()`
- `inputRewrites` is only used in `startBuild()`
- `outputRewrites` is only used in `unprepareBuild()`

Applied to all four builder implementations.

TODO there are more variables like this which can be converted.
2026-02-19 20:21:07 -05:00
John Ericson
97929f657a derivation-builder: Inline methods used once
Once per impl, that is.

This hopefully makes the code easier to read.
2026-02-19 20:21:07 -05:00
John Ericson
259eab10c5 Extract setupSeccomp to linux-derivation-builder-common
Move the identical setupSeccomp function from both
linux-derivation-builder.cc and linux-chroot-derivation-builder.cc
into a new shared file to eliminate code duplication.
2026-02-19 20:21:07 -05:00
Amaan Qureshi
4f1d552953 derivation-builder: extract initEnv and other identical methods into free functions 2026-02-19 20:21:07 -05:00
Amaan Qureshi
f1b13c4035 derivation-builder: extract registerOutputs into free function 2026-02-19 20:21:07 -05:00
Amaan Qureshi
991f0fca77 derivation-builder: flatten all builders to standalone, create GenericUnixDerivationBuilder 2026-02-19 20:21:07 -05:00
Amaan Qureshi
5232f17829 derivation-builder: add cleanupOnDestruction to DerivationBuilder interface
Add a virtual `cleanupOnDestruction() noexcept` method to `DerivationBuilder`.
This is needed so that `DerivationBuilderDeleter` can call it directly on any
`DerivationBuilder` pointer without requiring a `dynamic_cast` to
`DerivationBuilderImpl`. `DerivationBuilderImpl` overrides it with the
existing cleanup logic.
2026-02-19 20:21:07 -05:00
John Ericson
1b5d4258ec Move cgroup functionality inside nix::linux namespace
This is what we do for other OS-specific functionality.
2026-02-19 20:21:07 -05:00
John Ericson
8417b77f86 Factor out chroot logic from ChrootDerivationBuilder into its own file
This shrinks the big derivation builder compilation unit, and hopefully
puts on on the road to being able to do all derivation builder cleanup
with RAII.
2026-02-19 20:21:07 -05:00
Brian McKenna
9799023545 LocalStore: stop creating outdated profiles symlink
The gcroots/profiles link became outdated in
aeb810b01e when the GC code started
reading directly from /profiles, and gcroots/profiles was even
partially deleted in that commit.
2026-02-20 11:48:13 +11:00
John Ericson
ae4e229c9f Change writeFile(AutoCloseFD &, ...) to take a Descriptor
The new signature is:
  writeFile(Descriptor fd, std::string_view s, FsSync sync = FsSync::No, const Path * origPath = nullptr)

This uses `descriptorToPath` if `origPath` is not provided.
2026-02-19 19:30:55 -05:00
Amaan Qureshi
b63f5d1914 libutil: refactor AutoCloseFD::fsync into standalone syncDescriptor
`AutoCloseFD::fsync()` contained platform-specific logic behind CPP
guards. This extracts it into a free function `syncDescriptor(Descriptor)`
with separate Unix and Windows implementations, and makes
`AutoCloseFD::fsync()` an inline wrapper that delegates to it. The
Windows implementation uses `FlushFileBuffers` (returns `BOOL`, true on
success) while Unix uses `::fsync` (or `F_FULLFSYNC` on macOS), so
splitting them avoids conflating the two calling conventions.
2026-02-19 19:23:49 -05:00
John Ericson
31d87afc5a openFileEnsureBeneathNoSymlinks now returns AutoCloseFD
This indicates that they are returning an owned handle (i.e. the caller
 should --- and will, thanks to RAII --- close it).

 `ntOpenAt` and friends (internal) also use `AutoCloseFD` for the same
 reason.
2026-02-19 19:08:31 -05:00
John Ericson
204618c9d8 Misc unit test improvements 2026-02-19 17:26:38 -05:00
John Ericson
f0d90d3bdb Create two more FSSourceAccessorTests
Make sure we're testing non-directory roots.
2026-02-19 17:25:54 -05:00
John Ericson
0730dcb4a8 Skip chmodIfNeeded test on Windows
It doesn't do anything on Windows, it appears.
2026-02-19 17:21:46 -05:00
John Ericson
9b363e1e5c libutil-tests: Use FS_ROOT macros consistently in file-system tests
Add FS_ROOT_NO_TRAILING_SLASH macro and update isInDir and isDirOrInDir tests to use FS_ROOT and FS_SEP macros for cross-platform compatibility.
2026-02-19 17:21:12 -05:00
John Ericson
3df67a8347 Merge pull request #15298 from dramforever/parse-derivation-empty-special-msg
Add special error message for empty derivation file
2026-02-19 20:12:38 +00:00
Robert Hensing
dd4b73a44d Add rl-next/c-api-recoverable-errors 2026-02-19 14:23:05 +03:00
Sergei Zimmerman
fd4eee9d62 libexpr-tests: Add ValuePrintingTests.vFailed 2026-02-19 14:23:04 +03:00
Sergei Zimmerman
100e9a4436 Add tests/f/lang/eval-fail-memoised-error-trace-not-mutated.nix 2026-02-19 14:23:03 +03:00
Robert Hensing
17f344cdda libexpr: Add recoverable errors
This provides an explicit API for call-fail-retry-succeed evaluation
flows, such as currently used in NixOps4.

An alternative design would simply reset the `Value` to the original
thunk instead of `tFailed` under the condition of catching a
`RecoverableEvalError`.
That is somewhat simpler, but I believe the presence of `tFailed` is
beneficial for possible use in the repl; being able to show the error
sooner, without re-evaluation.

The hasPos method is required in order to avoid an include loop.
2026-02-19 14:23:02 +03:00
Eelco Dolstra
c33d9e31cc Introduce a "failed" value type
In the multithreaded evaluator, it's possible for multiple threads to
wait on the same thunk. If evaluation of the thunk results in an
exception, the waiting threads shouldn't try to re-force the thunk.
Instead, they should rethrow the same exception, without duplicating
any work.

Therefore, there is now a new value type `tFailed` that stores an
std::exception_ptr. If evaluation of a thunk/app results in an
exception, `forceValue()` overwrites the value with a `tFailed`. If
`forceValue()` encounters a `tFailed`, it rethrows the exception. So
you normally never need to check for failed values (since forcing them
causes a rethrow).

Co-authored-by: Robert Hensing <robert@roberthensing.nl>
2026-02-19 14:23:01 +03:00
Sergei Zimmerman
89158eedb5 treewide: Make exceptions cloneable
This is needed to make it possible to store exceptions in failed values
with each new rethrow getting a fresh copy of the exception object.
2026-02-19 14:22:58 +03:00
dramforever
c33c82f345 Add special error message for empty derivation file
This rather commonly occurs when the computer was uncleanly shut down
and fsync was not enabled. Show the user a more actionable message than
"expected string 'D'".
2026-02-19 17:34:57 +08:00
John Ericson
40abcebbe1 Merge pull request #15295 from NixOS/expr-op-update-dont-mutate-result
libexpr: Use temporary value in ExprOpUpdate::eval
2026-02-19 09:14:44 +00:00
John Ericson
3bf690a407 Merge pull request #15294 from NixOS/less-filesystem-to-string
libutil: Drop superflous .string() in pathExists and pathAccessible
2026-02-19 02:33:55 +00:00
Jörg Thalheim
360ff05e73 Merge pull request #15296 from Mic92/fix-nix-build
Reapply "Use the hash modulo in the derivation outputs"
2026-02-19 01:38:29 +00:00
Jörg Thalheim
100e7cc337 Reapply "Use the hash modulo in the derivation outputs"
This reverts commit 4f91e9599f.

This broke:

when I applied this pr, `--print-out-paths` wouldn't print anything:

```
shell-for-nix-env % nix build --print-out-paths .#legacyPackages.aarch64-darwin.homeConfigurations.macos.activationPackage
```

After dropping the patches from my fork, it does again.
```
shell-for-nix-env % nix build --print-out-paths .#legacyPackages.aarch64-darwin.homeConfigurations.macos.activationPackage
/nix/store/s8mlcalszdml0v8172w4hwqnx0m6477r-home-manager-generation
```
2026-02-19 01:50:17 +01:00
Sergei Zimmerman
247cc7013a libexpr: Use temporary value in ExprOpUpdate::eval 2026-02-19 02:49:10 +03:00
Sergei Zimmerman
9e865ae4ff libutil: Drop superflous .string() in pathExists and pathAccessible 2026-02-19 02:13:12 +03:00
Eelco Dolstra
fef83c9f9c Merge pull request #15287 from NixOS/narinfo-cache-meta-ttl
Add setting narinfo-cache-meta-ttl
2026-02-18 22:13:23 +00:00
Eelco Dolstra
8491e26cd4 Merge pull request #15291 from roberth/fix-evalstate-shared-from-this-usage
fix(EvalState): Use make_shared for enable_shared_from_this compatibi…
2026-02-18 22:06:03 +00:00
Eelco Dolstra
e5fa203d7f Add release note 2026-02-18 22:24:42 +01:00
Robert Hensing
987ecca24a fix(EvalState): Use make_shared for enable_shared_from_this compatibility
EvalState inherits from enable_shared_from_this (added in b4c24a29 for
debugRepl), but was being stack-allocated or created with make_unique
in several places. This causes bad_weak_ptr errors when shared_from_this
is called.

Convert all EvalState allocations to use make_shared.
2026-02-18 22:14:20 +01:00
Jörg Thalheim
a98b43b994 Merge pull request #12464 from obsidiansystems/build-trace-rework
Realisations use regular drv paths again
2026-02-18 20:54:56 +00:00
Eelco Dolstra
6733f2e5ce Add setting narinfo-cache-meta-ttl
This makes the current hard-coded 7-day `nix-cache-info` TTL
configurable, making `--offline` and `--refresh` do the right thing.
2026-02-18 21:30:37 +01:00
Robert Hensing
5c42b84a17 chore(nix): fix evaluation warnings 2026-02-17 16:45:38 +01:00
dependabot[bot]
6429f2fd6c build(deps): bump korthout/backport-action from 4.0.1 to 4.1.0
Bumps [korthout/backport-action](https://github.com/korthout/backport-action) from 4.0.1 to 4.1.0.
- [Release notes](https://github.com/korthout/backport-action/releases)
- [Commits](c656f5d585...01619ebc9a)

---
updated-dependencies:
- dependency-name: korthout/backport-action
  dependency-version: 4.1.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-02-16 22:11:56 +00:00
dependabot[bot]
1a2d73dc2b build(deps): bump cachix/install-nix-action from 31.9.0 to 31.9.1
Bumps [cachix/install-nix-action](https://github.com/cachix/install-nix-action) from 31.9.0 to 31.9.1.
- [Release notes](https://github.com/cachix/install-nix-action/releases)
- [Changelog](https://github.com/cachix/install-nix-action/blob/master/RELEASE.md)
- [Commits](4e002c8ec8...2126ae7fc5)

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

Signed-off-by: dependabot[bot] <support@github.com>
2026-02-16 22:11:52 +00:00
Robert Hensing
a3e99602c0 fix(tests): use glob instead of regex in nix-shell test
The NIX_BUILD_TOP test used regex matching with an unquoted path
variable. When the path contains `+` (or other regex operators),
the test fails because `+` is interpreted as a quantifier rather than
a literal character. Glob matching handles these characters correctly.
2026-02-16 21:06:55 +01:00
Robert Hensing
3083226336 fix(packaging): restrict nix-nswrapper to Linux platforms
It is only supported on that host platform.
This adds a bunch of redundant-ish wiring logic, because the alternative
is accessing `meta.platforms` generically, but that would cause these
flake output attrsets *containing* the packages to be *strict in all
packages*, which is very bad for performance and robustness.

Checked:
- Cross-compilation still works for Linux targets (arm, riscv),
  even when build platform is Darwin
- No attributes for nix-nswrapper for native Darwin
2026-02-16 17:22:19 +01:00
Amaan Qureshi
0e39aa2068 libstore: guard against empty archive in unpack paths
`DirectoryIterator` is dereferenced without an end check, which segfaults
when unpacking an empty or zero-sized archive. This commit adds an
emptiness check before the dereference in both `prefetchFile` and
`builtinUnpackChannel`, throwing a descriptive error instead.
Fixes #15116.
2026-02-15 10:16:54 -05:00
John Ericson
4f91e9599f Revert "Use the hash modulo in the derivation outputs"
Fix #11897

As described in the issue, this makes for a simpler and much more
intuitive notion of a realisation key. This is better for pedagogy, and
interoperability between more tools.

The way the issue was written was that we would switch to only having
shallow realisations first, and then do this. But going to only shallow
realisations is more complex change, and it turns out we weren't even
testing for the benefits that derivation hashes (modulo FODs) provided
in the deep realisation case, so I now just want to do this first.

Doing this gets the binary cache data structures in order, which will
unblock the Hydra fixed-output-derivation tracking work. I don't want to
delay that work while I figure out the changes needed for
shallow-realisations only.

This reverts commit bab1cda0e6.

Co-authored-by: Amaan Qureshi <git@amaanq.com>
2026-02-13 15:20:00 -05:00
Robert Hensing
5ee5e6e8a0 release notes: repl inherit and multiple bindings 2026-01-28 17:44:51 +01:00
Robert Hensing
0f2754a682 repl: support inherit statements and multiple bindings
Fixes #15053

This adds support for inherit statements and multiple bindings per
line in nix repl:

    nix-repl> a = { x = 1; y = 2; }
    nix-repl> inherit (a) x y
    nix-repl> x + y
    3

    nix-repl> p = 1; q = 2;
    nix-repl> p + q
    3

Design
------

The REPL now uses a parser-based approach instead of ad-hoc regex
matching. It tries parsing input in order:

  1. As an expression (using the regular parser entry point)
  2. As bindings (using a new REPL_BINDINGS entry point)
  3. As bindings with an appended semicolon (for convenience)

The third attempt allows users to omit the trailing semicolon for
single statements like "inherit (a) x" or "foo = 1".

A separate REPL_BINDINGS parser entry point is required because
combining expressions and bindings in the same production causes
reduce/reduce conflicts: "ID ." is ambiguous between attribute access
(a.b) and the start of a nested binding path (a.b = 1).

Alternatives considered
-----------------------

GLR parsing: Would resolve the ambiguity but risks parsing
performance regression for all Nix code, not just the REPL.

Duplicated grammar: A REPL-specific repl_binds production that
duplicates binds1 rules. Rejected to avoid maintenance burden and
grammar drift.

Single binding per line: Simpler but less useful. The current
approach reuses binds1 directly, getting multiple bindings and
nested attribute paths for free.

Tradeoffs
---------

Binding input may be parsed twice (once as expression, then as
bindings). This is acceptable for interactive REPL use where input is
typically small and latency from parsing is negligible.

Error messages preserve the original input even when the semicolon-
appended parse is attempted, so users see "inherit (a) y" in errors
rather than "inherit (a) y;".
2026-01-28 17:44:51 +01:00
684 changed files with 13878 additions and 7851 deletions

View File

@@ -1,37 +0,0 @@
name: Backport
on:
pull_request_target:
types: [closed, labeled]
permissions:
contents: read
jobs:
backport:
name: Backport Pull Request
permissions:
# for korthout/backport-action
contents: write
pull-requests: write
if: github.repository_owner == 'NixOS' && github.event.pull_request.merged == true && (github.event_name != 'labeled' || startsWith('backport', github.event.label.name))
runs-on: ubuntu-24.04-arm
steps:
- name: Generate GitHub App token
id: generate-token
uses: actions/create-github-app-token@v2
with:
app-id: ${{ vars.CI_APP_ID }}
private-key: ${{ secrets.CI_APP_PRIVATE_KEY }}
- uses: actions/checkout@v6
with:
ref: ${{ github.event.pull_request.head.sha }}
# required to find all branches
fetch-depth: 0
- name: Create backport PRs
uses: korthout/backport-action@c656f5d5851037b2b38fb5db2691a03fa229e3b2 # v4.0.1
id: backport
with:
# Config README: https://github.com/korthout/backport-action#backport-action
github_token: ${{ steps.generate-token.outputs.token }}
github_workspace: ${{ github.workspace }}
auto_merge_enabled: true
pull_description: |-
Automatic backport to `${target_branch}`, triggered by a label in #${pull_number}.

View File

@@ -1,252 +0,0 @@
name: "CI"
on:
pull_request:
merge_group:
push:
branches:
- master
workflow_dispatch:
inputs:
dogfood:
description: 'Use dogfood Nix build'
required: false
default: true
type: boolean
concurrency:
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
cancel-in-progress: true
permissions: read-all
jobs:
eval:
runs-on: ubuntu-24.04
steps:
- uses: actions/checkout@v6
with:
fetch-depth: 0
- uses: ./.github/actions/install-nix-action
with:
dogfood: ${{ github.event_name == 'workflow_dispatch' && inputs.dogfood || github.event_name != 'workflow_dispatch' }}
extra_nix_config:
experimental-features = nix-command flakes
github_token: ${{ secrets.GITHUB_TOKEN }}
use_cache: false
- run: nix flake show --all-systems --json
pre-commit-checks:
name: pre-commit checks
runs-on: ubuntu-24.04
steps:
- uses: actions/checkout@v6
- uses: ./.github/actions/install-nix-action
with:
dogfood: ${{ github.event_name == 'workflow_dispatch' && inputs.dogfood || github.event_name != 'workflow_dispatch' }}
extra_nix_config: experimental-features = nix-command flakes
github_token: ${{ secrets.GITHUB_TOKEN }}
- run: ./ci/gha/tests/pre-commit-checks
basic-checks:
name: aggregate basic checks
if: ${{ always() }}
runs-on: ubuntu-24.04
needs: [pre-commit-checks, eval]
steps:
- name: Exit with any errors
if: ${{ contains(needs.*.result, 'failure') || contains(needs.*.result, 'cancelled') }}
run: |
exit 1
tests:
needs: basic-checks
strategy:
fail-fast: false
matrix:
include:
- scenario: on ubuntu
runs-on: ubuntu-24.04
os: linux
instrumented: false
primary: true
stdenv: stdenv
- scenario: on macos
runs-on: macos-14
os: darwin
instrumented: false
primary: true
stdenv: stdenv
- scenario: on ubuntu (with sanitizers / coverage)
runs-on: ubuntu-24.04
os: linux
instrumented: true
primary: false
stdenv: clangStdenv
name: tests ${{ matrix.scenario }}
runs-on: ${{ matrix.runs-on }}
timeout-minutes: 60
steps:
- uses: actions/checkout@v6
with:
fetch-depth: 0
- uses: ./.github/actions/install-nix-action
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
dogfood: ${{ github.event_name == 'workflow_dispatch' && inputs.dogfood || github.event_name != 'workflow_dispatch' }}
# The sandbox would otherwise be disabled by default on Darwin
extra_nix_config: "sandbox = true"
# Since ubuntu 22.30, unprivileged usernamespaces are no longer allowed to map to the root user:
# https://ubuntu.com/blog/ubuntu-23-10-restricted-unprivileged-user-namespaces
- run: sudo sysctl -w kernel.apparmor_restrict_unprivileged_userns=0
if: matrix.os == 'linux'
- name: Run component tests
run: |
nix build --file ci/gha/tests/wrapper.nix componentTests -L \
--arg withInstrumentation ${{ matrix.instrumented }} \
--argstr stdenv "${{ matrix.stdenv }}"
- name: Run VM tests
run: |
nix build --file ci/gha/tests/wrapper.nix vmTests -L \
--arg withInstrumentation ${{ matrix.instrumented }} \
--argstr stdenv "${{ matrix.stdenv }}"
if: ${{ matrix.os == 'linux' }}
- name: Run flake checks and prepare the installer tarball
run: |
ci/gha/tests/build-checks
ci/gha/tests/prepare-installer-for-github-actions
if: ${{ matrix.primary }}
- name: Collect code coverage
run: |
nix build --file ci/gha/tests/wrapper.nix codeCoverage.coverageReports -L \
--arg withInstrumentation ${{ matrix.instrumented }} \
--argstr stdenv "${{ matrix.stdenv }}" \
--out-link coverage-reports
cat coverage-reports/index.txt >> $GITHUB_STEP_SUMMARY
if: ${{ matrix.instrumented }}
- name: Upload coverage reports
uses: actions/upload-artifact@v6
with:
name: coverage-reports
path: coverage-reports/
if: ${{ matrix.instrumented }}
- name: Upload installer tarball
uses: actions/upload-artifact@v6
with:
name: installer-${{matrix.os}}
path: out/*
if: ${{ matrix.primary }}
installer_test:
needs: [tests]
strategy:
fail-fast: false
matrix:
include:
- scenario: on ubuntu
runs-on: ubuntu-24.04
os: linux
experimental-installer: false
- scenario: on macos
runs-on: macos-14
os: darwin
experimental-installer: false
- scenario: on ubuntu (experimental)
runs-on: ubuntu-24.04
os: linux
experimental-installer: true
- scenario: on macos (experimental)
runs-on: macos-14
os: darwin
experimental-installer: true
name: installer test ${{ matrix.scenario }}
runs-on: ${{ matrix.runs-on }}
steps:
- uses: actions/checkout@v6
- name: Download installer tarball
uses: actions/download-artifact@37930b1c2abaa49bbe596cd826c3c89aef350131 # v7.0.0
with:
name: installer-${{matrix.os}}
path: out
- name: Looking up the installer tarball URL
id: installer-tarball-url
run: |
echo "installer-url=file://$GITHUB_WORKSPACE/out" >> "$GITHUB_OUTPUT"
TARBALL_PATH="$(find "$GITHUB_WORKSPACE/out" -name 'nix*.tar.xz' -print | head -n 1)"
echo "tarball-path=file://$TARBALL_PATH" >> "$GITHUB_OUTPUT"
- uses: cachix/install-nix-action@4e002c8ec80594ecd40e759629461e26c8abed15 # v31.9.0
if: ${{ !matrix.experimental-installer }}
with:
install_url: ${{ format('{0}/install', steps.installer-tarball-url.outputs.installer-url) }}
install_options: ${{ format('--tarball-url-prefix {0}', steps.installer-tarball-url.outputs.installer-url) }}
- uses: ./.github/actions/install-nix-action
if: ${{ matrix.experimental-installer }}
with:
dogfood: false
experimental-installer: true
tarball_url: ${{ steps.installer-tarball-url.outputs.tarball-path }}
github_token: ${{ secrets.GITHUB_TOKEN }}
- run: sudo apt install fish zsh
if: matrix.os == 'linux'
- run: brew install fish
if: matrix.os == 'darwin'
- run: exec bash -c "nix-instantiate -E 'builtins.currentTime' --eval"
- run: exec sh -c "nix-instantiate -E 'builtins.currentTime' --eval"
- run: exec zsh -c "nix-instantiate -E 'builtins.currentTime' --eval"
- run: exec fish -c "nix-instantiate -E 'builtins.currentTime' --eval"
- run: exec bash -c "nix-channel --add https://releases.nixos.org/nixos/unstable/nixos-23.05pre466020.60c1d71f2ba nixpkgs"
- run: exec bash -c "nix-channel --update && nix-env -iA nixpkgs.hello && hello"
flake_regressions:
needs: tests
runs-on: ubuntu-24.04
steps:
- name: Checkout nix
uses: actions/checkout@v6
- name: Checkout flake-regressions
uses: actions/checkout@v6
with:
repository: NixOS/flake-regressions
path: flake-regressions
- name: Checkout flake-regressions-data
uses: actions/checkout@v6
with:
repository: NixOS/flake-regressions-data
path: flake-regressions/tests
- name: Download installer tarball
uses: actions/download-artifact@37930b1c2abaa49bbe596cd826c3c89aef350131 # v7.0.0
with:
name: installer-linux
path: out
- name: Looking up the installer tarball URL
id: installer-tarball-url
run: |
echo "installer-url=file://$GITHUB_WORKSPACE/out" >> "$GITHUB_OUTPUT"
- uses: cachix/install-nix-action@4e002c8ec80594ecd40e759629461e26c8abed15 # v31.9.0
with:
install_url: ${{ format('{0}/install', steps.installer-tarball-url.outputs.installer-url) }}
install_options: ${{ format('--tarball-url-prefix {0}', steps.installer-tarball-url.outputs.installer-url) }}
- name: Run flake regressions tests
run: MAX_FLAKES=25 flake-regressions/eval-all.sh
profile_build:
needs: tests
runs-on: ubuntu-24.04
timeout-minutes: 60
if: >-
github.event_name == 'push' &&
github.ref_name == 'master'
steps:
- uses: actions/checkout@v6
with:
fetch-depth: 0
- uses: ./.github/actions/install-nix-action
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
dogfood: ${{ github.event_name == 'workflow_dispatch' && inputs.dogfood || github.event_name != 'workflow_dispatch' }}
extra_nix_config: |
experimental-features = flakes nix-command ca-derivations impure-derivations
max-jobs = 1
- run: |
nix build -L --file ./ci/gha/profile-build buildTimeReport --out-link build-time-report.md
cat build-time-report.md >> $GITHUB_STEP_SUMMARY

View File

@@ -1,24 +0,0 @@
name: "Label PR"
on:
pull_request_target:
types: [edited, opened, synchronize, reopened]
# WARNING:
# When extending this action, be aware that $GITHUB_TOKEN allows some write
# access to the GitHub API. This means that it should not evaluate user input in
# a way that allows code injection.
permissions:
contents: read
pull-requests: write
jobs:
labels:
runs-on: ubuntu-24.04
if: github.repository_owner == 'NixOS'
steps:
- uses: actions/labeler@v6
with:
repo-token: ${{ secrets.GITHUB_TOKEN }}
sync-labels: false

View File

@@ -1,80 +0,0 @@
name: Upload Release
on:
workflow_dispatch:
inputs:
eval_id:
description: "Hydra evaluation ID"
required: true
type: number
is_latest:
description: "Mark as latest release"
required: false
type: boolean
default: false
permissions:
contents: read
id-token: write
packages: write
jobs:
release:
runs-on: ubuntu-24.04
environment: releases
steps:
- uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
- uses: ./.github/actions/install-nix-action
with:
dogfood: false # Use stable version
use_cache: false # Don't want any cache injection shenanigans
extra_nix_config: |
experimental-features = nix-command flakes
- name: Set NIX_PATH from flake input
run: |
NIXPKGS_PATH=$(nix build --inputs-from .# nixpkgs#path --print-out-paths --no-link)
# Shebangs with perl have issues. Pin nixpkgs this way. nix shell should maybe
# get the same uberhack that nix-shell has to support it.
echo "NIX_PATH=nixpkgs=$NIXPKGS_PATH" >> "$GITHUB_ENV"
- name: Configure AWS credentials
uses: aws-actions/configure-aws-credentials@61815dcd50bd041e203e49132bacad1fd04d2708 # v5.1.1
with:
role-to-assume: "arn:aws:iam::080433136561:role/nix-release"
role-session-name: nix-release-oidc-${{ github.run_id }}
aws-region: eu-west-1
- name: Disable containerd image store
run: |
# Docker 28+ defaults to the containerd image store, which
# pushes layers uncompressed instead of gzip. OCI clients
# that only support gzip (e.g. go-containerregistry) fail
# with "gzip: invalid header". Disabling the containerd
# snapshotter restores the classic storage driver, which
# preserves gzip-compressed layers through the
# `docker load` / `docker push` pipeline.
echo '{"features":{"containerd-snapshotter":false}}' | sudo tee /etc/docker/daemon.json > /dev/null
sudo systemctl restart docker
- name: Login to Docker Hub
uses: docker/login-action@c94ce9fb468520275223c153574b00df6fe4bcc9 # v3.7.0
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: Login to GitHub Container Registry
uses: docker/login-action@c94ce9fb468520275223c153574b00df6fe4bcc9 # v3.7.0
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Upload release
run: |
./maintainers/upload-release.pl \
${{ inputs.eval_id }} \
--skip-git
env:
IS_LATEST: ${{ inputs.is_latest && '1' || '' }}
- name: Push to GHCR
run: |
DOCKER_OWNER="ghcr.io/$(echo '${{ github.repository_owner }}' | tr '[A-Z]' '[a-z]')/nix"
./maintainers/upload-release.pl \
${{ inputs.eval_id }} \
--skip-git \
--skip-s3 \
--docker-owner "$DOCKER_OWNER"
env:
IS_LATEST: ${{ inputs.is_latest && '1' || '' }}

4
.gitignore vendored
View File

@@ -16,6 +16,10 @@ src/.wraplock
/tests/functional/lang/*.err
/tests/functional/lang/*.ast
# /tests/functional/cli-characterisation/
/tests/functional/cli-characterisation/*.out
/tests/functional/cli-characterisation/*.err
/outputs
*~

View File

@@ -1 +1 @@
2.34.0
2.35.0

30
ci/gha/tests/windows.nix Normal file
View File

@@ -0,0 +1,30 @@
{
nixFlake ? builtins.getFlake ("git+file://" + toString ../../..),
system ? builtins.currentSystem,
pkgs ? nixFlake.inputs.nixpkgs.legacyPackages.${system},
}:
let
packages = nixFlake.packages.${system};
fixOutput =
test:
test.overrideAttrs (prev: {
nativeBuildInputs = prev.nativeBuildInputs or [ ] ++ [ pkgs.colorized-logs ];
env.GTEST_COLOR = "no";
# Wine's console emulation wraps every character in ANSI cursor
# hide/show sequences, making logs unreadable in GitHub Actions.
buildCommand = ''
set -o pipefail
{
${prev.buildCommand}
} 2>&1 | ansi2txt
'';
});
in
{
unitTests = {
"nix-util-tests" = fixOutput packages."nix-util-tests-x86_64-w64-mingw32".passthru.tests.run;
};
}

View File

@@ -1,5 +1,6 @@
{
lib,
stdenv,
callPackage,
mkMesonDerivation,
runCommand,
@@ -93,10 +94,11 @@ mkMesonDerivation (finalAttrs: {
mdbook
json-schema-for-humans
]
++ lib.optionals (!officialRelease && buildHtmlManual) [
++ lib.optionals (!officialRelease && buildHtmlManual && !stdenv.hostPlatform.isi686) [
# When not an official release, we likely have changelog entries that have
# yet to be rendered.
# When released, these are rendered into a committed file to save a dependency.
# Broken on i686.
changelog-d
];

View File

@@ -0,0 +1,81 @@
---
synopsis: "Content-addressed derivations: realisations keyed by store path instead of hash modulo"
issues: [11897]
prs: [12464]
---
The experimental content-addressed (CA) derivation feature has undergone a significant change to how build traces (formerly called "realisations") are identified. This affects the **binary cache protocol** and the **wire protocols**.
### What changed
Previously, a build trace entry (realisation) was keyed by the **hash modulo** of the derivation.
A SHA-256 hash computed via the complex "derivation hash modulo" algorithm.
This required implementations to understand ATerm serialisation and the full derivation hashing scheme just to look up or store build results.
Now, build trace entries are keyed by the **regular derivation store path** plus the output name. For example, instead of:
```
sha256:ba7816bf8f01...!out
```
The key is now:
```
/nix/store/abc...-foo.drv^out
```
This is simpler, more intuitive, and means that third-party tools implementing CA derivation support (e.g., Hydra)
no longer need to implement the derivation hash modulo algorithm.
### Binary cache protocol
- The directory for build traces moved from `realisations/` to `build-trace-v2/`.
- File paths changed from `realisations/<hash>!<output>.doi` to `build-trace-v2/<drvName>/<outputName>.doi`.
- The JSON format of build trace entries is now split into `key` and `value` objects:
```json
{
"key": {
"drvPath": "abc...-foo.drv",
"outputName": "out"
},
"value": {
"outPath": "xyz...-foo",
"signatures": [{ "keyName": "cache.example.com-1", "sig": "..." }]
}
}
```
Previously, these were flat objects with a string `id` field like `"sha256:...!out"`.
- The deprecated `dependentRealisations` field has been removed.
Existing binary caches will need to be re-populated with the new format for CA derivation build traces.
Old build traces at the previous URLs are simply abandoned.
Non-CA builds are unaffected.
### Wire protocols
- **Worker protocol**:
A new feature flag `realisation-with-path-not-hash` is negotiated during the handshake.
Clients and daemons that both support this feature use the new binary serialisation for `DrvOutput`, `UnkeyedRealisation`, and related types.
Fallback to older protocol versions gracefully degrades (realisations are unavailable).
- **Serve protocol**:
Bumped from 2.7 to 2.8 with native serialisers for the new types.
Fallback to older protocol versions gracefully degrades in the same way.
Stable code paths do use the realization fields (`BuildResult::Success::builtOutputs`), but only the output name and outpath parts of that.
For older protocols, we can fake enough of the realisation format to provide those two parts forthat map, which keeps operations like `--print-output-paths` working.
### Structured signatures
[Signatures](@docroot@/protocols/json/signature.md) in JSON formats are now represented as structured objects with `keyName` and `sig` fields, rather than colon-separated strings.
`nix path-info --json --json-format 3` opts into the new version for this command.
JSON parsing accepts both the old string format and new structured format for backwards compatibility.
### Impact
- **Non-CA derivation users**: No impact. This only affects the experimental `ca-derivations` feature.
- **Binary cache operators**:
Binary caches serving CA derivation build traces will need to be repopulated.
Existing NARs and narinfo files are unaffected.
- **Tool authors**:
Implementations interfacing with the CA derivations protocol are simplified.
The derivation hash modulo algorithm is no longer required to form build trace keys.

View File

@@ -1,9 +0,0 @@
---
synopsis: "C API: New store API methods"
prs: [14766]
---
The C API now includes additional methods:
- `nix_store_query_path_from_hash_part()` - Get the full store path given its hash part
- `nix_store_copy_path()` - Copy a single store path between two stores, allows repairs and configuring signature checking

View File

@@ -0,0 +1,10 @@
---
synopsis: "C API: Fix `EvalState` pointer passed to primop callbacks"
prs: [15300, 15383]
---
The `EvalState *` passed to C API primop callbacks was incorrectly pointing to
the internal `nix::EvalState` rather than the C API wrapper struct. This caused
a segfault when the callback used the pointer with C API functions such as
`nix_alloc_value()`. The same issue affected `printValueAsJSON` and
`printValueAsXML` callbacks on external values.

View File

@@ -0,0 +1,7 @@
---
synopsis: GitHub fetcher now validates URL parameters
prs: [15331]
issues: [15304]
---
The `github:` fetcher now validates URL parameters, and will error if an invalid parameter like `tag` is provided.

View File

@@ -1,10 +0,0 @@
---
synopsis: "New setting `ignore-gc-delete-failure` for local stores"
prs: [15054]
---
A new local store setting [`ignore-gc-delete-failure`](@docroot@/store/types/local-store.md#store-local-store-ignore-gc-delete-failure) has been added.
When enabled, garbage collection will log warnings instead of failing when it cannot delete store paths.
This is useful when running Nix as an unprivileged user that may not have write access to all paths in the store.
This setting is experimental and requires the [`local-overlay-store`](@docroot@/development/experimental-features.md#xp-feature-local-overlay-store) experimental feature.

View File

@@ -1,15 +0,0 @@
---
synopsis: Support HTTPS binary caches using mTLS (client certificate) authentication
issues: [13002]
prs: [13030]
---
Added support for `tls-certificate` and `tls-private-key` options in substituter URLs.
Example:
```
https://substituter.invalid?tls-certificate=/path/to/cert.pem&tls-private-key=/path/to/key.pem
```
When these options are configured, Nix will use this certificate/private key pair to authenticate to the server.

View File

@@ -1,11 +0,0 @@
---
synopsis: New command `nix store roots-daemon` for serving GC roots
prs: [15143]
---
New command [`nix store roots-daemon`](@docroot@/command-ref/new-cli/nix3-store-roots-daemon.md) runs a daemon that serves garbage collector roots over a Unix domain socket.
It enables the garbage collector to discover runtime roots when the main Nix daemon doesn't have `CAP_SYS_PTRACE` capability and therefore cannot scan `/proc`.
The garbage collector can be configured to use this daemon via the [`use-roots-daemon`](@docroot@/store/types/local-store.md#store-experimental-option-use-roots-daemon) store setting.
This feature requires the [`local-overlay-store` experimental feature](@docroot@/development/experimental-features.md#xp-feature-local-overlay-store).

View File

@@ -1,32 +0,0 @@
---
synopsis: S3 binary caches now use virtual-hosted-style addressing by default
issues: [15208]
---
S3 binary caches now use virtual-hosted-style URLs
(`https://bucket.s3.region.amazonaws.com/key`) instead of path-style URLs
(`https://s3.region.amazonaws.com/bucket/key`) when connecting to standard AWS
S3 endpoints. This enables HTTP/2 multiplexing and fixes TCP connection
exhaustion (TIME_WAIT socket accumulation) under high-concurrency workloads.
A new `addressing-style` store option controls this behavior:
- `auto` (default): virtual-hosted-style for standard AWS endpoints, path-style
for custom endpoints.
- `path`: forces path-style addressing (deprecated by AWS).
- `virtual`: forces virtual-hosted-style addressing (bucket names must not
contain dots).
Bucket names containing dots (e.g., `my.bucket.name`) automatically fall back
to path-style addressing in `auto` mode, because dotted names create
multi-level subdomains that break TLS wildcard certificate validation.
Example using path-style for backwards compatibility:
```
s3://my-bucket/key?region=us-east-1&addressing-style=path
```
Additionally, TCP keep-alive is now enabled on all HTTP connections, preventing
idle connections from being silently dropped by intermediate network devices
(NATs, firewalls, load balancers).

View File

@@ -125,6 +125,7 @@
- [Hash](protocols/json/hash.md)
- [Content Address](protocols/json/content-address.md)
- [Store Path](protocols/json/store-path.md)
- [Signature](protocols/json/signature.md)
- [Store Object Info](protocols/json/store-object-info.md)
- [Derivation](protocols/json/derivation/index.md)
- [Derivation Options](protocols/json/derivation/options.md)
@@ -153,6 +154,7 @@
- [Contributing](development/contributing.md)
- [Releases](release-notes/index.md)
{{#include ./SUMMARY-rl-next.md}}
- [Release 2.34 (2026-02-27)](release-notes/rl-2.34.md)
- [Release 2.33 (2025-12-09)](release-notes/rl-2.33.md)
- [Release 2.32 (2025-10-06)](release-notes/rl-2.32.md)
- [Release 2.31 (2025-08-21)](release-notes/rl-2.31.md)

View File

@@ -39,7 +39,7 @@
This sandbox by default only allows reading from store objects specified as inputs, and only allows writing to designated [outputs][output] to be [captured as store objects](@docroot@/store/building.md#processing-outputs).
A derivation is typically specified as a [derivation expression] in the [Nix language], and [instantiated][instantiate] to a [store derivation].
There are multiple ways of obtaining store objects from store derivatons, collectively called [realisation][realise].
There are multiple ways of obtaining store objects from store derivations, collectively called [realisation][realise].
[derivation]: #gloss-derivation

View File

@@ -16,30 +16,29 @@ If you are on Linux with systemd:
sudo systemctl daemon-reload
```
Remove files created by Nix:
2. Remove files created by Nix:
```console
sudo rm -rf /etc/nix /etc/profile.d/nix.sh /etc/tmpfiles.d/nix-daemon.conf /nix ~root/.nix-channels ~root/.nix-defexpr ~root/.nix-profile ~root/.cache/nix
```
```console
sudo rm -rf /etc/nix /etc/profile.d/nix.sh /etc/tmpfiles.d/nix-daemon.conf /nix ~/.local/share/nix ~/.local/state/nix ~/.cache/nix ~/.nix-defexpr ~/.nix-profile ~/.nix-channels ~root/.nix-channels ~root/.nix-defexpr ~root/.nix-profile ~root/.cache/nix
```
Remove build users and their group:
3. Remove build users and their group:
```console
for i in $(seq 1 32); do
sudo userdel nixbld$i
done
sudo groupdel nixbld
```
```console
for i in $(seq 1 32); do
sudo userdel nixbld$i
done
sudo groupdel nixbld
```
There may also be references to Nix in
4. There may also be references to Nix in
- `/etc/bash.bashrc`
- `/etc/bashrc`
- `/etc/profile`
- `/etc/zsh/zshrc`
- `/etc/zshrc`
- `/etc/bash.bashrc`
- `/etc/bashrc`
- `/etc/profile`
- `/etc/zsh/zshrc`
- `/etc/zshrc`
which you may remove.
which you may remove.
### FreeBSD
@@ -54,7 +53,7 @@ which you may remove.
2. Remove files created by Nix:
```console
sudo rm -rf /etc/nix /usr/local/etc/profile.d/nix.sh /nix ~root/.nix-channels ~root/.nix-defexpr ~root/.nix-profile ~root/.cache/nix
sudo rm -rf /etc/nix /usr/local/etc/profile.d/nix.sh /nix ~/.local/share/nix ~/.local/state/nix ~/.cache/nix ~/.nix-defexpr ~/.nix-profile ~/.nix-channels ~root/.nix-channels ~root/.nix-defexpr ~root/.nix-profile ~root/.cache/nix
```
3. Remove build users and their group:
@@ -154,7 +153,7 @@ which you may remove.
6. Remove the files Nix added to your system, except for the store:
```console
sudo rm -rf /etc/nix /var/root/.nix-profile /var/root/.nix-defexpr /var/root/.nix-channels ~/.nix-profile ~/.nix-defexpr ~/.nix-channels
sudo rm -rf /etc/nix /var/root/.nix-profile /var/root/.nix-defexpr /var/root/.nix-channels ~/.nix-profile ~/.nix-defexpr ~/.nix-channels ~/.local/share/nix ~/.local/state/nix ~/.cache/nix
```
@@ -192,6 +191,6 @@ which you may remove.
To remove a [single-user installation](./installing-binary.md#single-user-installation) of Nix, run:
```console
rm -rf /nix ~/.nix-channels ~/.nix-defexpr ~/.nix-profile
rm -rf /nix ~/.nix-channels ~/.nix-defexpr ~/.nix-profile ~/.local/share/nix ~/.local/state/nix ~/.cache/nix
```
You might also want to manually remove references to Nix from your `~/.profile`.

View File

@@ -1,21 +1,21 @@
{{#include build-trace-entry-v2-fixed.md}}
{{#include build-trace-entry-v3-fixed.md}}
## Examples
### Simple build trace entry
```json
{{#include schema/build-trace-entry-v2/simple.json}}
{{#include schema/build-trace-entry-v3/simple.json}}
```
### Build trace entry with signature
```json
{{#include schema/build-trace-entry-v2/with-signature.json}}
{{#include schema/build-trace-entry-v3/with-structured-signature.json}}
```
<!--
## Raw Schema
[JSON Schema for Build Trace Entry v1](schema/build-trace-entry-v2.json)
[JSON Schema for Build Trace Entry v1](schema/build-trace-entry-v3.json)
-->

View File

@@ -13,11 +13,12 @@ schemas = [
'hash-v1',
'content-address-v1',
'store-path-v1',
'store-object-info-v2',
'signature-v2',
'store-object-info-v3',
'derivation-v4',
'derivation-options-v1',
'deriving-path-v1',
'build-trace-entry-v2',
'build-trace-entry-v3',
'build-result-v1',
'store-v1',
]

View File

@@ -83,7 +83,7 @@ properties:
description: |
A mapping from output names to their build trace entries.
additionalProperties:
"$ref": "build-trace-entry-v2.yaml"
"$ref": "build-trace-entry-v3.yaml#/$defs/value"
failure:
type: object

View File

@@ -1,5 +1,5 @@
"$schema": "http://json-schema.org/draft-04/schema"
"$id": "https://nix.dev/manual/nix/latest/protocols/json/schema/build-trace-entry-v2.json"
"$id": "https://nix.dev/manual/nix/latest/protocols/json/schema/build-trace-entry-v3.json"
title: Build Trace Entry
description: |
A record of a successful build outcome for a specific derivation output.
@@ -12,28 +12,28 @@ description: |
> [**experimental**](@docroot@/development/experimental-features.md#xp-feature-ca-derivations)
> and subject to change.
Verision history:
## Version History
- Version 1: Original format
- Version 2: Remove `dependentRealisations`
- Version 2:
- Remove `dependentRealisations`
- Version 3:
- Use `drvPath` not `drvHash` to refer to derivation in a more conventional way.
- Separate into `key` and `value`
- Use 2nd version of signatures format (objects, not strings)
type: object
required:
- id
- outPath
- signatures
allOf:
- "$ref": "#/$defs/key"
- "$ref": "#/$defs/value"
- key
- value
properties:
id: {}
outPath: {}
signatures: {}
additionalProperties:
dependentRealisations:
description: deprecated field
type: object
key:
"$ref": "#/$defs/key"
value:
"$ref": "#/$defs/value"
additionalProperties: false
"$defs":
key:
@@ -43,23 +43,20 @@ additionalProperties:
This is the "key" part, refering to a derivation and output.
type: object
required:
- id
- drvPath
- outputName
properties:
id:
type: string
title: Derivation Output ID
pattern: "^sha256:[0-9a-f]{64}![a-zA-Z_][a-zA-Z0-9_-]*$"
drvPath:
"$ref": "store-path-v1.yaml"
title: Derivation Path
description: |
Unique identifier for the derivation output that was built.
Format: `{hash-quotient-drv}!{output-name}`
- **hash-quotient-drv**: SHA-256 [hash of the quotient derivation](@docroot@/store/derivation/outputs/input-address.md#hash-quotient-drv).
Begins with `sha256:`.
- **output-name**: Name of the specific output (e.g., "out", "dev", "doc")
Example: `"sha256:ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad!foo"`
The store path of the derivation that was built.
outputName:
type: string
title: Output Name
description: |
Name of the specific output (e.g., "out", "dev", "doc")
additionalProperties: false
value:
title: Build Trace Value
@@ -77,19 +74,10 @@ additionalProperties:
description: |
The path to the store object that resulted from building this derivation for the given output name.
patternProperties:
"^sha256:[0-9a-f]{64}![a-zA-Z_][a-zA-Z0-9_-]*$":
"$ref": "store-path-v1.yaml"
title: Dependent Store Path
description: Store path that this dependency resolved to during the build
additionalProperties: false
signatures:
type: array
title: Build Signatures
description: |
A set of cryptographic signatures attesting to the authenticity of this build trace entry.
items:
type: string
title: Signature
description: A single cryptographic signature
"$ref": "signature-v2.yaml"

View File

@@ -1 +1 @@
../../../../../../src/libutil-tests/data/hash/
../../../../../../src/libutil-tests/data/hash

View File

@@ -0,0 +1 @@
../../../../../../src/libstore-tests/data/nar-info/json-3

View File

@@ -0,0 +1,33 @@
"$schema": "http://json-schema.org/draft-07/schema"
"$id": "https://nix.dev/manual/nix/latest/protocols/json/schema/signature-v2.json"
title: Signature
description: |
A cryptographic signature along with the name of the key that produced it.
This schema describes the JSON representation of signatures as used in various Nix JSON APIs.
> **Warning**
>
> This JSON format is currently
> [**experimental**](@docroot@/development/experimental-features.md#xp-feature-nix-command)
> and subject to change.
## Version History
- Version 1: Colon-separated string in the format `<key-name>:<signature-in-Base64>`
- Version 2: Structured object with `keyName` and `sig` fields
type: object
required:
- keyName
- sig
properties:
keyName:
type: string
title: Key Name
description: The name of the key used to produce this signature
sig:
type: string
title: Signature Data
description: The raw signature bytes, Base64-encoded

View File

@@ -0,0 +1,4 @@
{
"keyName": "cache.nixos.org-1",
"sig": "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
}

View File

@@ -1 +0,0 @@
../../../../../../src/libstore-tests/data/path-info/json-2

View File

@@ -0,0 +1 @@
../../../../../../src/libstore-tests/data/path-info/json-3

View File

@@ -1,6 +1,6 @@
"$schema": "http://json-schema.org/draft-04/schema"
"$id": "https://nix.dev/manual/nix/latest/protocols/json/schema/store-object-info-v2.json"
title: Store Object Info v2
"$id": "https://nix.dev/manual/nix/latest/protocols/json/schema/store-object-info-v3.json"
title: Store Object Info v3
description: |
Information about a [store object](@docroot@/store/store-object.md).
@@ -50,10 +50,10 @@ $defs:
properties:
version:
type: integer
const: 2
title: Format version (must be 2)
const: 3
title: Format version (must be 3)
description: |
Must be `2`.
Must be `3`.
This is a guard that allows us to continue evolving this format.
Here is the rough version history:
@@ -63,6 +63,8 @@ $defs:
- Version 2: Use structured JSON type for `ca`
- Version 3: Use structured JSON type for `signatures`
path:
"$ref": "./store-path-v1.yaml"
title: Store Path
@@ -174,7 +176,7 @@ $defs:
> This is an "impure" field that may not be included in certain contexts.
items:
type: string
"$ref": "./signature-v2.yaml"
# Computed closure fields
closureSize:

View File

@@ -37,7 +37,7 @@ properties:
- contents
properties:
info:
"$ref": "./store-object-info-v2.yaml#/$defs/impure"
"$ref": "./store-object-info-v3.yaml#/$defs/impure"
title: Store Object Info
description: |
Metadata about the [store object](@docroot@/store/store-object.md) including hash, size, references, etc.
@@ -70,7 +70,7 @@ properties:
"^[A-Za-z0-9+/]{43}=$":
type: object
additionalProperties:
"$ref": "./build-trace-entry-v2.yaml#/$defs/value"
"$ref": "./build-trace-entry-v3.yaml#/$defs/value"
additionalProperties: false
"$defs":

View File

@@ -0,0 +1,9 @@
{{#include signature-v2-fixed.md}}
## Examples
### Simple signature
```json
{{#include schema/signature-v2/simple.json}}
```

View File

@@ -1,45 +1,45 @@
{{#include store-object-info-v2-fixed.md}}
{{#include store-object-info-v3-fixed.md}}
## Examples
### Minimal store object (content-addressed)
```json
{{#include schema/store-object-info-v2/pure.json}}
{{#include schema/store-object-info-v3/pure.json}}
```
### Store object with impure fields
```json
{{#include schema/store-object-info-v2/impure.json}}
{{#include schema/store-object-info-v3/impure.json}}
```
### Minimal store object (empty)
```json
{{#include schema/store-object-info-v2/empty_pure.json}}
{{#include schema/store-object-info-v3/empty_pure.json}}
```
### Store object with all impure fields
```json
{{#include schema/store-object-info-v2/empty_impure.json}}
{{#include schema/store-object-info-v3/empty_impure.json}}
```
### NAR info (minimal)
```json
{{#include schema/nar-info-v2/pure.json}}
{{#include schema/nar-info-v3/pure.json}}
```
### NAR info (with binary cache fields)
```json
{{#include schema/nar-info-v2/impure.json}}
{{#include schema/nar-info-v3/impure.json}}
```
<!-- need to convert YAML to JSON first
## Raw Schema
[JSON Schema for Store Object Info v1](schema/store-object-info-v2.json)
[JSON Schema for Store Object Info v1](schema/store-object-info-v3.json)
-->

View File

@@ -0,0 +1,352 @@
# Release 2.34.0 (2026-02-27)
## Highlights
- Rust nix-installer in beta
The Rust-based rewrite of the Nix installer is now in beta.
We'd love help testing it out!
To test out the new installer, run:
```
curl -sSfL https://artifacts.nixos.org/nix-installer | sh -s -- install
```
This installer can be run even when you have an existing, script-based Nix installation without any adjustments.
This new installer also comes with the ability to uninstall your Nix installation; run:
```
/nix/nix-installer uninstall
```
This will get rid of your entire Nix installation (even if you installed over an existing, script-based installation).
This installer is a modified version of the [Determinate Nix Installer](https://github.com/DeterminateSystems/nix-installer) by Determinate Systems.
Thanks to Determinate Systems for all the investment they've put into the installer.
Source for the installer is in <https://github.com/NixOS/nix-installer>.
Report any issues in that repo.
For CI usage, a GitHub Action to install Nix using this installer is available at <https://github.com/NixOS/nix-installer-action>.
- Stabilisation of `no-url-literals` experimental feature and new diagnostics infrastructure, with `lint-url-literals`, `lint-short-path-literals`, and `lint-absolute-path-literals` settings [#8738](https://github.com/NixOS/nix/issues/8738) [#10048](https://github.com/NixOS/nix/issues/10048) [#10281](https://github.com/NixOS/nix/issues/10281) [#15326](https://github.com/NixOS/nix/pull/15326)
Experimental feature `no-url-literals` has been stabilised and is now controlled by the `lint-url-literals` option.
New diagnostics infrastructure has been added for linting discouraged language features.
### New lint infrastructure
#### [`lint-url-literals`](@docroot@/command-ref/conf-file.md#conf-lint-url-literals)
The `no-url-literals` experimental feature has been stabilised and replaced with a new [`lint-url-literals`](@docroot@/command-ref/conf-file.md#conf-lint-url-literals) setting.
To migrate from the experimental feature, replace:
```
experimental-features = no-url-literals
```
with:
```
lint-url-literals = fatal
```
#### [`lint-short-path-literals`](@docroot@/command-ref/conf-file.md#conf-lint-short-path-literals)
The [`warn-short-path-literals`](@docroot@/command-ref/conf-file.md#conf-warn-short-path-literals) boolean setting has been deprecated and replaced with [`lint-short-path-literals`](@docroot@/command-ref/conf-file.md#conf-lint-short-path-literals).
To migrate, replace:
```
warn-short-path-literals = true
```
with:
```
lint-short-path-literals = warn
```
#### [`lint-absolute-path-literals`](@docroot@/command-ref/conf-file.md#conf-lint-absolute-path-literals)
A new [`lint-absolute-path-literals`](@docroot@/command-ref/conf-file.md#conf-lint-absolute-path-literals) setting has been added to control handling of absolute path literals (paths starting with `/`) and home path literals (paths starting with `~/`).
#### Setting values
All three settings accept three values:
- `ignore`: Allow the feature without emitting any diagnostic (default)
- `warn`: Emit a warning when the feature is used
- `fatal`: Treat the feature as a parse error
The defaults may change in future versions.
- Improved parser error messages [#15092](https://github.com/NixOS/nix/pull/15092)
Parser error messages now use legible strings for tokens instead of internal names. For example, malformed expression `a ++ ++ b` now produces the following error:
```
error: syntax error, unexpected '++'
at «string»:1:6:
1| a ++ ++ b
| ^
```
Instead of:
```
error: syntax error, unexpected CONCAT
at «string»:1:6:
1| a ++ ++ b
| ^
```
## New features
- `nix repl` now supports `inherit` and multiple bindings [#15082](https://github.com/NixOS/nix/pull/15082)
The `nix repl` now supports `inherit` statements and multiple bindings per line:
```
nix-repl> a = { x = 1; y = 2; }
nix-repl> inherit (a) x y
nix-repl> x + y
3
nix-repl> p = 1; q = 2;
nix-repl> p + q
3
nix-repl> foo.bar.baz = 1;
nix-repl> foo.bar
{ baz = 1; }
```
- New command `nix store roots-daemon` for serving GC roots [#15143](https://github.com/NixOS/nix/pull/15143)
New command [`nix store roots-daemon`](@docroot@/command-ref/new-cli/nix3-store-roots-daemon.md) runs a daemon that serves garbage collector roots over a Unix domain socket.
It enables the garbage collector to discover runtime roots when the main Nix daemon doesn't have `CAP_SYS_PTRACE` capability and therefore cannot scan `/proc`.
The garbage collector can be configured to use this daemon via the [`use-roots-daemon`](@docroot@/store/types/local-store.md#store-experimental-option-use-roots-daemon) store setting.
This feature requires the [`local-overlay-store` experimental feature](@docroot@/development/experimental-features.md#xp-feature-local-overlay-store).
- New command `nix-nswrapper` in `libexec` [#15183](https://github.com/NixOS/nix/pull/15183)
The new command `libexec/nix-nswrapper` is used to run the Nix daemon in an unprivileged user namespace on Linux. In order to use this command, build user UIDs and GIDs must be allocated in `/etc/subuid` and `/etc/subgid`.
It can be used to run the Nix daemon with full sandboxing without executing as root. Support has been added to Nixpkgs with the new `nix.daemonUser` and `nix.daemonGroup` settings.
- New setting `ignore-gc-delete-failure` for local stores [#15054](https://github.com/NixOS/nix/pull/15054)
A new local store setting [`ignore-gc-delete-failure`](@docroot@/store/types/local-store.md#store-local-store-ignore-gc-delete-failure) has been added.
When enabled, garbage collection will log warnings instead of failing when it cannot delete store paths.
This is useful when running Nix as an unprivileged user that may not have write access to all paths in the store.
This setting is experimental and requires the [`local-overlay-store`](@docroot@/development/experimental-features.md#xp-feature-local-overlay-store) experimental feature.
- New setting `narinfo-cache-meta-ttl` [#15287](https://github.com/NixOS/nix/pull/15287)
The new setting `narinfo-cache-meta-ttl` controls how long binary cache metadata (i.e. `/nix-cache-info`) is cached locally, in seconds. This was previously hard-coded to 7 days, which is still the default. As a result, you can now use `nix store info --refresh` to check whether a binary cache is still valid.
- Support HTTPS binary caches using mTLS (client certificate) authentication [#13002](https://github.com/NixOS/nix/issues/13002) [#13030](https://github.com/NixOS/nix/pull/13030)
Added support for `tls-certificate` and `tls-private-key` options in substituter URLs.
Example:
```
https://substituter.invalid?tls-certificate=/path/to/cert.pem&tls-private-key=/path/to/key.pem
```
When these options are configured, Nix will use this certificate/private key pair to authenticate to the server.
- `nix store gc --dry-run` and `nix-collect-garbage --dry-run` now report the number of paths that would be freed [#15229](https://github.com/NixOS/nix/pull/15229) [#5704](https://github.com/NixOS/nix/issues/5704)
## Performance improvements
- Unpacking tarballs to `~/.cache/nix/tarball-cache-v2` is now multithreaded [#12087](https://github.com/NixOS/nix/pull/12087)
Content-addressed cache for `builtins.fetchTarball` and tarball-based flake inputs (e.g. `github:NixOS/nixpkgs`, `https://channels.nixos.org/nixos-25.11/nixexprs.tar.xz`) now writes git blobs (files) to the `tarball-cache-v2` repository concurrently, which significantly reduces the wall time for tarball unpacking (up to ~1.8x faster unpacking for `https://channels.nixos.org/nixos-25.11/nixexprs.tar.xz` in our testing).
Currently, Nix doesn't perform any maintenance on the `~/.cache/nix/tarball-cache-v2` repository, which will be addressed in future versions. Users that wish to reclaim disk space used by the tarball cache may want to run:
```
rm -rf ~/.cache/nix/tarball-cache # Historical tarball-cache, not used by Nix >= 2.33
cd ~/.cache/nix/tarball-cache-v2 && git multi-pack-index write && git multi-pack-index repack && git multi-pack-index expire
```
- `nix nar ls` and other NAR listing operations have been optimised further [#15163](https://github.com/NixOS/nix/pull/15163)
- Evaluator hot-path optimizations [#15270](https://github.com/NixOS/nix/pull/15270) [#15271](https://github.com/NixOS/nix/pull/15271)
## C API Changes
- New store API methods [#14766](https://github.com/NixOS/nix/pull/14766) [#14768](https://github.com/NixOS/nix/pull/14768)
The C API now includes additional methods:
- `nix_store_query_path_from_hash_part()` - Get the full store path given its hash part
- `nix_store_copy_path()` - Copy a single store path between two stores, allows repairs and configuring signature checking
- Errors returned from your primops are not treated as recoverable by default [#13930](https://github.com/NixOS/nix/pull/13930) [#15286](https://github.com/NixOS/nix/pull/15286)
Nix 2.34 by default remembers the error in the thunk that triggered it.
Previously the following sequence of events worked:
1. Have a thunk that invokes a primop that's defined through the C API
2. The primop returns an error
3. Force the thunk again
4. The primop returns a value
5. The thunk evaluated successfully
**Resolution**
C API consumers that rely on this must change their recoverable error calls:
```diff
-nix_set_err_msg(context, NIX_ERR_*, msg);
+nix_set_err_msg(context, NIX_ERR_RECOVERABLE, msg);
```
## Bug fixes
- Avoid dropping ssh connections with `ssh-ng://` stores for store path copying [#14998](https://github.com/NixOS/nix/pull/14998) [#6950](https://github.com/NixOS/nix/issues/6950)
Due to a bug in how Nix handled Boost.Coroutine2 suspension and resumption, copying from `ssh-ng://` stores would drop the SSH connection for each copied path. This issue has been fixed, which improves performance by avoiding multiple SSH/Nix Worker Protocol handshakes.
- S3 binary caches now use virtual-hosted-style addressing by default [#15208](https://github.com/NixOS/nix/issues/15208) [#15216](https://github.com/NixOS/nix/pull/15216)
S3 binary caches now use virtual-hosted-style URLs
(`https://bucket.s3.region.amazonaws.com/key`) instead of path-style URLs
(`https://s3.region.amazonaws.com/bucket/key`) when connecting to standard AWS
S3 endpoints. This enables HTTP/2 multiplexing and fixes TCP connection
exhaustion (TIME_WAIT socket accumulation) under high-concurrency workloads.
A new `addressing-style` store option controls this behavior:
- `auto` (default): virtual-hosted-style for standard AWS endpoints, path-style
for custom endpoints.
- `path`: forces path-style addressing (deprecated by AWS).
- `virtual`: forces virtual-hosted-style addressing (bucket names must not
contain dots).
Bucket names containing dots (e.g., `my.bucket.name`) automatically fall back
to path-style addressing in `auto` mode, because dotted names create
multi-level subdomains that break TLS wildcard certificate validation.
Example using path-style for backwards compatibility:
```
s3://my-bucket/key?region=us-east-1&addressing-style=path
```
Additionally, TCP keep-alive is now enabled on all HTTP connections, preventing
idle connections from being silently dropped by intermediate network devices
(NATs, firewalls, load balancers).
- `nix-prefetch-url --unpack` now properly checks for empty archives [#15242](https://github.com/NixOS/nix/pull/15242)
Prior versions failed to check for empty archives and would crash with a `nullptr` dereference when unpacking empty archives.
This is now fixed.
- Prevent runaway processes when Nix is killed with `SIGKILL` when building in a local store with build users [#15193](https://github.com/NixOS/nix/pull/15193)
When run as root, Nix doesn't run builds via the daemon and is a parent of the forked build processes. Prior versions of Nix failed to preserve the `PR_SET_PDEATHSIG` parent-death signal across `setuid` calls. This could lead to build processes being reparented and continue running in the background. This has been fixed.
- Fix crash when interrupting `--log-format internal-json` [#15335](https://github.com/NixOS/nix/pull/15335)
Pressing Ctrl-C during `--log-format internal-json` (used by [nix-output-monitor](https://github.com/maralorn/nix-output-monitor)) no longer causes a spurious "Nix crashed. This is a bug." report.
- Fix percent-encoding in `file://` and `local://` store URIs [#15280](https://github.com/NixOS/nix/pull/15280)
Store URIs with special characters like `+` in the path (e.g. `file:///tmp/a+b`) no longer incorrectly create percent-encoded directories (e.g. `/tmp/a%2Bb`).
- Fix crash during tab completion in `nix repl` [#15255](https://github.com/NixOS/nix/pull/15255)
- Fix "Too many open files" on macOS [#15205](https://github.com/NixOS/nix/pull/15205)
Nix now raises the open file soft limit to the hard limit at startup, fixing "Too many open files" errors on macOS where the default soft limit is low.
- `nix develop` no longer fails when `inputs.nixpkgs` has `flake = false` [#15175](https://github.com/NixOS/nix/pull/15175)
- `builtins.flakeRefToString` no longer fails with "attribute is a thunk" [#15160](https://github.com/NixOS/nix/pull/15160)
- Fix `QueryPathInfo` throwing on invalid paths in the daemon [#15134](https://github.com/NixOS/nix/pull/15134)
- `nix-store --generate-binary-cache-key` now fsyncs key files to prevent corruption [#15107](https://github.com/NixOS/nix/pull/15107)
- Fix `build-hook` setting in `nix.conf` being ignored [#15083](https://github.com/NixOS/nix/pull/15083)
- Fix empty error messages when builds are cancelled due to a dependency failure [#14972](https://github.com/NixOS/nix/pull/14972)
When a build fails without `--keep-going`, other in-progress builds are cancelled. Previously, these cancelled builds were incorrectly reported as failed with empty error messages. This affected `buildPathsWithResults` callers such as `nix flake check`.
## Miscellaneous changes
- Content-Encoding decompression is now handled by libcurl [#14324](https://github.com/NixOS/nix/issues/14324) [#15336](https://github.com/NixOS/nix/pull/15336)
Transparent decompression of HTTP downloads specifying `Content-Encoding` header now uses libcurl. This adds support for previously advertised, but not supported `deflate` encoding as well as deprecated `x-gzip` alias.
Non-standard `xz`, `bzip2` encodings that were previously advertised are no longer supported, as they do not commonly appear in the wild and should not be sent by compliant servers.
`br`, `zstd`, `gzip` continue to be supported. Distro packaging should ensure that the `libcurl` dependency is linked against required libraries to support these encodings. By default, the build system now requires libcurl >= 8.17.0, which is not known to have issues around [pausing and decompression](https://github.com/curl/curl/issues/16280).
- Static builds now support S3 features (`libstore:s3-aws-auth` meson option) [#15076](https://github.com/NixOS/nix/pull/15076)
- Improved package-related error messages [#15349](https://github.com/NixOS/nix/pull/15349)
Store path context is now rendered in the user-facing `hash^out` format instead of the internal `!out!hash` format.
A misleading error message in `nix-env` that incorrectly blamed content-addressed derivations has been fixed.
- Improved error message for empty derivation files [#15298](https://github.com/NixOS/nix/pull/15298)
Parsing an empty `.drv` file (e.g. due to store corruption after an unclean shutdown) now produces a clear error message instead of the cryptic `expected string 'D'`.
- Relative `file:` paths for tarballs are now rejected with a clear error [#14983](https://github.com/NixOS/nix/pull/14983)
- Continued progress on the Windows port, including build fixes, CI improvements, and platform abstractions.
- Nix docker images are now uploaded to [GHCR](https://github.com/NixOS/nix/pkgs/container/nix) as part of the release process
Historically, only pre-release builds of `amd64` docker images have been uploaded to ghcr.io with the `latest` tag pointing to the last built image from `master` branch. This has been fixed and going forward, <https://github.com/NixOS/nix/pkgs/container/nix> will include the same images as <https://hub.docker.com/r/nixos/nix/> that are built by [Hydra](https://hydra.nixos.org/project/nix) for [arm64](https://hydra.nixos.org/job/nix/maintenance-2.34/dockerImage.aarch64-linux) and [amd64](https://hydra.nixos.org/job/nix/maintenance-2.34/dockerImage.x86_64-linux). Pre-release versions are no longer pushed to the registry.
## Contributors
This release was made possible by the following 43 contributors:
- Taeer Bar-Yam [**(@Radvendii)**](https://github.com/Radvendii)
- Sergei Zimmerman [**(@xokdvium)**](https://github.com/xokdvium)
- Jörg Thalheim [**(@Mic92)**](https://github.com/Mic92)
- Graham Dennis [**(@GrahamDennis)**](https://github.com/GrahamDennis)
- Damien Diederen [**(@ztzg)**](https://github.com/ztzg)
- koberbe-jh [**(@koberbe-jh)**](https://github.com/koberbe-jh)
- Robert Hensing [**(@roberth)**](https://github.com/roberth)
- Bouke van der Bijl [**(@bouk)**](https://github.com/bouk)
- Lisanna Dettwyler [**(@lisanna-dettwyler)**](https://github.com/lisanna-dettwyler)
- kiara [**(@KiaraGrouwstra)**](https://github.com/KiaraGrouwstra)
- Side Effect [**(@YawKar)**](https://github.com/YawKar)
- dram [**(@dramforever)**](https://github.com/dramforever)
- tomf [**(@tomfitzhenry)**](https://github.com/tomfitzhenry)
- Kamil Monicz [**(@Zaczero)**](https://github.com/Zaczero)
- Cosima Neidahl [**(@OPNA2608)**](https://github.com/OPNA2608)
- Siddhant Kumar [**(@siddhantk232)**](https://github.com/siddhantk232)
- Jens Petersen [**(@juhp)**](https://github.com/juhp)
- Johannes Kirschbauer [**(@hsjobeki)**](https://github.com/hsjobeki)
- tomberek [**(@tomberek)**](https://github.com/tomberek)
- Eelco Dolstra [**(@edolstra)**](https://github.com/edolstra)
- Artemis Tosini [**(@artemist)**](https://github.com/artemist)
- David McFarland [**(@corngood)**](https://github.com/corngood)
- Tucker Shea [**(@NoRePercussions)**](https://github.com/NoRePercussions)
- Connor Baker [**(@ConnorBaker)**](https://github.com/ConnorBaker)
- Cole Helbling [**(@cole-h)**](https://github.com/cole-h)
- Eveeifyeve [**(@Eveeifyeve)**](https://github.com/Eveeifyeve)
- John Ericson [**(@Ericson2314)**](https://github.com/Ericson2314)
- Graham Christensen [**(@grahamc)**](https://github.com/grahamc)
- Ilja [**(@iljah)**](https://github.com/iljah)
- Pol Dellaiera [**(@drupol)**](https://github.com/drupol)
- steelman [**(@steelman)**](https://github.com/steelman)
- Brian McKenna [**(@puffnfresh)**](https://github.com/puffnfresh)
- JustAGuyTryingHisBest [**(@JustAGuyTryingHisBest)**](https://github.com/JustAGuyTryingHisBest)
- zowoq [**(@zowoq)**](https://github.com/zowoq)
- Agustín Covarrubias [**(@agucova)**](https://github.com/agucova)
- Sergei Trofimovich [**(@trofi)**](https://github.com/trofi)
- Bernardo Meurer [**(@lovesegfault)**](https://github.com/lovesegfault)
- Peter Bynum [**(@pkpbynum)**](https://github.com/pkpbynum)
- Amaan Qureshi [**(@amaanq)**](https://github.com/amaanq)
- Michael Hoang [**(@Enzime)**](https://github.com/Enzime)
- Michael Daniels [**(@mdaniels5757)**](https://github.com/mdaniels5757)
- Matthew Kenigsberg [**(@mkenigs)**](https://github.com/mkenigs)
- Shea Levy [**(@shlevy)**](https://github.com/shlevy)

View File

@@ -358,7 +358,6 @@ dockerTools.buildLayeredImageWithNixDb {
extraCommands = ''
rm -rf nix-support
ln -s /nix/var/nix/profiles nix/var/nix/gcroots/profiles
'';
fakeRootCommands = ''
chmod 1777 tmp

8
flake.lock generated
View File

@@ -63,11 +63,11 @@
},
"nixpkgs": {
"locked": {
"lastModified": 1771043024,
"narHash": "sha256-WoiezqWJQ3OHILah+p6rzNXdJceEAmAhyDFZFZ6pZzY=",
"rev": "3aadb7ca9eac2891d52a9dec199d9580a6e2bf44",
"lastModified": 1771903837,
"narHash": "sha256-jEA8WggGKtMFeNeCKq3NK8cLEjJmG6/RLUElYYbBZ0E=",
"rev": "e764fc9a405871f1f6ca3d1394fb422e0a0c3951",
"type": "tarball",
"url": "https://releases.nixos.org/nixos/25.11/nixos-25.11.5960.3aadb7ca9eac/nixexprs.tar.xz"
"url": "https://releases.nixos.org/nixos/25.11/nixos-25.11.6495.e764fc9a4058/nixexprs.tar.xz"
},
"original": {
"type": "tarball",

View File

@@ -409,7 +409,9 @@
"nix-cmd" = { };
"nix-nswrapper" = { };
"nix-nswrapper" = {
linuxOnly = true;
};
"nix-cli" = { };
@@ -431,32 +433,37 @@
pkgName:
{
supportsCross ? true,
linuxOnly ? false,
}:
{
# These attributes go right into `packages.<system>`.
"${pkgName}" = nixpkgsFor.${system}.native.nixComponents2.${pkgName};
"${pkgName}-static" = nixpkgsFor.${system}.native.pkgsStatic.nixComponents2.${pkgName};
"${pkgName}-llvm" = nixpkgsFor.${system}.native.pkgsLLVM.nixComponents2.${pkgName};
}
lib.optionalAttrs (linuxOnly -> nixpkgsFor.${system}.native.stdenv.hostPlatform.isLinux) (
{
# These attributes go right into `packages.<system>`.
"${pkgName}" = nixpkgsFor.${system}.native.nixComponents2.${pkgName};
"${pkgName}-static" = nixpkgsFor.${system}.native.pkgsStatic.nixComponents2.${pkgName};
"${pkgName}-llvm" = nixpkgsFor.${system}.native.pkgsLLVM.nixComponents2.${pkgName};
}
// flatMapAttrs (lib.genAttrs stdenvs (_: { })) (
stdenvName:
{ }:
{
# These attributes go right into `packages.<system>`.
"${pkgName}-${stdenvName}" =
nixpkgsFor.${system}.nativeForStdenv.${stdenvName}.nixComponents2.${pkgName};
}
)
)
// lib.optionalAttrs supportsCross (
flatMapAttrs (lib.genAttrs crossSystems (_: { })) (
crossSystem:
{ }:
{
# These attributes go right into `packages.<system>`.
"${pkgName}-${crossSystem}" = nixpkgsFor.${system}.cross.${crossSystem}.nixComponents2.${pkgName};
}
lib.optionalAttrs
(linuxOnly -> nixpkgsFor.${system}.cross.${crossSystem}.stdenv.hostPlatform.isLinux)
{
# These attributes go right into `packages.<system>`.
"${pkgName}-${crossSystem}" = nixpkgsFor.${system}.cross.${crossSystem}.nixComponents2.${pkgName};
}
)
)
// flatMapAttrs (lib.genAttrs stdenvs (_: { })) (
stdenvName:
{ }:
{
# These attributes go right into `packages.<system>`.
"${pkgName}-${stdenvName}" =
nixpkgsFor.${system}.nativeForStdenv.${stdenvName}.nixComponents2.${pkgName};
}
)
)
// lib.optionalAttrs (builtins.elem system linux64BitSystems) {
dockerImage =

View File

@@ -95,6 +95,11 @@
''^tests/functional/lang/eval-fail-bad-string-interpolation-3\.nix$''
''^tests/functional/lang/eval-fail-bad-string-interpolation-4\.nix$''
''^tests/functional/lang/eval-okay-regex-match2\.nix$''
# URL literal tests - nixfmt converts unquoted URLs to strings
''^tests/functional/lang/eval-fail-url-literal\.nix$''
''^tests/functional/lang/eval-okay-url-literal-warn\.nix$''
''^tests/functional/lang/eval-okay-url-literal-default\.nix$''
];
};
clang-format = {

View File

@@ -1,6 +1,6 @@
{
runCommand,
system,
stdenv,
buildPackages,
cacert,
nix,
@@ -9,6 +9,8 @@
let
inherit (stdenv.hostPlatform) system;
installerClosureInfo = buildPackages.closureInfo {
rootPaths = [
nix

View File

@@ -30,9 +30,19 @@ scope: {
NIX_CFLAGS_COMPILE = "-DINITIAL_MARK_STACK_SIZE=1048576";
});
curl = pkgs.curl.override {
http3Support = !pkgs.stdenv.hostPlatform.isWindows;
};
curl =
(pkgs.curl.override {
http3Support = !pkgs.stdenv.hostPlatform.isWindows;
# Make sure we enable all the dependencies for Content-Encoding/Transfer-Encoding decompression.
zstdSupport = true;
brotliSupport = true;
zlibSupport = true;
}).overrideAttrs
{
# TODO: Fix in nixpkgs. Static build with brotli is marked as broken, but it's not the case.
# Remove once https://github.com/NixOS/nixpkgs/pull/494111 lands in the 25.11 channel.
meta.broken = false;
};
libblake3 = pkgs.libblake3.override {
useTBB = !(stdenv.hostPlatform.isWindows || stdenv.hostPlatform.isStatic);

View File

@@ -115,7 +115,11 @@ rec {
# Binary package for various platforms.
build = forAllPackages (
pkgName: forAllSystems (system: nixpkgsFor.${system}.native.nixComponents2.${pkgName})
pkgName:
lib.filterAttrs (
system: _do_not_touch:
pkgName == "nix-nswrapper" -> nixpkgsFor.${system}.native.stdenv.hostPlatform.isLinux
) (forAllSystems (system: nixpkgsFor.${system}.native.nixComponents2.${pkgName}))
);
shellInputs = removeAttrs (forAllSystems (
@@ -135,6 +139,10 @@ rec {
(
if pkgName == "nix-functional-tests" then
lib.flip builtins.removeAttrs [ "x86_64-w64-mingw32" ]
else if pkgName == "nix-nswrapper" then
lib.filterAttrs (
crossSystem: _do_not_touch: nixpkgsFor.x86_64-linux.cross.${crossSystem}.stdenv.hostPlatform.isLinux
)
else
lib.id
)
@@ -171,7 +179,13 @@ rec {
)
);
in
forAllPackages (pkgName: forAllSystems (system: components.${system}.${pkgName}));
forAllPackages (
pkgName:
lib.filterAttrs (
system: _do_not_touch:
pkgName == "nix-nswrapper" -> nixpkgsFor.${system}.native.stdenv.hostPlatform.isLinux
) (forAllSystems (system: components.${system}.${pkgName}))
);
buildNoTests = forAllSystems (system: nixpkgsFor.${system}.native.nixComponents2.nix-cli);
@@ -191,7 +205,13 @@ rec {
)
);
in
forAllPackages (pkgName: forAllSystems (system: components.${system}.${pkgName}));
forAllPackages (
pkgName:
lib.filterAttrs (
system: _do_not_touch:
pkgName == "nix-nswrapper" -> nixpkgsFor.${system}.native.stdenv.hostPlatform.isLinux
) (forAllSystems (system: components.${system}.${pkgName}))
);
# Perl bindings for various platforms.
perlBindings = forAllSystems (system: nixpkgsFor.${system}.native.nixComponents2.nix-perl-bindings);

View File

@@ -1 +1 @@
../../src/libstore-tests/data/build-result
../libstore-tests/data/build-result

View File

@@ -1 +1 @@
../../src/libstore-tests/data/realisation
../libstore-tests/data/realisation

View File

@@ -1 +1 @@
../../src/libstore-tests/data/content-address
../libstore-tests/data/content-address

View File

@@ -1 +1 @@
../../src/libstore-tests/data/derivation
../libstore-tests/data/derivation

View File

@@ -1 +1 @@
../../src/libstore-tests/data/derived-path
../libstore-tests/data/derived-path

View File

@@ -1 +1 @@
../../src/libutil-tests/data/memory-source-accessor
../libutil-tests/data/memory-source-accessor

View File

@@ -1 +1 @@
../../src/libutil-tests/data/hash
../libutil-tests/data/hash

View File

@@ -51,6 +51,13 @@ schemas = [
'simple.json',
],
},
{
'stem' : 'signature',
'schema' : schema_dir / 'signature-v2.yaml',
'files' : [
'simple.json',
],
},
{
'stem' : 'deriving-path',
'schema' : schema_dir / 'deriving-path-v1.yaml',
@@ -62,13 +69,10 @@ schemas = [
},
{
'stem' : 'build-trace-entry',
'schema' : schema_dir / 'build-trace-entry-v2.yaml',
'schema' : schema_dir / 'build-trace-entry-v3.yaml',
'files' : [
'simple.json',
# The field is no longer supported, but we want to show that we
# ignore it during parsing.
'with-dependent-realisations.json',
'with-signature.json',
'with-structured-signature.json',
],
},
{
@@ -154,20 +158,20 @@ schemas += [
# Match overall
{
'stem' : 'store-object-info',
'schema' : schema_dir / 'store-object-info-v2.yaml',
'schema' : schema_dir / 'store-object-info-v3.yaml',
'files' : [
'json-2' / 'pure.json',
'json-2' / 'impure.json',
'json-2' / 'empty_pure.json',
'json-2' / 'empty_impure.json',
'json-3' / 'pure.json',
'json-3' / 'impure.json',
'json-3' / 'empty_pure.json',
'json-3' / 'empty_impure.json',
],
},
{
'stem' : 'nar-info',
'schema' : schema_dir / 'store-object-info-v2.yaml',
'schema' : schema_dir / 'store-object-info-v3.yaml',
'files' : [
'json-2' / 'pure.json',
'json-2' / 'impure.json',
'json-3' / 'pure.json',
'json-3' / 'impure.json',
],
},
{
@@ -182,32 +186,32 @@ schemas += [
# Match exact variant
{
'stem' : 'store-object-info',
'schema' : schema_dir / 'store-object-info-v2.yaml#/$defs/base',
'schema' : schema_dir / 'store-object-info-v3.yaml#/$defs/base',
'files' : [
'json-2' / 'pure.json',
'json-2' / 'empty_pure.json',
'json-3' / 'pure.json',
'json-3' / 'empty_pure.json',
],
},
{
'stem' : 'store-object-info',
'schema' : schema_dir / 'store-object-info-v2.yaml#/$defs/impure',
'schema' : schema_dir / 'store-object-info-v3.yaml#/$defs/impure',
'files' : [
'json-2' / 'impure.json',
'json-2' / 'empty_impure.json',
'json-3' / 'impure.json',
'json-3' / 'empty_impure.json',
],
},
{
'stem' : 'nar-info',
'schema' : schema_dir / 'store-object-info-v2.yaml#/$defs/base',
'schema' : schema_dir / 'store-object-info-v3.yaml#/$defs/base',
'files' : [
'json-2' / 'pure.json',
'json-3' / 'pure.json',
],
},
{
'stem' : 'nar-info',
'schema' : schema_dir / 'store-object-info-v2.yaml#/$defs/narInfo',
'schema' : schema_dir / 'store-object-info-v3.yaml#/$defs/narInfo',
'files' : [
'json-2' / 'impure.json',
'json-3' / 'impure.json',
],
},
]

View File

@@ -1 +1 @@
../../src/libstore-tests/data/nar-info
../libstore-tests/data/nar-info

View File

@@ -0,0 +1,4 @@
{
"keyName": "cache.nixos.org-1",
"sig": "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
}

View File

@@ -1 +1 @@
../../src/libstore-tests/data/dummy-store
../libstore-tests/data/dummy-store

View File

@@ -1 +1 @@
../../src/libstore-tests/data/path-info
../libstore-tests/data/path-info

View File

@@ -1 +1 @@
../../src/libstore-tests/data/store-path
../libstore-tests/data/store-path

View File

@@ -108,20 +108,16 @@ RealisedPath::Set BuiltPath::toRealisedPaths(Store & store) const
overloaded{
[&](const BuiltPath::Opaque & p) { res.insert(p.path); },
[&](const BuiltPath::Built & p) {
auto drvHashes = staticOutputHashes(store, store.readDerivation(p.drvPath->outPath()));
for (auto & [outputName, outputPath] : p.outputs) {
if (experimentalFeatureSettings.isEnabled(Xp::CaDerivations)) {
auto drvOutput = get(drvHashes, outputName);
if (!drvOutput)
throw Error(
"the derivation '%s' has unrealised output '%s' (derived-path.cc/toRealisedPaths)",
store.printStorePath(p.drvPath->outPath()),
outputName);
DrvOutput key{*drvOutput, outputName};
DrvOutput key{
.drvPath = p.drvPath->outPath(),
.outputName = outputName,
};
auto thisRealisation = store.queryRealisation(key);
assert(thisRealisation); // Weve built it, so we must
// have the realisation
res.insert(Realisation{*thisRealisation, std::move(key)});
// Weve built it, so we must have the realisation.
assert(thisRealisation);
res.insert(Realisation{*thisRealisation, key});
} else {
res.insert(outputPath);
}

View File

@@ -300,11 +300,11 @@ struct StorePathCommand : public StorePathsCommand
*/
struct RegisterCommand
{
typedef std::map<std::vector<std::string>, std::function<ref<Command>()>> Commands;
typedef std::map<std::vector<std::string>, fun<ref<Command>()>> Commands;
static Commands & commands();
RegisterCommand(std::vector<std::string> && name, std::function<ref<Command>()> command)
RegisterCommand(std::vector<std::string> && name, fun<ref<Command>()> command)
{
commands().emplace(name, command);
}
@@ -407,7 +407,7 @@ void createOutLinks(const std::filesystem::path & outLink, const BuiltPaths & bu
struct MixOutLinkBase : virtual Args
{
/** Prefix for any output symlinks. Empty means do not write an output symlink. */
Path outLink;
std::filesystem::path outLink;
MixOutLinkBase(const std::string & defaultOutLink)
: outLink(defaultOutLink)
@@ -435,7 +435,7 @@ struct MixOutLinkByDefault : MixOutLinkBase, virtual Args
addFlag({
.longName = "no-link",
.description = "Do not create symlinks to the build results.",
.handler = {&outLink, Path("")},
.handler = {&outLink, std::filesystem::path{}},
});
}
};

View File

@@ -1,13 +1,14 @@
#pragma once
///@file
#include <functional>
#include "nix/util/fun.hh"
#include <map>
#include <string>
namespace nix {
typedef std::function<void(int, char **)> MainFunction;
typedef fun<void(int, char **)> MainFunction;
struct RegisterLegacyCommand
{
@@ -15,9 +16,9 @@ struct RegisterLegacyCommand
static Commands & commands();
RegisterLegacyCommand(const std::string & name, MainFunction fun)
RegisterLegacyCommand(const std::string & name, MainFunction command)
{
commands()[name] = fun;
commands().insert_or_assign(name, command);
}
};

View File

@@ -2,7 +2,9 @@
/// @file
#include "nix/util/finally.hh"
#include "nix/util/fun.hh"
#include "nix/util/types.hh"
#include <filesystem>
#include <functional>
#include <string>
@@ -26,7 +28,7 @@ enum class ReplPromptType {
class ReplInteracter
{
public:
using Guard = Finally<std::function<void()>>;
using Guard = Finally<fun<void()>>;
virtual Guard init(detail::ReplCompleterMixin * repl) = 0;
/** Returns a boolean of whether the interacter got EOF */
@@ -36,10 +38,10 @@ public:
class ReadlineLikeInteracter : public virtual ReplInteracter
{
std::string historyFile;
std::filesystem::path historyFile;
public:
ReadlineLikeInteracter(std::string historyFile)
: historyFile(historyFile)
ReadlineLikeInteracter(std::filesystem::path historyFile)
: historyFile(std::move(historyFile))
{
}

View File

@@ -2,6 +2,7 @@
///@file
#include "nix/expr/eval.hh"
#include "nix/util/os-string.hh"
namespace nix {
@@ -27,8 +28,7 @@ struct AbstractNixRepl
* @param programName Name of the command, e.g. `nix` or `nix-env`.
* @param args arguments to the command.
*/
using RunNix =
void(const std::string & programName, const Strings & args, const std::optional<std::string> & input);
using RunNix = void(const std::string & programName, OsStrings args, const std::optional<std::string> & input);
/**
* @param runNix Function to run the nix CLI to support various
@@ -38,7 +38,7 @@ struct AbstractNixRepl
static std::unique_ptr<AbstractNixRepl> create(
const LookupPath & lookupPath,
ref<EvalState> state,
std::function<AnnotatedValues()> getValues,
fun<AnnotatedValues()> getValues,
RunNix * runNix = nullptr);
static ReplExitStatus runSimple(ref<EvalState> evalState, const ValMap & extraEnv);

View File

@@ -35,7 +35,7 @@ PeerInfo getPeerInfo(Descriptor remote);
* @param closeListeners A callback to close the listening sockets.
* Useful in forked child processes to release the bound sockets.
*/
using UnixSocketHandler = std::function<void(AutoCloseFD socket, std::function<void()> closeListeners)>;
using UnixSocketHandler = fun<void(AutoCloseFD socket, std::function<void()> closeListeners)>;
/**
* Options for the serve loop.
@@ -53,6 +53,19 @@ struct ServeUnixSocketOptions
* Mode for the created socket file.
*/
mode_t socketMode = 0666;
#ifndef _WIN32
/**
* Additional file descriptor to poll. Useful for doing a self-pipe trick
* https://cr.yp.to/docs/selfpipe.html.
*/
Descriptor auxiliaryFd = INVALID_DESCRIPTOR;
/**
* Optional callback invoked on POLLIN event for auxiliaryFd.
*/
std::function<void()> onAuxiliaryFdPollin = nullptr;
#endif
};
/**

View File

@@ -157,7 +157,7 @@ MixFlakeOptions::MixFlakeOptions()
.category = category,
.labels = {"flake-lock-path"},
.handler = {[&](std::string lockFilePath) {
lockFlags.referenceLockFilePath = {getFSSourceAccessor(), CanonPath(absPath(lockFilePath))};
lockFlags.referenceLockFilePath = {getFSSourceAccessor(), CanonPath(absPath(lockFilePath).string())};
}},
.completer = completePath,
});
@@ -400,7 +400,7 @@ void completeFlakeRefWithFragment(
}
}
} catch (Error & e) {
warn(e.msg());
logWarning(e.info());
}
}

View File

@@ -118,14 +118,14 @@ ReadlineLikeInteracter::Guard ReadlineLikeInteracter::init(detail::ReplCompleter
// Allow nix-repl specific settings in .inputrc
rl_readline_name = "nix-repl";
try {
createDirs(dirOf(historyFile));
createDirs(historyFile.parent_path());
} catch (SystemError & e) {
logWarning(e.info());
}
#if !USE_READLINE
el_hist_size = 1000;
#endif
read_history(historyFile.c_str());
read_history(historyFile.string().c_str());
auto oldRepl = curRepl;
curRepl = repl;
Guard restoreRepl([oldRepl] { curRepl = oldRepl; });
@@ -208,7 +208,7 @@ bool ReadlineLikeInteracter::getLine(std::string & input, ReplPromptType promptT
ReadlineLikeInteracter::~ReadlineLikeInteracter()
{
write_history(historyFile.c_str());
write_history(historyFile.string().c_str());
}
}; // namespace nix

View File

@@ -1,6 +1,7 @@
#include <iostream>
#include <cstdlib>
#include <cstring>
#include <memory>
#include "nix/util/error.hh"
#include "nix/cmd/repl-interacter.hh"
@@ -28,6 +29,8 @@
#include "nix/util/ref.hh"
#include "nix/expr/value.hh"
#include "nix/util/os-string.hh"
#include "nix/util/processes.hh"
#include "nix/util/strings.hh"
namespace nix {
@@ -60,7 +63,7 @@ struct NixRepl : AbstractNixRepl, detail::ReplCompleterMixin, gc
std::list<std::filesystem::path> loadedFiles;
// Arguments passed to :load-flake, saved so they can be reloaded with :reload
Strings loadedFlakes;
std::function<AnnotatedValues()> getValues;
fun<AnnotatedValues()> getValues;
const static int envSize = 32768;
std::shared_ptr<StaticEnv> staticEnv;
@@ -71,15 +74,11 @@ struct NixRepl : AbstractNixRepl, detail::ReplCompleterMixin, gc
RunNix * runNixPtr;
void runNix(const std::string & program, const Strings & args, const std::optional<std::string> & input = {});
void runNix(const std::string & program, OsStrings args, const std::optional<std::string> & input = {});
std::unique_ptr<ReplInteracter> interacter;
NixRepl(
const LookupPath & lookupPath,
ref<EvalState> state,
std::function<AnnotatedValues()> getValues,
RunNix * runNix);
NixRepl(const LookupPath & lookupPath, ref<EvalState> state, fun<AnnotatedValues()> getValues, RunNix * runNix);
virtual ~NixRepl() = default;
ReplExitStatus mainLoop() override;
@@ -98,6 +97,7 @@ struct NixRepl : AbstractNixRepl, detail::ReplCompleterMixin, gc
void addAttrsToScope(Value & attrs);
void addVarToScope(const Symbol name, Value & v);
Expr * parseString(std::string s);
ExprAttrs * parseReplBindings(std::string s);
void evalString(std::string s, Value & v);
void loadDebugTraceEnv(DebugTrace & dt);
@@ -130,16 +130,13 @@ std::string removeWhitespace(std::string s)
}
NixRepl::NixRepl(
const LookupPath & lookupPath,
ref<EvalState> state,
std::function<NixRepl::AnnotatedValues()> getValues,
RunNix * runNix)
const LookupPath & lookupPath, ref<EvalState> state, fun<NixRepl::AnnotatedValues()> getValues, RunNix * runNix)
: AbstractNixRepl(state)
, debugTraceIndex(0)
, getValues(getValues)
, staticEnv(new StaticEnv(nullptr, state->staticBaseEnv))
, runNixPtr{runNix}
, interacter(make_unique<ReadlineLikeInteracter>((getDataDir() / "repl-history").string()))
, interacter(std::make_unique<ReadlineLikeInteracter>(getDataDir() / "repl-history"))
{
}
@@ -306,21 +303,6 @@ StringSet NixRepl::completePrefix(const std::string & prefix)
return completions;
}
// FIXME: DRY and match or use the parser
static bool isVarName(std::string_view s)
{
if (s.size() == 0)
return false;
char c = s[0];
if ((c >= '0' && c <= '9') || c == '-' || c == '\'')
return false;
for (auto & i : s)
if (!((i >= 'a' && i <= 'z') || (i >= 'A' && i <= 'Z') || (i >= '0' && i <= '9') || i == '_' || i == '-'
|| i == '\''))
return false;
return true;
}
StorePath NixRepl::getDerivationPath(Value & v)
{
auto packageInfo = getDerivation(*state, v, false);
@@ -508,7 +490,12 @@ ProcessLineResult NixRepl::processLine(std::string line)
// runProgram redirects stdout to a StringSink,
// using runProgram2 to allow editors to display their UI
runProgram2(RunOptions{.program = editor, .lookupPath = true, .args = args, .isInteractive = true});
runProgram2({
.program = editor,
.lookupPath = true,
.args = toOsStrings(std::move(args)),
.isInteractive = true,
});
// Reload right after exiting the editor
state->resetFileCache();
@@ -528,7 +515,7 @@ ProcessLineResult NixRepl::processLine(std::string line)
state->callFunction(f, v, result, PosIdx());
StorePath drvPath = getDerivationPath(result);
runNix("nix-shell", {state->store->printStorePath(drvPath)});
runNix("nix-shell", toOsStrings({state->store->printStorePath(drvPath)}));
}
else if (command == ":b" || command == ":bl" || command == ":i" || command == ":sh" || command == ":log") {
@@ -559,7 +546,7 @@ ProcessLineResult NixRepl::processLine(std::string line)
}
}
} else if (command == ":i") {
runNix("nix-env", {"-i", drvPathRaw});
runNix("nix-env", toOsStrings({"-i", drvPathRaw}));
} else if (command == ":log") {
settings.readOnlyMode = true;
Finally roModeReset([&]() { settings.readOnlyMode = false; });
@@ -567,7 +554,7 @@ ProcessLineResult NixRepl::processLine(std::string line)
auto log = fetchBuildLog(state->store, drvPath, drvPathRaw);
logger->writeToStdout(log);
} else {
runNix("nix-shell", {drvPathRaw});
runNix("nix-shell", toOsStrings({drvPathRaw}));
}
}
@@ -668,15 +655,22 @@ ProcessLineResult NixRepl::processLine(std::string line)
throw Error("unknown command '%1%'", command);
else {
size_t p = line.find('=');
std::string name;
if (p != std::string::npos && p < line.size() && line[p + 1] != '='
&& isVarName(name = removeWhitespace(line.substr(0, p)))) {
Expr * e = parseString(line.substr(p + 1));
Value & v(*state->allocValue());
v.mkThunk(env, e);
addVarToScope(state->symbols.create(name), v);
// Try parsing as bindings first (handles `x = 1`, `inherit ...`, etc.)
ExprAttrs * bindings = nullptr;
try {
bindings = parseReplBindings(line);
} catch (ParseError &) {
}
if (bindings) {
Env * inheritEnv = bindings->inheritFromExprs ? bindings->buildInheritFromEnv(*state, *env) : nullptr;
for (auto & [symbol, def] : *bindings->attrs) {
Value & v(*state->allocValue());
v.mkThunk(def.chooseByKind(env, env, inheritEnv), def.e);
addVarToScope(symbol, v);
}
} else {
// Otherwise evaluate as expression
Value v;
evalString(line, v);
auto suspension = logger->suspend();
@@ -865,6 +859,28 @@ Expr * NixRepl::parseString(std::string s)
}
}
ExprAttrs * NixRepl::parseReplBindings(std::string s)
{
auto basePath = state->rootPath(".");
// Try parsing as bindings
std::exception_ptr bindingsError;
try {
return state->parseReplBindings(s, basePath, staticEnv);
} catch (ParseError &) {
bindingsError = std::current_exception();
}
// Try with semicolon appended (for `inherit foo` shorthand)
// Use original source (s) for error messages, not s + ";"
try {
return state->parseReplBindings(s + ";", s, basePath, staticEnv);
} catch (ParseError &) {
// Semicolon retry failed; rethrow the original bindings error
std::rethrow_exception(bindingsError);
}
}
void NixRepl::evalString(std::string s, Value & v)
{
Expr * e = parseString(s);
@@ -872,10 +888,10 @@ void NixRepl::evalString(std::string s, Value & v)
state->forceValue(v, v.determinePos(noPos));
}
void NixRepl::runNix(const std::string & program, const Strings & args, const std::optional<std::string> & input)
void NixRepl::runNix(const std::string & program, OsStrings args, const std::optional<std::string> & input)
{
if (runNixPtr)
(*runNixPtr)(program, args, input);
(*runNixPtr)(program, std::move(args), input);
else
throw Error(
"Cannot run '%s' because no method of calling the Nix CLI was provided. This is a configuration problem pertaining to how this program was built. See Nix 2.25 release notes",
@@ -883,7 +899,7 @@ void NixRepl::runNix(const std::string & program, const Strings & args, const st
}
std::unique_ptr<AbstractNixRepl> AbstractNixRepl::create(
const LookupPath & lookupPath, ref<EvalState> state, std::function<AnnotatedValues()> getValues, RunNix * runNix)
const LookupPath & lookupPath, ref<EvalState> state, fun<AnnotatedValues()> getValues, RunNix * runNix)
{
return std::make_unique<NixRepl>(lookupPath, state, getValues, runNix);
}

View File

@@ -9,6 +9,8 @@
#include "nix/util/unix-domain-socket.hh"
#include "nix/util/util.hh"
#include <ranges>
#include <sys/socket.h>
#include <sys/un.h>
#include <poll.h>
@@ -83,6 +85,9 @@ PeerInfo getPeerInfo(Descriptor remote)
for (auto & i : listeningSockets)
fds.push_back({.fd = i.get(), .events = POLLIN});
if (options.auxiliaryFd != INVALID_DESCRIPTOR)
fds.push_back({.fd = options.auxiliaryFd, .events = POLLIN});
// Loop accepting connections.
while (1) {
try {
@@ -95,7 +100,11 @@ PeerInfo getPeerInfo(Descriptor remote)
throw SysError("polling for incoming connections");
}
for (auto & fd : fds) {
if (options.auxiliaryFd != INVALID_DESCRIPTOR && options.onAuxiliaryFdPollin && fds.back().revents & POLLIN)
/* Useful for reaping children. */
options.onAuxiliaryFdPollin();
for (auto & fd : std::views::take(fds, listeningSockets.size())) {
if (!fd.revents)
continue;

View File

@@ -181,13 +181,13 @@ EvalState * nix_eval_state_build(nix_c_context * context, nix_eval_state_builder
if (context)
context->last_err_code = NIX_OK;
try {
return unsafe_new_with_self<EvalState>([&](auto * self) {
return EvalState{
.fetchSettings = std::move(builder->fetchSettings),
.settings = std::move(builder->settings),
.state = nix::EvalState(builder->lookupPath, builder->store, self->fetchSettings, self->settings),
};
});
auto fetchSettings = std::make_unique<nix::fetchers::Settings>(std::move(builder->fetchSettings));
auto settings = std::make_unique<nix::EvalSettings>(std::move(builder->settings));
auto ownedState =
std::make_shared<nix::EvalState>(builder->lookupPath, builder->store, *fetchSettings, *settings);
auto & stateRef = *ownedState;
void * p = ::operator new(sizeof(EvalState), static_cast<std::align_val_t>(alignof(EvalState)));
return new (p) EvalState{stateRef, std::move(fetchSettings), std::move(settings), std::move(ownedState)};
}
NIXC_CATCH_ERRS_NULL
}

View File

@@ -1,6 +1,8 @@
#ifndef NIX_API_EXPR_INTERNAL_H
#define NIX_API_EXPR_INTERNAL_H
#include <memory>
#include "nix/fetchers/fetch-settings.hh"
#include "nix/expr/eval.hh"
#include "nix/expr/eval-settings.hh"
@@ -22,9 +24,11 @@ struct nix_eval_state_builder
struct EvalState
{
nix::fetchers::Settings fetchSettings;
nix::EvalSettings settings;
nix::EvalState state;
nix::EvalState & state;
// Owned resources; null for temporary wrappers created in C API callbacks.
std::unique_ptr<nix::fetchers::Settings> ownedFetchSettings;
std::unique_ptr<nix::EvalSettings> ownedSettings;
std::shared_ptr<nix::EvalState> ownedState;
};
struct BindingsBuilder

View File

@@ -137,7 +137,8 @@ public:
}
nix_string_context ctx{context};
nix_string_return res{""};
desc.printValueAsJSON(v, (EvalState *) &state, strict, &ctx, copyToStore, &res);
EvalState wrapper{state};
desc.printValueAsJSON(v, &wrapper, strict, &ctx, copyToStore, &res);
if (res.str.empty()) {
return nix::ExternalValueBase::printValueAsJSON(state, strict, context, copyToStore);
}
@@ -153,22 +154,16 @@ public:
bool location,
nix::XMLWriter & doc,
nix::NixStringContext & context,
nix::PathSet & drvsSeen,
nix::StringSet & drvsSeen,
const nix::PosIdx pos) const override
{
if (!desc.printValueAsXML) {
return nix::ExternalValueBase::printValueAsXML(state, strict, location, doc, context, drvsSeen, pos);
}
nix_string_context ctx{context};
EvalState wrapper{state};
desc.printValueAsXML(
v,
(EvalState *) &state,
strict,
location,
&doc,
&ctx,
&drvsSeen,
*reinterpret_cast<const uint32_t *>(&pos));
v, &wrapper, strict, location, &doc, &ctx, &drvsSeen, *reinterpret_cast<const uint32_t *>(&pos));
}
virtual ~NixCExternalValue() override {};

View File

@@ -145,6 +145,7 @@ typedef struct NixCExternalValueDesc
* Optional, the default is to throw an error
* @todo The mechanisms for this call are incomplete. There are no C
* bindings to work with XML, pathsets and positions.
* This callback also has no test coverage.
* @param[in] self the void* passed to nix_create_external_value
* @param[in] state The evaluator state
* @param[in] strict boolean Whether to force the value before printing

View File

@@ -1,4 +1,5 @@
#include "nix/expr/attr-set.hh"
#include "nix/expr/eval-error.hh"
#include "nix/util/configuration.hh"
#include "nix/expr/eval.hh"
#include "nix/store/globals.hh"
@@ -104,11 +105,17 @@ static void nix_c_primop_wrapper(
nix_value * external_arg = new_nix_value(args[i], state.mem);
external_args.push_back(external_arg);
}
f(userdata, &ctx, (EvalState *) &state, external_args.data(), vTmpPtr);
EvalState wrapper{state};
f(userdata, &ctx, &wrapper, external_args.data(), vTmpPtr);
if (ctx.last_err_code != NIX_OK) {
/* TODO: Throw different errors depending on the error code */
state.error<nix::EvalError>("Error from custom function: %s", *ctx.last_err).atPos(pos).debugThrow();
if (ctx.last_err_code == NIX_ERR_RECOVERABLE) {
state.error<nix::RecoverableEvalError>("Recoverable error from custom function: %s", *ctx.last_err)
.atPos(pos)
.debugThrow();
} else {
state.error<nix::EvalError>("Error from custom function: %s", *ctx.last_err).atPos(pos).debugThrow();
}
}
if (!vTmp.isValid()) {
@@ -153,7 +160,7 @@ PrimOp * nix_alloc_primop(
.args = {},
.arity = (size_t) arity,
.doc = doc,
.fun = std::bind(nix_c_primop_wrapper, fun, user_data, arity, _1, _2, _3, _4)};
.impl = std::bind(nix_c_primop_wrapper, fun, user_data, arity, _1, _2, _3, _4)};
if (args)
for (size_t i = 0; args[i]; i++)
p->args.emplace_back(*args);
@@ -194,6 +201,8 @@ ValueType nix_get_type(nix_c_context * context, const nix_value * value)
switch (v.type()) {
case nThunk:
return NIX_TYPE_THUNK;
case nFailed:
return NIX_TYPE_FAILED;
case nInt:
return NIX_TYPE_INT;
case nFloat:

View File

@@ -100,7 +100,8 @@ typedef enum {
/** @brief External value from C++ plugins or C API
* @see Externals
*/
NIX_TYPE_EXTERNAL
NIX_TYPE_EXTERNAL,
NIX_TYPE_FAILED,
} ValueType;
// forward declarations

View File

@@ -12,21 +12,22 @@ class nix_api_expr_test : public nix_api_store_test
{
protected:
nix_api_expr_test()
void SetUp() override
{
nix_api_store_test::SetUp();
nix_libexpr_init(ctx);
state = nix_state_create(nullptr, nullptr, store);
value = nix_alloc_value(nullptr, state);
}
~nix_api_expr_test()
void TearDown() override
{
nix_gc_decref(nullptr, value);
nix_state_free(state);
}
EvalState * state;
nix_value * value;
EvalState * state = nullptr;
nix_value * value = nullptr;
};
} // namespace nixC

View File

@@ -37,7 +37,8 @@ static void BM_EvalDynamicAttrs(benchmark::State & state)
EvalSettings evalSettings{readOnlyMode};
evalSettings.nixPath = {};
EvalState st({}, store, fetchSettings, evalSettings, nullptr);
auto stPtr = std::make_shared<EvalState>(LookupPath{}, store, fetchSettings, evalSettings, nullptr);
auto & st = *stPtr;
Expr * expr = st.parseExprFromString(exprStr, st.rootPath(CanonPath::root));
Value v;

View File

@@ -16,7 +16,8 @@ struct GetDerivationsEnv
fetchers::Settings fetchSettings{};
bool readOnlyMode = true;
EvalSettings evalSettings{readOnlyMode};
EvalState state;
std::shared_ptr<EvalState> statePtr;
EvalState & state;
Bindings * autoArgs = nullptr;
Value attrsValue;
@@ -27,7 +28,8 @@ struct GetDerivationsEnv
settings.nixPath = {};
return settings;
}())
, state({}, store, fetchSettings, evalSettings, nullptr)
, statePtr(std::make_shared<EvalState>(LookupPath{}, store, fetchSettings, evalSettings, nullptr))
, state(*statePtr)
{
autoArgs = state.buildBindings(0).finish();

View File

@@ -84,6 +84,8 @@ test(
this_exe,
env : {
'_NIX_TEST_UNIT_DATA' : meson.current_source_dir() / 'data',
'HOME' : meson.current_build_dir() / 'test-home',
'NIX_STORE' : '',
},
protocol : 'gtest',
)

View File

@@ -476,6 +476,52 @@ TEST_F(nix_api_expr_test, nix_expr_primop_nix_err_key_conversion)
nix_gc_decref(ctx, result);
}
static void
primop_alloc_value(void * user_data, nix_c_context * context, EvalState * state, nix_value ** args, nix_value * ret)
{
assert(context);
assert(state);
// Regression test: nix_c_primop_wrapper previously cast the inner
// nix::EvalState* directly to EvalState* (C wrapper). C API functions
// like nix_alloc_value() then accessed state->state at the wrong offset,
// causing a segfault.
nix_value * v = nix_alloc_value(context, state);
assert(v != nullptr);
nix_init_int(context, v, 42);
nix_copy_value(context, ret, v);
nix_gc_decref(nullptr, v);
}
TEST_F(nix_api_expr_test, nix_primop_can_use_state_in_callback)
{
PrimOp * primop =
nix_alloc_primop(ctx, primop_alloc_value, 1, "allocValue", nullptr, "test alloc_value in callback", nullptr);
assert_ctx_ok();
nix_value * primopValue = nix_alloc_value(ctx, state);
assert_ctx_ok();
nix_init_primop(ctx, primopValue, primop);
assert_ctx_ok();
nix_value * dummy = nix_alloc_value(ctx, state);
assert_ctx_ok();
nix_init_int(ctx, dummy, 0);
assert_ctx_ok();
nix_value * result = nix_alloc_value(ctx, state);
assert_ctx_ok();
nix_value_call(ctx, state, primopValue, dummy, result);
assert_ctx_ok();
auto r = nix_get_int(ctx, result);
ASSERT_EQ(42, r);
nix_gc_decref(ctx, dummy);
nix_gc_decref(ctx, result);
nix_gc_decref(ctx, primopValue);
nix_gc_decref(ctx, primop);
}
TEST_F(nix_api_expr_test, nix_value_call_multi_no_args)
{
nix_value * n = nix_alloc_value(ctx, state);
@@ -517,4 +563,106 @@ TEST_F(nix_api_expr_test, nix_expr_attrset_update)
assert_ctx_ok();
}
// The following is a test case for retryable thunks. This is a requirement
// for the current way in which NixOps4 evaluates its deployment expressions.
// An alternative strategy could be implemented, but unwinding the stack may
// be a more efficient way to deal with many suspensions/resumptions, compared
// to e.g. using a thread or coroutine stack for each suspended dependency.
// This test models the essential bits of a deployment tool that uses such
// a strategy.
// State for the retryable primop - simulates deployment resource availability
struct DeploymentResourceState
{
bool vm_created = false;
};
static void primop_load_resource_input(
void * user_data, nix_c_context * context, EvalState * state, nix_value ** args, nix_value * ret)
{
assert(context);
assert(state);
auto * resource_state = static_cast<DeploymentResourceState *>(user_data);
// Get the resource input name argument
std::string input_name;
if (nix_get_string(context, args[0], OBSERVE_STRING(input_name)) != NIX_OK)
return;
// Only handle "vm_id" input - throw for anything else
if (input_name != "vm_id") {
std::string error_msg = "unknown resource input: " + input_name;
nix_set_err_msg(context, NIX_ERR_NIX_ERROR, error_msg.c_str());
return;
}
if (resource_state->vm_created) {
// VM has been created, return the ID
nix_init_string(context, ret, "vm-12345");
} else {
// VM not created yet, fail with dependency error
nix_set_err_msg(context, NIX_ERR_RECOVERABLE, "VM not yet created");
}
}
TEST_F(nix_api_expr_test, nix_expr_thunk_re_evaluation_after_deployment)
{
// This test demonstrates NixOps4's requirement: a thunk calling a primop should be
// re-evaluable when deployment resources become available that were not available initially.
DeploymentResourceState resource_state;
PrimOp * primop = nix_alloc_primop(
ctx,
primop_load_resource_input,
1,
"loadResourceInput",
nullptr,
"load a deployment resource input",
&resource_state);
assert_ctx_ok();
nix_value * primopValue = nix_alloc_value(ctx, state);
assert_ctx_ok();
nix_init_primop(ctx, primopValue, primop);
assert_ctx_ok();
nix_value * inputName = nix_alloc_value(ctx, state);
assert_ctx_ok();
nix_init_string(ctx, inputName, "vm_id");
assert_ctx_ok();
// Create a single thunk by using nix_init_apply instead of nix_value_call
// This creates a lazy application that can be forced multiple times
nix_value * thunk = nix_alloc_value(ctx, state);
assert_ctx_ok();
nix_init_apply(ctx, thunk, primopValue, inputName);
assert_ctx_ok();
// First force: VM not created yet, should fail
nix_value_force(ctx, state, thunk);
ASSERT_EQ(NIX_ERR_NIX_ERROR, nix_err_code(ctx));
ASSERT_THAT(nix_err_msg(nullptr, ctx, nullptr), testing::HasSubstr("VM not yet created"));
// Clear the error context for the next attempt
nix_c_context_free(ctx);
ctx = nix_c_context_create();
// Simulate deployment process: VM gets created
resource_state.vm_created = true;
// Second force of the SAME thunk: this is where the "failed" value issue appears
// With failed value caching, this should fail because the thunk is marked as permanently failed
// Without failed value caching (or with retryable failures), this should succeed
nix_value_force(ctx, state, thunk);
// If we get here without error, the thunk was successfully re-evaluated
assert_ctx_ok();
std::string result;
nix_get_string(ctx, thunk, OBSERVE_STRING(result));
assert_ctx_ok();
ASSERT_STREQ("vm-12345", result.c_str());
}
} // namespace nixC

View File

@@ -66,4 +66,44 @@ TEST_F(nix_api_expr_test, nix_expr_eval_external)
nix_state_free(stateFn);
}
static void print_value_as_json_using_state(
void * self, EvalState * state, bool strict, nix_string_context * c, bool copyToStore, nix_string_return * res)
{
// Regression test: same cast bug as in nix_c_primop_wrapper (see primop_alloc_value).
nix_value * v = nix_alloc_value(nullptr, state);
assert(v != nullptr);
nix_gc_decref(nullptr, v);
nix_set_string_return(res, "42");
}
TEST_F(nix_api_expr_test, nix_external_printValueAsJSON_can_use_state)
{
NixCExternalValueDesc desc{};
desc.print = [](void *, nix_printer *) {};
desc.showType = [](void *, nix_string_return *) {};
desc.typeOf = [](void *, nix_string_return *) {};
desc.printValueAsJSON = print_value_as_json_using_state;
ExternalValue * val = nix_create_external_value(ctx, &desc, nullptr);
assert_ctx_ok();
nix_init_external(ctx, value, val);
assert_ctx_ok();
nix_value * toJsonFn = nix_alloc_value(ctx, state);
nix_expr_eval_from_string(ctx, state, "builtins.toJSON", ".", toJsonFn);
assert_ctx_ok();
nix_value * result = nix_alloc_value(ctx, state);
nix_value_call(ctx, state, toJsonFn, value, result);
assert_ctx_ok();
std::string json_str;
nix_get_string(ctx, result, OBSERVE_STRING(json_str));
ASSERT_EQ("42", json_str);
nix_gc_decref(ctx, result);
nix_gc_decref(ctx, toJsonFn);
}
} // namespace nixC

View File

@@ -27,7 +27,8 @@ static void BM_EvalManyBuiltinsMatchSameRegex(benchmark::State & state)
EvalSettings evalSettings{readOnlyMode};
evalSettings.nixPath = {};
EvalState st({}, store, fetchSettings, evalSettings, nullptr);
auto stPtr = std::make_shared<EvalState>(LookupPath{}, store, fetchSettings, evalSettings, nullptr);
auto & st = *stPtr;
Expr * expr = st.parseExprFromString(std::string(exprStr), st.rootPath(CanonPath::root));
Value v;

View File

@@ -127,7 +127,7 @@ TEST_F(ValuePrintingTests, vLambda)
TEST_F(ValuePrintingTests, vPrimOp)
{
Value vPrimOp;
PrimOp primOp{.name = "puppy"};
PrimOp primOp{.name = "puppy", .impl = [](EvalState &, const PosIdx, Value **, Value &) {}};
vPrimOp.mkPrimOp(&primOp);
test(vPrimOp, "«primop puppy»");
@@ -135,7 +135,7 @@ TEST_F(ValuePrintingTests, vPrimOp)
TEST_F(ValuePrintingTests, vPrimOpApp)
{
PrimOp primOp{.name = "puppy"};
PrimOp primOp{.name = "puppy", .impl = [](EvalState &, const PosIdx, Value **, Value &) {}};
Value vPrimOp;
vPrimOp.mkPrimOp(&primOp);
@@ -188,6 +188,22 @@ TEST_F(ValuePrintingTests, vBlackhole)
test(vBlackhole, "«potential infinite recursion»");
}
TEST_F(ValuePrintingTests, vFailed)
{
Value v;
try {
throw Error("nope");
} catch (...) {
v.mkFailed(std::current_exception(), nullptr);
}
// Historically, a tried and then ignored value (e.g. through tryEval) was
// reverted to the original thunk.
test(v, "«thunk»");
test(v, ANSI_MAGENTA "«thunk»" ANSI_NORMAL, PrintOptions{.ansiColors = true});
}
TEST_F(ValuePrintingTests, depthAttrs)
{
Value vOne;
@@ -515,7 +531,7 @@ TEST_F(ValuePrintingTests, ansiColorsLambda)
TEST_F(ValuePrintingTests, ansiColorsPrimOp)
{
PrimOp primOp{.name = "puppy"};
PrimOp primOp{.name = "puppy", .impl = [](EvalState &, const PosIdx, Value **, Value &) {}};
Value v;
v.mkPrimOp(&primOp);
@@ -524,7 +540,7 @@ TEST_F(ValuePrintingTests, ansiColorsPrimOp)
TEST_F(ValuePrintingTests, ansiColorsPrimOpApp)
{
PrimOp primOp{.name = "puppy"};
PrimOp primOp{.name = "puppy", .impl = [](EvalState &, const PosIdx, Value **, Value &) {}};
Value vPrimOp;
vPrimOp.mkPrimOp(&primOp);

55
src/libexpr/diagnose.cc Normal file
View File

@@ -0,0 +1,55 @@
#include "nix/expr/diagnose.hh"
#include "nix/util/configuration.hh"
#include "nix/util/config-impl.hh"
#include "nix/util/abstract-setting-to-json.hh"
#include <nlohmann/json.hpp>
namespace nix {
template<>
Diagnose BaseSetting<Diagnose>::parse(const std::string & str) const
{
if (str == "ignore")
return Diagnose::Ignore;
else if (str == "warn")
return Diagnose::Warn;
else if (str == "fatal")
return Diagnose::Fatal;
else
throw UsageError("option '%s' has invalid value '%s' (expected 'ignore', 'warn', or 'fatal')", name, str);
}
template<>
struct BaseSetting<Diagnose>::trait
{
static constexpr bool appendable = false;
};
template<>
std::string BaseSetting<Diagnose>::to_string() const
{
switch (value) {
case Diagnose::Ignore:
return "ignore";
case Diagnose::Warn:
return "warn";
case Diagnose::Fatal:
return "fatal";
default:
unreachable();
}
}
NLOHMANN_JSON_SERIALIZE_ENUM(
Diagnose,
{
{Diagnose::Ignore, "ignore"},
{Diagnose::Warn, "warn"},
{Diagnose::Fatal, "fatal"},
});
/* Explicit instantiation of templates */
template class BaseSetting<Diagnose>;
} // namespace nix

View File

@@ -11,7 +11,7 @@
namespace nix::eval_cache {
CachedEvalError::CachedEvalError(ref<AttrCursor> cursor, Symbol attr)
: EvalError(cursor->root->state, "cached failure of attribute '%s'", cursor->getAttrPathStr(attr))
: CloneableError(cursor->root->state, "cached failure of attribute '%s'", cursor->getAttrPathStr(attr))
, cursor(cursor)
, attr(attr)
{
@@ -70,7 +70,7 @@ struct AttrDb
{
auto state(_state->lock());
auto cacheDir = std::filesystem::path(getCacheDir()) / "eval-cache-v6";
auto cacheDir = getCacheDir() / "eval-cache-v6";
createDirs(cacheDir);
auto dbPath = cacheDir / (fingerprint.to_string(HashFormat::Base16, false) + ".sqlite");

View File

@@ -1,9 +1,16 @@
#include "nix/expr/eval-error.hh"
#include "nix/expr/eval.hh"
#include "nix/expr/value.hh"
#include "nix/store/store-api.hh"
namespace nix {
InvalidPathError::InvalidPathError(EvalState & state, const StorePath & path)
: CloneableError(state, "path '%s' is not valid", path.to_string())
, path{path}
{
}
template<class T>
EvalErrorBuilder<T> & EvalErrorBuilder<T>::withExitStatus(unsigned int exitStatus)
{
@@ -114,5 +121,6 @@ template class EvalErrorBuilder<InfiniteRecursionError>;
template class EvalErrorBuilder<StackOverflowError>;
template class EvalErrorBuilder<InvalidPathError>;
template class EvalErrorBuilder<IFDError>;
template class EvalErrorBuilder<RecoverableEvalError>;
} // namespace nix

View File

@@ -137,7 +137,7 @@ public:
: state(state)
, sampleInterval(period)
, profileFd([&]() {
AutoCloseFD fd = openNewFileForWrite(
auto fd = openNewFileForWrite(
profileFile,
0660,
{

View File

@@ -1,4 +1,5 @@
#include "nix/util/users.hh"
#include "nix/util/logging.hh"
#include "nix/store/globals.hh"
#include "nix/store/profiles.hh"
#include "nix/expr/eval.hh"
@@ -6,6 +7,26 @@
namespace nix {
void DeprecatedWarnSetting::assign(const bool & v)
{
value = v;
warn("'%s' is deprecated, use '%s = %s' instead", name, targetName, v ? "warn" : "ignore");
if (!target.overridden)
target = v ? Diagnose::Warn : Diagnose::Ignore;
}
void DeprecatedWarnSetting::appendOrSet(bool newValue, bool append)
{
assert(!append);
assign(newValue);
}
void DeprecatedWarnSetting::override(const bool & v)
{
overridden = true;
assign(v);
}
/* Very hacky way to parse $NIX_PATH, which is colon-separated, but
can contain URLs (e.g. "nixpkgs=https://bla...:foo=https://"). */
Strings EvalSettings::parseNixPath(const std::string & s)
@@ -70,7 +91,7 @@ Strings EvalSettings::getDefaultNixPath()
}
};
add(std::filesystem::path{getNixDefExpr()} / "channels");
add(getNixDefExpr() / "channels");
auto profilesDirOpts = settings.getProfileDirsOptions();
add(rootChannelsDir(profilesDirOpts) / "nixpkgs", "nixpkgs");
add(rootChannelsDir(profilesDirOpts));

View File

@@ -1,4 +1,5 @@
#include "nix/expr/eval.hh"
#include "nix/expr/eval-error.hh"
#include "nix/expr/eval-settings.hh"
#include "nix/expr/primops.hh"
#include "nix/expr/print-options.hh"
@@ -31,6 +32,7 @@
#include <algorithm>
#include <cstddef>
#include <cstdlib>
#include <exception>
#include <iostream>
#include <sstream>
#include <cstring>
@@ -155,6 +157,8 @@ std::string_view showType(ValueType type, bool withArticle)
return WA("a", "float");
case nThunk:
return WA("a", "thunk");
case nFailed:
return WA("an", "error");
}
unreachable();
}
@@ -224,10 +228,6 @@ static Symbol getName(const AttrName & name, EvalState & state, Env & env)
static constexpr size_t BASE_ENV_SIZE = 128;
EvalMemory::EvalMemory()
#if NIX_USE_BOEHMGC
: valueAllocCache(std::allocate_shared<void *>(traceable_allocator<void *>(), nullptr))
, env1AllocCache(std::allocate_shared<void *>(traceable_allocator<void *>(), nullptr))
#endif
{
assertGCInitialized();
}
@@ -364,7 +364,7 @@ EvalState::EvalState(
EvalState::~EvalState() {}
void EvalState::allowPathLegacy(const Path & path)
void EvalState::allowPathLegacy(const std::string & path)
{
if (auto rootFS2 = rootFS.dynamic_pointer_cast<AllowListSourceAccessor>())
rootFS2->allowPrefix(CanonPath(path));
@@ -422,29 +422,36 @@ bool isAllowedURI(std::string_view uri, const Strings & allowedUris)
return false;
}
void EvalState::checkURI(const std::string & uri)
void EvalState::checkURI(const std::string & uri0)
{
if (!settings.restrictEval)
return;
if (isAllowedURI(uri, settings.allowedUris.get()))
if (isAllowedURI(uri0, settings.allowedUris.get()))
return;
/* If the URI is a path, then check it against allowedPaths as
well. */
if (isAbsolute(uri)) {
if (auto rootFS2 = rootFS.dynamic_pointer_cast<AllowListSourceAccessor>())
rootFS2->checkAccess(CanonPath(uri));
return;
{
std::filesystem::path path(uri0);
if (path.is_absolute()) {
if (auto rootFS2 = rootFS.dynamic_pointer_cast<AllowListSourceAccessor>())
rootFS2->checkAccess(CanonPath(path.string()));
return;
}
}
if (hasPrefix(uri, "file://")) {
if (auto rootFS2 = rootFS.dynamic_pointer_cast<AllowListSourceAccessor>())
rootFS2->checkAccess(CanonPath(uri.substr(7)));
return;
try {
ParsedURL uri = parseURL(uri0);
if (uri.scheme == "file") {
if (auto rootFS2 = rootFS.dynamic_pointer_cast<AllowListSourceAccessor>())
rootFS2->checkAccess(CanonPath(urlPathToPath(uri.path).string()));
return;
}
} catch (BadURL &) {
}
throw RestrictedPathError("access to URI '%s' is forbidden in restricted mode", uri);
throw RestrictedPathError("access to URI '%s' is forbidden in restricted mode", uri0);
}
Value * EvalState::addConstant(const std::string & name, Value & v, Constant info)
@@ -1653,7 +1660,7 @@ void EvalState::callFunction(Value & fun, std::span<Value *> args, Value & vRes,
primOpCalls[fn->name]++;
try {
fn->fun(*this, vCur.determinePos(noPos), args.data(), vCur);
fn->impl(*this, vCur.determinePos(noPos), args.data(), vCur);
} catch (Error & e) {
if (fn->addTrace)
addErrorTrace(e, pos, "while calling the '%1%' builtin", fn->name);
@@ -1703,7 +1710,7 @@ void EvalState::callFunction(Value & fun, std::span<Value *> args, Value & vRes,
// 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.
fn->fun(*this, vCur.determinePos(noPos), vArgs, vCur);
fn->impl(*this, vCur.determinePos(noPos), vArgs, vCur);
} catch (Error & e) {
if (fn->addTrace)
addErrorTrace(e, pos, "while calling the '%1%' builtin", fn->name);
@@ -1987,11 +1994,15 @@ void ExprOpUpdate::eval(EvalState & state, Env & env, Value & v)
UpdateQueue q;
evalForUpdate(state, env, q);
v.mkAttrs(&Bindings::emptyBindings);
Value vTmp;
vTmp.mkAttrs(&Bindings::emptyBindings);
for (auto & rhs : std::views::reverse(q)) {
/* Remember that queue is sorted rightmost attrset first. */
eval(state, /*v=*/v, /*v1=*/v, /*v2=*/rhs);
eval(state, /*v=*/vTmp, /*v1=*/vTmp, /*v2=*/rhs);
}
v = vTmp;
}
void Expr::evalForUpdate(EvalState & state, Env & env, UpdateQueue & q, std::string_view errorCtx)
@@ -2174,6 +2185,54 @@ void ExprBlackHole::eval(EvalState & state, [[maybe_unused]] Env & env, Value &
// always force this to be separate, otherwise forceValue may inline it and take
// a massive perf hit
[[gnu::noinline]]
void EvalState::handleEvalExceptionForThunk(Env * env, Expr * expr, Value & v, const PosIdx pos)
{
if (!env)
tryFixupBlackHolePos(v, pos);
auto e = std::current_exception();
Value * recovery = nullptr;
try {
std::rethrow_exception(e);
} catch (const RecoverableEvalError & e) {
recovery = allocValue();
} catch (...) {
}
if (recovery) {
recovery->mkThunk(env, expr);
}
v.mkFailed(e, recovery);
}
[[gnu::noinline]]
void EvalState::handleEvalExceptionForApp(Value & v, const Value & savedApp)
{
auto e = std::current_exception();
Value * recovery = nullptr;
try {
std::rethrow_exception(e);
} catch (const RecoverableEvalError & e) {
recovery = allocValue();
} catch (...) {
}
if (recovery) {
*recovery = savedApp;
}
v.mkFailed(e, recovery);
}
[[gnu::noinline]]
void EvalState::handleEvalFailed(Value & v, const PosIdx pos)
{
assert(v.isFailed());
if (auto recoveryValue = v.failed().recoveryValue) {
v = *recoveryValue;
forceValue(v, pos);
} else {
v.failed().rethrow();
}
}
void EvalState::tryFixupBlackHolePos(Value & v, PosIdx pos)
{
if (!v.isBlackhole())
@@ -2182,7 +2241,8 @@ void EvalState::tryFixupBlackHolePos(Value & v, PosIdx pos)
try {
std::rethrow_exception(e);
} catch (InfiniteRecursionError & e) {
e.atPos(positions[pos]);
if (!e.hasPos())
e.atPos(positions[pos]);
} catch (...) {
}
}
@@ -2355,10 +2415,11 @@ std::string_view EvalState::forceStringNoCtx(Value & v, const PosIdx pos, std::s
{
auto s = forceString(v, pos, errorCtx);
if (v.context()) {
auto ctxElem = NixStringContextElem::parse((*v.context()->begin())->view());
error<EvalError>(
"the string '%1%' is not allowed to refer to a store path (such as '%2%')",
v.string_view(),
(*v.context()->begin())->view())
ctxElem.display(*store))
.withTrace(pos, errorCtx)
.debugThrow();
}
@@ -2821,8 +2882,11 @@ void EvalState::assertEqValues(Value & v1, Value & v2, const PosIdx pos, std::st
}
return;
case nThunk: // Must not be left by forceValue
assert(false);
// Cannot be returned by forceValue().
case nThunk:
case nFailed:
unreachable();
default: // Note that we pass compiler flags that should make `default:` unreachable.
// Also note that this probably ran after `eqValues`, which implements
// the same logic more efficiently (without having to unwind stacks),
@@ -2916,8 +2980,11 @@ bool EvalState::eqValues(Value & v1, Value & v2, const PosIdx pos, std::string_v
// !!!
return v1.fpoint() == v2.fpoint();
case nThunk: // Must not be left by forceValue
assert(false);
// Cannot be returned by forceValue().
case nThunk:
case nFailed:
unreachable();
default: // Note that we pass compiler flags that should make `default:` unreachable.
error<EvalError>("eqValues: cannot compare %1% with %2%", showType(v1), showType(v2))
.withTrace(pos, errorCtx)
@@ -3138,6 +3205,21 @@ Expr * EvalState::parseExprFromString(std::string s, const SourcePath & basePath
return parseExprFromString(std::move(s), basePath, staticBaseEnv);
}
ExprAttrs *
EvalState::parseReplBindings(std::string s_, const SourcePath & basePath, const std::shared_ptr<StaticEnv> & staticEnv)
{
return parseReplBindings(s_, s_, basePath, staticEnv);
}
ExprAttrs * EvalState::parseReplBindings(
std::string s_, std::string errorSource, const SourcePath & basePath, const std::shared_ptr<StaticEnv> & staticEnv)
{
auto s = make_ref<std::string>(std::move(errorSource));
// flex requires two NUL terminators for yy_scan_buffer
s_.append("\0\0", 2);
return parseReplBindings(s_.data(), s_.size(), Pos::String{.source = s}, basePath, staticEnv);
}
Expr * EvalState::parseStdin()
{
// NOTE this method (and parseExprFromString) must take care to *fully copy* their
@@ -3279,6 +3361,30 @@ Expr * EvalState::parse(
return result;
}
ExprAttrs * EvalState::parseReplBindings(
char * text,
size_t length,
Pos::Origin origin,
const SourcePath & basePath,
const std::shared_ptr<StaticEnv> & staticEnv)
{
DocCommentMap tmpDocComments;
DocCommentMap * docComments = &tmpDocComments;
if (auto sourcePath = std::get_if<SourcePath>(&origin)) {
auto [it, _] = positionToDocComment.try_emplace(*sourcePath);
docComments = &it->second;
}
auto bindings = parseReplBindingsFromBuf(
text, length, origin, basePath, mem.exprs, symbols, settings, positions, *docComments, rootFS);
assert(bindings);
bindings->bindVars(*this, staticEnv);
return bindings;
}
DocComment EvalState::getDocCommentForPos(PosIdx pos)
{
auto pos2 = positions[pos];

View File

@@ -101,7 +101,7 @@ StorePath PackageInfo::queryOutPath() const
i->pos, *i->value, context, "while evaluating the output path of a derivation");
}
if (!outPath)
throw UnimplementedError("CA derivations are not yet supported");
throw Error("derivation does not have attribute 'outPath'");
return *outPath;
}

View File

@@ -0,0 +1,76 @@
#pragma once
///@file
#include <optional>
#include "nix/util/ansicolor.hh"
#include "nix/util/configuration.hh"
#include "nix/util/error.hh"
#include "nix/util/logging.hh"
namespace nix {
/**
* Diagnostic level for deprecated or non-portable language features.
*/
enum struct Diagnose {
/**
* Ignore the feature without any diagnostic.
*/
Ignore,
/**
* Warn when the feature is used, but allow it.
*/
Warn,
/**
* Treat the feature as a fatal error.
*/
Fatal,
};
template<>
Diagnose BaseSetting<Diagnose>::parse(const std::string & str) const;
template<>
std::string BaseSetting<Diagnose>::to_string() const;
/**
* Check a diagnostic setting and either do nothing, log a warning, or throw an error.
*
* The setting name is automatically appended to the error message.
*
* @param setting The diagnostic setting to check
* @param mkError A function that takes a bool (true if fatal, false if warning) and
* returns an optional error to throw (or warn with).
* Only called if level is not `Ignore`.
* If the function returns `std::nullopt`, no diagnostic is emitted.
*
* @throws The error returned by mkError if level is `Fatal` and mkError returns a value
*/
template<typename F>
void diagnose(const Setting<Diagnose> & setting, F && mkError)
{
auto withError = [&](bool fatal, auto && handler) {
auto maybeError = mkError(fatal);
if (!maybeError)
return;
auto & info = maybeError->unsafeInfo();
// Append the setting name to help users find the right setting
info.msg = HintFmt("%s (" ANSI_BOLD "%s" ANSI_NORMAL ")", Uncolored(info.msg.str()), setting.name);
maybeError->recalcWhat();
handler(std::move(*maybeError));
};
switch (setting.get()) {
case Diagnose::Ignore:
return;
case Diagnose::Warn:
withError(false, [](auto && error) { logWarning(error.info()); });
return;
case Diagnose::Fatal:
withError(true, [](auto && error) { throw std::move(error); });
return;
}
}
} // namespace nix

View File

@@ -14,7 +14,7 @@ namespace nix::eval_cache {
struct AttrDb;
class AttrCursor;
struct CachedEvalError : EvalError
struct CachedEvalError : CloneableError<CachedEvalError, EvalError>
{
const ref<AttrCursor> cursor;
const Symbol attr;
@@ -36,7 +36,7 @@ class EvalCache : public std::enable_shared_from_this<EvalCache>
std::shared_ptr<AttrDb> db;
EvalState & state;
typedef std::function<Value *()> RootLoader;
typedef fun<Value *()> RootLoader;
RootLoader rootLoader;
RootValue value;

View File

@@ -2,6 +2,7 @@
#include "nix/util/error.hh"
#include "nix/util/pos-idx.hh"
#include "nix/store/path.hh"
namespace nix {
@@ -18,7 +19,7 @@ class EvalErrorBuilder;
*
* Most subclasses should inherit from `EvalError` instead of this class.
*/
class EvalBaseError : public Error
class EvalBaseError : public CloneableError<EvalBaseError, Error>
{
template<class T>
friend class EvalErrorBuilder;
@@ -26,14 +27,14 @@ public:
EvalState & state;
EvalBaseError(EvalState & state, ErrorInfo && errorInfo)
: Error(errorInfo)
: CloneableError(errorInfo)
, state(state)
{
}
template<typename... Args>
explicit EvalBaseError(EvalState & state, const std::string & formatString, const Args &... formatArgs)
: Error(formatString, formatArgs...)
: CloneableError(formatString, formatArgs...)
, state(state)
{
}
@@ -60,25 +61,30 @@ MakeError(InfiniteRecursionError, EvalError);
* Inherits from EvalBaseError (not EvalError) because resource exhaustion
* should not be cached.
*/
struct StackOverflowError : public EvalBaseError
struct StackOverflowError : public CloneableError<StackOverflowError, EvalBaseError>
{
StackOverflowError(EvalState & state)
: EvalBaseError(state, "stack overflow; max-call-depth exceeded")
: CloneableError(state, "stack overflow; max-call-depth exceeded")
{
}
};
MakeError(IFDError, EvalBaseError);
struct InvalidPathError : public EvalError
/**
* An evaluation error which should be retried instead of rethrown.
*
* A RecoverableEvalError is not an EvalError, because we shouldn't cache it in
* the eval cache, as it should be retried anyway.
*/
MakeError(RecoverableEvalError, EvalBaseError);
struct InvalidPathError : public CloneableError<InvalidPathError, EvalError>
{
public:
Path path;
StorePath path;
InvalidPathError(EvalState & state, const Path & path)
: EvalError(state, "path '%s' is not valid", path)
{
}
InvalidPathError(EvalState & state, const StorePath & path);
};
/**

View File

@@ -33,6 +33,9 @@ using gc_allocator = std::allocator<T>;
struct gc
{};
struct gc_cleanup
{};
#endif
namespace nix {

View File

@@ -5,6 +5,7 @@
#include "nix/expr/eval.hh"
#include "nix/expr/eval-error.hh"
#include "nix/expr/eval-settings.hh"
#include <exception>
namespace nix {
@@ -29,6 +30,11 @@ inline void * EvalMemory::allocBytes(size_t n)
Value * EvalMemory::allocValue()
{
#if NIX_USE_BOEHMGC
/* Allocation cache for GC'd Value objects. Boehm GC is already a global resource, so thread_local is
a natural solution. Multiple EvalState instances on the same thread will reuse the same cache. */
static thread_local std::shared_ptr<void *> valueAllocCache{
std::allocate_shared<void *>(traceable_allocator<void *>(), nullptr)};
/* We use the boehm batch allocator to speed up allocations of Values (of which there are many).
GC_malloc_many returns a linked list of objects of the given size, where the first word
of each object is also the pointer to the next object in the list. This also means that we
@@ -62,6 +68,10 @@ Env & EvalMemory::allocEnv(size_t size)
#if NIX_USE_BOEHMGC
if (size == 1) {
/* Allocation cache for size-1 Env objects. Boehm GC is already a global resource, so thread_local is
a natural solution. Multiple EvalState instances on the same thread will reuse the same cache. */
static thread_local std::shared_ptr<void *> env1AllocCache{
std::allocate_shared<void *>(traceable_allocator<void *>(), nullptr)};
/* see allocValue for explanations. */
if (!*env1AllocCache) {
*env1AllocCache = GC_malloc_many(sizeof(Env) + sizeof(Value *));
@@ -91,18 +101,25 @@ void EvalState::forceValue(Value & v, const PosIdx pos)
Expr * expr = v.thunk().expr;
try {
v.mkBlackhole();
// checkInterrupt();
if (env) [[likely]]
expr->eval(*this, *env, v);
else
ExprBlackHole::throwInfiniteRecursionError(*this, v);
} catch (...) {
v.mkThunk(env, expr);
tryFixupBlackHolePos(v, pos);
handleEvalExceptionForThunk(env, expr, v, pos);
throw;
}
} else if (v.isApp())
callFunction(*v.app().left, *v.app().right, v, pos);
} else if (v.isApp()) {
Value savedApp = v;
try {
callFunction(*v.app().left, *v.app().right, v, pos);
} catch (...) {
handleEvalExceptionForApp(v, savedApp);
throw;
}
} else if (v.isFailed()) {
handleEvalFailed(v, pos);
}
}
[[gnu::always_inline]]

View File

@@ -1,6 +1,7 @@
#pragma once
///@file
#include "nix/expr/diagnose.hh"
#include "nix/expr/eval-profiler-settings.hh"
#include "nix/util/configuration.hh"
#include "nix/util/source-path.hh"
@@ -10,6 +11,36 @@ namespace nix {
class EvalState;
struct PrimOp;
/**
* A deprecated bool setting that migrates to a `Setting<Diagnose>`.
* When set to true, it emits a deprecation warning and sets the target
* `Setting<Diagnose>` setting to `Warn`.
*/
class DeprecatedWarnSetting : public BaseSetting<bool>
{
Setting<Diagnose> & target;
const char * targetName;
public:
DeprecatedWarnSetting(
Config * options,
Setting<Diagnose> & target,
const char * targetName,
const std::string & name,
const std::string & description,
const StringSet & aliases = {})
: BaseSetting<bool>(false, true, name, description, aliases, std::nullopt)
, target(target)
, targetName(targetName)
{
options->addSetting(this);
}
void assign(const bool & v) override;
void appendOrSet(bool newValue, bool append) override;
void override(const bool & v) override;
};
struct EvalSettings : Config
{
/**
@@ -36,7 +67,7 @@ struct EvalSettings : Config
* if `<scheme>` is a key in this map, then `<arbitrary string>` is
* passed to the hook that is the value in this map.
*/
using LookupPathHooks = std::map<std::string, std::function<LookupPathHook>>;
using LookupPathHooks = std::map<std::string, fun<LookupPathHook>>;
EvalSettings(bool & readOnlyMode, LookupPathHooks lookupPathHooks = {});
@@ -237,7 +268,7 @@ struct EvalSettings : Config
See [Using the `eval-profiler`](@docroot@/advanced-topics/eval-profiler.md).
)"};
Setting<Path> evalProfileFile{
Setting<std::filesystem::path> evalProfileFile{
this,
"nix.profile",
"eval-profile-file",
@@ -328,20 +359,94 @@ struct EvalSettings : Config
This option can be enabled by setting `NIX_ABORT_ON_WARN=1` in the environment.
)"};
Setting<bool> warnShortPathLiterals{
Setting<Diagnose> lintShortPathLiterals{
this,
false,
"warn-short-path-literals",
Diagnose::Ignore,
"lint-short-path-literals",
R"(
If set to true, the Nix evaluator will warn when encountering relative path literals
that don't start with `./` or `../`.
Controls handling of relative path literals that don't start with `./` or `../`.
For example, with this setting enabled, `foo/bar` would emit a warning
suggesting to use `./foo/bar` instead.
- `ignore`: Ignore without warning (default)
- `warn`: Emit a warning suggesting to use `./` prefix
- `fatal`: Treat as a parse error
For example, with this setting set to `warn` or `fatal`, `foo/bar` would
suggest using `./foo/bar` instead.
This is useful for improving code readability and making path literals
more explicit.
)"};
)",
};
DeprecatedWarnSetting warnShortPathLiterals{
this,
lintShortPathLiterals,
"lint-short-path-literals",
"warn-short-path-literals",
R"(
Deprecated. Use [`lint-short-path-literals`](#conf-lint-short-path-literals)` = warn` instead.
)",
};
Setting<Diagnose> lintAbsolutePathLiterals{
this,
Diagnose::Ignore,
"lint-absolute-path-literals",
R"(
Controls handling of absolute path literals (paths starting with `/`) and home path literals (paths starting with `~/`).
- `ignore`: Ignore without warning (default)
- `warn`: Emit a warning about non-portability
- `fatal`: Treat as a parse error
It is true that some files are more difficult to reference with relative paths,
because they would require lots of `../../..` upward traversing to reach them.
But firstly, it is probably not a good idea to reference these files ---
such paths often make Nix expressions less portable and reproducible,
as they depend on the file system layout of the machine evaluating the expression.
Secondly, with [pure evaluation mode](#conf-pure-eval), most such files are prohibited to access anyway,
whether by absolute or relative paths.
In that case, enabling this lint in fatal mode is less disruptive,
because the paths pure eval allows are usually not the ones that would be ergonomically expressed with absolute paths anyway.
)",
};
Setting<Diagnose> lintUrlLiterals{
this,
Diagnose::Ignore,
"lint-url-literals",
R"(
Controls handling of unquoted URLs as part of the Nix language syntax.
The Nix language allows for URL literals, like so:
```
$ nix repl
nix-repl> http://foo
"http://foo"
```
Setting this to `warn` or `fatal` will cause the Nix parser to
warn or throw an error when encountering a URL literal:
```
$ nix repl --lint-url-literals fatal
nix-repl> http://foo
error: URL literal 'http://foo' is deprecated
at «string»:1:1:
1| http://foo
| ^
```
Unquoted URLs are being deprecated and their usage is discouraged.
The reason is that, as opposed to path literals, URLs have no
special properties that distinguish them from regular strings, URLs
containing query parameters have to be quoted anyway, and unquoted URLs
may confuse external tooling.
)",
};
Setting<unsigned> bindingsUpdateLayerRhsSizeThreshold{
this,

View File

@@ -120,7 +120,7 @@ struct PrimOp
/**
* Implementation of the primop.
*/
std::function<PrimOpFun> fun;
fun<PrimOpFun> impl;
/**
* Optional experimental for this to be gated on.
@@ -305,18 +305,6 @@ struct StaticEvalSymbols
class EvalMemory
{
#if NIX_USE_BOEHMGC
/**
* Allocation cache for GC'd Value objects.
*/
std::shared_ptr<void *> valueAllocCache;
/**
* Allocation cache for size-1 Env objects.
*/
std::shared_ptr<void *> env1AllocCache;
#endif
public:
struct Statistics
{
@@ -548,7 +536,7 @@ public:
/**
* Variant which accepts relative paths too.
*/
SourcePath rootPath(PathView path);
SourcePath rootPath(std::string_view path);
/**
* Return a `SourcePath` that refers to `path` in the store.
@@ -565,7 +553,7 @@ public:
* Only for restrict eval: pure eval just whitelist store paths,
* never arbitrary paths.
*/
void allowPathLegacy(const Path & path);
void allowPathLegacy(const std::string & path);
/**
* Allow access to a store path. Note that this gets remapped to
@@ -603,6 +591,18 @@ public:
parseExprFromString(std::string s, const SourcePath & basePath, const std::shared_ptr<StaticEnv> & staticEnv);
Expr * parseExprFromString(std::string s, const SourcePath & basePath);
/**
* Parse REPL bindings from the specified string.
* Returns ExprAttrs with bindings to add to scope.
*/
ExprAttrs *
parseReplBindings(std::string s, const SourcePath & basePath, const std::shared_ptr<StaticEnv> & staticEnv);
ExprAttrs * parseReplBindings(
std::string s,
std::string errorSource,
const SourcePath & basePath,
const std::shared_ptr<StaticEnv> & staticEnv);
Expr * parseStdin();
/**
@@ -652,8 +652,28 @@ public:
*/
inline void forceValue(Value & v, const PosIdx pos);
private:
/**
* Internal support function for forceValue
*
* This code is factored out so that it's not in the heavily inlined hot path.
*/
void handleEvalExceptionForThunk(Env * env, Expr * expr, Value & v, const PosIdx pos);
/**
* Internal support function for forceValue
*
* This code is factored out so that it's not in the heavily inlined hot path.
*/
void handleEvalExceptionForApp(Value & v, const Value & savedApp);
void handleEvalFailed(Value & v, PosIdx pos);
void tryFixupBlackHolePos(Value & v, PosIdx pos);
public:
/**
* Force a value, then recursively force list elements and
* attributes.
@@ -867,6 +887,13 @@ private:
const SourcePath & basePath,
const std::shared_ptr<StaticEnv> & staticEnv);
ExprAttrs * parseReplBindings(
char * text,
size_t length,
Pos::Origin origin,
const SourcePath & basePath,
const std::shared_ptr<StaticEnv> & staticEnv);
/**
* Current Nix call stack depth, used with `max-call-depth` setting to throw stack overflow hopefully before we run
* out of system stack.

View File

@@ -11,6 +11,7 @@ headers = [ config_pub_h ] + files(
'attr-path.hh',
'attr-set.hh',
'counter.hh',
'diagnose.hh',
'eval-cache.hh',
'eval-error.hh',
'eval-gc.hh',

View File

@@ -7,6 +7,7 @@
#include <cstring>
#include <memory>
#include <memory_resource>
#include <exception>
#include <span>
#include <string_view>
#include <type_traits>
@@ -42,6 +43,7 @@ enum InternalType {
tBool,
tNull,
tFloat,
tFailed,
tExternal,
tPrimOp,
tAttrs,
@@ -68,6 +70,7 @@ enum InternalType {
*/
typedef enum {
nThunk,
nFailed,
nInt,
nFloat,
nBool,
@@ -152,7 +155,7 @@ public:
bool location,
XMLWriter & doc,
NixStringContext & context,
PathSet & drvsSeen,
StringSet & drvsSeen,
const PosIdx pos) const;
virtual ~ExternalValueBase() {};
@@ -424,6 +427,36 @@ struct ValueBase
size_t size;
Value * const * elems;
};
struct Failed : gc_cleanup
{
std::exception_ptr ex;
/**
* Optional value for recovering `RecoverableEvalError`
* Must be set iff `ex` is an instance of `RecoverableEvalError`.
*/
Value * recoveryValue;
Failed(std::exception_ptr ex, Value * recoveryValue)
: ex(ex)
, recoveryValue(recoveryValue)
{
}
[[noreturn]] void rethrow() const
{
try {
std::rethrow_exception(ex);
} catch (BaseError & e) {
/* Rethrow the copy of the exception - not the original one.
Stack tracing mechanisms rely on being able to modify the exceptions
they catch by reference. */
e.throwClone();
} catch (...) {
throw;
}
}
};
};
template<typename T>
@@ -450,6 +483,7 @@ struct PayloadTypeToInternalType
MACRO(PrimOp *, primOp, tPrimOp) \
MACRO(ValueBase::PrimOpApplicationThunk, primOpApp, tPrimOpApp) \
MACRO(ExternalValueBase *, external, tExternal) \
MACRO(ValueBase::Failed *, failed, tFailed) \
MACRO(NixFloat, fpoint, tFloat)
#define NIX_VALUE_PAYLOAD_TYPE(T, FIELD_NAME, DISCRIMINATOR) \
@@ -754,6 +788,11 @@ protected:
path.path = std::bit_cast<const StringData *>(payload[1]);
}
void getStorage(Failed *& failed) const noexcept
{
failed = std::bit_cast<Failed *>(payload[1]);
}
void setStorage(NixInt integer) noexcept
{
setSingleDWordPayload<tInt>(integer.value);
@@ -803,6 +842,11 @@ protected:
{
setUntaggablePayload<pdPath>(path.accessor, path.path);
}
void setStorage(Failed * failed) noexcept
{
setSingleDWordPayload<tFailed>(std::bit_cast<PackedPointer>(failed));
}
};
/**
@@ -1054,12 +1098,12 @@ public:
inline bool isThunk() const
{
return isa<tThunk>();
};
}
inline bool isApp() const
{
return isa<tApp>();
};
}
inline bool isBlackhole() const;
@@ -1067,17 +1111,22 @@ public:
inline bool isLambda() const
{
return isa<tLambda>();
};
}
inline bool isPrimOp() const
{
return isa<tPrimOp>();
};
}
inline bool isPrimOpApp() const
{
return isa<tPrimOpApp>();
};
}
inline bool isFailed() const
{
return isa<tFailed>();
}
/**
* Returns the normal type of a Value. This only returns nThunk if
@@ -1098,6 +1147,7 @@ public:
t[tBool] = nBool;
t[tNull] = nNull;
t[tFloat] = nFloat;
t[tFailed] = nFailed;
t[tExternal] = nExternal;
t[tAttrs] = nAttrs;
t[tPrimOp] = nFunction;
@@ -1235,6 +1285,11 @@ public:
setStorage(n);
}
inline void mkFailed(std::exception_ptr e, Value * recovery) noexcept
{
setStorage(new Value::Failed(e, recovery));
}
bool isList() const noexcept
{
return isa<tListSmall, tListN>();
@@ -1349,6 +1404,13 @@ public:
{
return getStorage<Path>().accessor;
}
Failed & failed() const noexcept
{
auto p = getStorage<Failed *>();
assert(p);
return *p;
}
};
extern ExprBlackHole eBlackHole;

View File

@@ -9,14 +9,14 @@
namespace nix {
class BadNixStringContextElem : public Error
class BadNixStringContextElem final : public CloneableError<BadNixStringContextElem, Error>
{
public:
std::string_view raw;
template<typename... Args>
BadNixStringContextElem(std::string_view raw_, const Args &... args)
: Error("")
: CloneableError("")
{
raw = raw_;
auto hf = HintFmt(args...);
@@ -83,6 +83,14 @@ struct NixStringContextElem
static NixStringContextElem
parse(std::string_view s, const ExperimentalFeatureSettings & xpSettings = experimentalFeatureSettings);
std::string to_string() const;
/**
* Render for use in error messages and other user-facing output.
*
* Uses store paths and `DerivedPath` syntax, unlike `to_string()`
* which uses the internal encoding.
*/
std::string display(const StoreDirConfig & store) const;
};
/**

View File

@@ -14,6 +14,7 @@
%x INPATH
%x INPATH_SLASH
%x PATH_START
%x REPL_BINDINGS_MODE
%top {
#include "parser-tab.hh" // YYSTYPE
@@ -113,6 +114,13 @@ URI [a-zA-Z][a-zA-Z0-9\+\-\.]*\:[a-zA-Z0-9\%\/\?\:\@\&\=\+\$\,\-\_\.\!\~
%%
/* REPL bindings mode: inject REPL_BINDINGS token at start, then switch to normal lexing */
<REPL_BINDINGS_MODE>.|\n {
yyless(0);
yylloc->unstash();
POP_STATE();
return REPL_BINDINGS;
}
if { return IF; }
then { return THEN; }
@@ -339,3 +347,17 @@ or { return OR_KW; }
}
%%
#include <type_traits>
// Verify that the forward declaration in parser.y matches flex's definition
static_assert(std::is_same_v<yyscan_t, void *>);
namespace nix {
void setReplBindingsMode(yyscan_t scanner)
{
yy_push_state(REPL_BINDINGS_MODE, scanner);
}
}

View File

@@ -153,6 +153,7 @@ endforeach
sources = files(
'attr-path.cc',
'attr-set.cc',
'diagnose.cc',
'eval-cache.cc',
'eval-error.cc',
'eval-gc.cc',

View File

@@ -30,6 +30,7 @@
#include "nix/expr/nixexpr.hh"
#include "nix/expr/eval.hh"
#include "nix/expr/eval-settings.hh"
#include "nix/expr/diagnose.hh"
#include "nix/expr/parser-state.hh"
#define YY_DECL \
@@ -54,6 +55,11 @@
} \
while (0)
// Forward declaration for flex scanner type.
// lexer-tab.hh is large; avoid including it just for this type.
// Asserted with static_assert in lexer.l.
typedef void * yyscan_t;
namespace nix {
typedef boost::unordered_flat_map<PosIdx, DocComment, std::hash<PosIdx>> DocCommentMap;
@@ -70,6 +76,28 @@ Expr * parseExprFromBuf(
DocCommentMap & docComments,
const ref<SourceAccessor> rootFS);
/**
* Puts the lexer in REPL bindings mode before the first token. This causes
* the parser to accept REPL bindings (attribute definitions).
*/
void setReplBindingsMode(yyscan_t scanner);
/**
* Parse REPL bindings from a buffer.
* Returns ExprAttrs with bindings to add to scope.
*/
ExprAttrs * parseReplBindingsFromBuf(
char * text,
size_t length,
Pos::Origin origin,
const SourcePath & basePath,
Exprs & exprs,
SymbolTable & symbols,
const EvalSettings & settings,
PosTable & positions,
DocCommentMap & docComments,
const ref<SourceAccessor> rootFS);
}
#endif
@@ -175,6 +203,7 @@ static Expr * makeCall(Exprs & exprs, PosIdx pos, Expr * fn, Expr * arg) {
%token IND_STRING_OPEN "start of an indented string"
%token IND_STRING_CLOSE "end of an indented string"
%token ELLIPSIS "'...'"
%token REPL_BINDINGS "start of REPL bindings"
%right IMPL
%left OR
@@ -196,6 +225,10 @@ start: expr {
// This parser does not use yynerrs; suppress the warning.
(void) yynerrs_;
}
| REPL_BINDINGS binds1 {
state->result = $2;
(void) yynerrs_;
};
expr: expr_function;
@@ -337,12 +370,14 @@ expr_simple
state->exprs.add<ExprString>(state->exprs.alloc, path)});
}
| URI {
static bool noURLLiterals = experimentalFeatureSettings.isEnabled(Xp::NoUrlLiterals);
if (noURLLiterals)
throw ParseError({
.msg = HintFmt("URL literals are disabled"),
diagnose(state->settings.lintUrlLiterals, [&](bool fatal) -> std::optional<ParseError> {
return ParseError({
.msg = HintFmt("URL literals are %s. Consider using a string literal \"%s\" instead",
fatal ? "disallowed" : "discouraged",
std::string_view($1.p, $1.l)),
.pos = state->positions[CUR_POS]
});
});
$$ = state->exprs.add<ExprString>(state->exprs.alloc, $1);
}
| '(' expr ')' { $$ = $2; }
@@ -380,35 +415,57 @@ path_start
: PATH {
std::string_view literal({$1.p, $1.l});
/* check for short path literals */
if (state->settings.warnShortPathLiterals && literal.front() != '/' && literal.front() != '.') {
logWarning({
.msg = HintFmt("relative path literal '%s' should be prefixed with '.' for clarity: './%s'. (" ANSI_BOLD "warn-short-path-literals" ANSI_NORMAL " = true)", literal, literal),
.pos = state->positions[CUR_POS]
if (literal.front() == '/') {
diagnose(state->settings.lintAbsolutePathLiterals, [&](bool) -> std::optional<ParseError> {
return ParseError({
.msg = HintFmt("absolute path literals are not portable. Consider replacing path literal '%s' by a string, relative path, or parameter", literal),
.pos = state->positions[CUR_POS]
});
});
}
Path path(absPath(literal, state->basePath.path.abs()));
/* add back in the trailing '/' to the first segment */
if (literal.size() > 1 && literal.back() == '/')
path += '/';
$$ =
/* Absolute paths are always interpreted relative to the
root filesystem accessor, rather than the accessor of the
current Nix expression. */
literal.front() == '/'
? state->exprs.add<ExprPath>(state->exprs.alloc, state->rootFS, path)
: state->exprs.add<ExprPath>(state->exprs.alloc, state->basePath.accessor, path);
auto path = canonPath(literal).string();
/* add back in the trailing '/' to the first segment */
if (literal.size() > 1 && literal.back() == '/')
path += '/';
$$ = state->exprs.add<ExprPath>(state->exprs.alloc, state->rootFS, path);
} else {
/* check for short path literals */
diagnose(state->settings.lintShortPathLiterals, [&](bool) -> std::optional<ParseError> {
if (literal.front() != '.')
return ParseError({
.msg = HintFmt("relative path literal '%s' should be prefixed with '.' for clarity: './%s'", literal, literal),
.pos = state->positions[CUR_POS]
});
return std::nullopt;
});
auto basePath = std::filesystem::path(state->basePath.path.abs());
auto path = absPath(literal, &basePath).string();
/* add back in the trailing '/' to the first segment */
if (literal.size() > 1 && literal.back() == '/')
path += '/';
$$ = state->exprs.add<ExprPath>(state->exprs.alloc, state->basePath.accessor, path);
}
}
| HPATH {
std::string_view literal($1.p, $1.l);
if (state->settings.pureEval) {
throw Error(
"the path '%s' can not be resolved in pure mode",
std::string_view($1.p, $1.l)
literal
);
}
Path path(getHome().string() + std::string($1.p + 1, $1.l - 1));
$$ = state->exprs.add<ExprPath>(state->exprs.alloc, ref<SourceAccessor>(state->rootFS), path);
diagnose(state->settings.lintAbsolutePathLiterals, [&](bool) -> std::optional<ParseError> {
return ParseError({
.msg = HintFmt("home path literals are not portable. Consider replacing path literal '%s' by a string, relative path, or parameter", literal),
.pos = state->positions[CUR_POS]
});
});
auto path(getHome().string() + std::string($1.p + 1, $1.l - 1));
$$ = state->exprs.add<ExprPath>(state->exprs.alloc, state->rootFS, path);
}
;
@@ -577,6 +634,51 @@ Expr * parseExprFromBuf(
return state.result;
}
ExprAttrs * parseReplBindingsFromBuf(
char * text,
size_t length,
Pos::Origin origin,
const SourcePath & basePath,
Exprs & exprs,
SymbolTable & symbols,
const EvalSettings & settings,
PosTable & positions,
DocCommentMap & docComments,
const ref<SourceAccessor> rootFS)
{
yyscan_t scanner;
LexerState lexerState {
.positionToDocComment = docComments,
.positions = positions,
.origin = positions.addOrigin(origin, length),
};
ParserState state {
.lexerState = lexerState,
.exprs = exprs,
.symbols = symbols,
.positions = positions,
.basePath = basePath,
.origin = lexerState.origin,
.rootFS = rootFS,
.settings = settings,
};
yylex_init_extra(&lexerState, &scanner);
Finally _destroy([&] { yylex_destroy(scanner); });
yy_scan_buffer(text, length, scanner);
setReplBindingsMode(scanner);
Parser parser(scanner, &state);
parser.parse();
assert(state.result);
// state.result is Expr *, but the REPL_BINDINGS grammar rule
// always produces an ExprAttrs via the binds1 production.
auto bindings = dynamic_cast<ExprAttrs *>(state.result);
assert(bindings);
return bindings;
}
}
#pragma GCC diagnostic pop // end ignored "-Wswitch-enum"

View File

@@ -10,9 +10,9 @@ SourcePath EvalState::rootPath(CanonPath path)
return {rootFS, std::move(path)};
}
SourcePath EvalState::rootPath(PathView path)
SourcePath EvalState::rootPath(std::string_view path)
{
return {rootFS, CanonPath(absPath(path))};
return {rootFS, CanonPath(absPath(path).string())};
}
SourcePath EvalState::storePath(const StorePath & path)

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