Compare commits

..

264 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
John Ericson
08ce8dbfba Merge pull request #15283 from obsidiansystems/filesytem-error-improvements
Filesystem error improvements
2026-02-18 18:29:44 +00:00
John Ericson
bbcf2041e1 File system error improvements
- Make `descriptorToPath` cross-platform (renamed from
  `windows::handleToPath`). Uses `/proc/self/fd` on Linux and
  `F_GETPATH` on macOS. Add `HAVE_F_GETPATH` meson check.

  This is based on 7226a116a0, which was
  removed in 479c356510, but is now
  introduced more judiciously.

- Unix error messages in `readFull`, `writeFull`, `readLine` now include
  file paths via `descriptorToPath`.

- Convert `std::filesystem::filesystem_error` to `SystemError`

  Wrappers like `readLink`, `createDirs`, `DirectoryIterator`, etc. now
  catch `std::filesystem::filesystem_error` and rethrow as `SystemError`
  with the error code preserved. This ensures consistent exception types
  throughout the codebase.

  Call sites that previously caught `filesystem_error` and rethrew with
  `throw;` now throw `SystemError(e.code(), ...)` instead.

  Some call sites can stop catching `filesystem_error` at all,
  because they only call the wrapped functions.

- Rework `SystemError` constructors to auto-append error message

  The public `SystemError(std::error_code, ...)` constructor now
  automatically appends `errorCode.message()` to the error message.
  A protected constructor takes an explicit error message string for
  subclasses.

  `SysError` delegates to the protected constructor with `strerror(errNo)`.
  `WinError` delegates with `renderError(lastError)` (now static).

  This removes the need to manually append `e.code().message()` at call
  sites when converting `filesystem_error` to `SystemError`.

- Use perfect forwarding (`Args &&...` with `std::forward`) consistently
  in `BaseError`, `SystemError`, `SysError`, and `WinError` constructors.

Co-authored-by: Sergei Zimmerman <sergei@zimmerman.foo>
2026-02-18 12:29:11 -05:00
John Ericson
96bcf5928f Merge pull request #15273 from NixOS/more-robust-ubsan-macro
libutil: More robust check for NIX_UBSAN_ENABLED
2026-02-18 16:15:26 +00:00
Sergei Zimmerman
db853cf4fb libutil: More robust check for NIX_UBSAN_ENABLED
In 3df91bea62 I forgot that the header
might get included out-of-tree with -Wundef. Let's make this a public
config option for libutil as it can affect function bodies in headers.
2026-02-18 17:33:51 +03:00
John Ericson
663db5b48b Merge pull request #15278 from puffnfresh/windows/bar-log-format
Windows: don't use bar log format
2026-02-18 05:14:27 +00:00
Brian McKenna
c486e78235 Windows: don't use bar log format
Relies on terminal features that don't always work on Windows.
2026-02-18 14:35:35 +11:00
John Ericson
4fff871383 Merge pull request #15274 from obsidiansystems/tryToBuild-raii
libstore: refactor `tryToBuild` with coroutine lambdas and RAII
2026-02-17 22:10:42 +00:00
Amaan Qureshi
b9acea908e libstore: refactor tryToBuild with coroutine lambdas and RAII
`tryToBuild` threaded a single `PathLocks outputLocks` by reference
across all build phases and managed a `std::unique_ptr<Activity> actLock`
with explicit `if (!actLock)` guards and `.reset()` calls around the hook
retry loop. This commit introduces coroutine lambdas for the three phases:
`tryHookLoop` owns a `PathLocks` in a scoped block for the first attempt
and per-iteration in the retry loop, `tryBuildLocally` acquires its own
`PathLocks`, and the hook-wait `Activity` is a stack variable scoped to
the postpone block.
2026-02-17 16:23:44 -05:00
John Ericson
c3f0670b4e Merge pull request #15266 from obsidiansystems/fix-maxjobs-error
libstore: structured diagnostics for local build rejection
2026-02-17 18:39:58 +00:00
Amaan Qureshi
7cd4359a8b libstore: structured diagnostics for local build rejection
When `max-jobs = 0` and no remote builders are available, Nix reported
"required system or feature not available" even though the system and
features matched fine. The `canBuildLocally` lambda returned a plain
`bool`, conflating a configuration knob (`max-jobs = 0`) with actual
incompatibility (wrong platform, missing features). It also short-circuited
on the first failing check, so a user with both a platform mismatch and
missing features would only see one of the two.

This commit replaces the bool with a `LocalBuildRejection` struct whose
`WrongLocalStore` variant collects all applicable failures into
`badPlatform`, `missingFeatures`, and an orthogonal `maxJobsZero` flag.
Platform mismatch and missing features now produce separate error
paragraphs, and all applicable reasons appear in a single message.

The local-build capability check also now returns
`std::variant<LocalBuildCapability, LocalBuildRejection>`, bundling
the `LocalStore &` and optional `ExternalBuilder *` together.
2026-02-17 12:54:24 -05:00
John Ericson
6e725093e6 Merge pull request #15143 from obsidiansystems/rootless-daemon-minimal
Support garbage collection in external daemon
2026-02-17 16:53:06 +00:00
Robert Hensing
5c42b84a17 chore(nix): fix evaluation warnings 2026-02-17 16:45:38 +01:00
Artemis Tosini
96fef69755 libstore: support searching for roots from an external daemon
This comes in two parts: a `nix store roots-daemon` command that
can run as root and list runtime roots,
and client logic to find runtime roots for a `LocalStore` by connecting
to that daemon.

This may be useful with an unprivileged nix daemon, as it would
otherwise be unable to find runtime roots from process open files
and maps.
2026-02-17 10:42:04 -05:00
John Ericson
16b0bb7548 Merge pull request #15270 from NixOS/inline-lookup-var
libexpr: Make sure `EvalState::lookupVar` is inlined
2026-02-17 15:12:00 +00:00
John Ericson
ebcd31e434 Merge pull request #15271 from NixOS/faster-type-internal-type
libexpr: Optimise `Value::type()`, `ValueStorage::getInternalType()`
2026-02-17 15:11:23 +00:00
John Ericson
f940ab5146 Merge pull request #15265 from xokdvium/libgit2-error
libfetchers/git-utils: Add GitError class for deduplicating error…
2026-02-17 15:06:31 +00:00
Sergei Zimmerman
3df91bea62 libexpr: Optimise Value::type(), ValueStorage::getInternalType()
Using nix::unreachable() in getInternalType() and type() turns
out to be quite expensive and prevents inlining. Also Value::type
got compiled to a jump table which has a high overhead from indirect
jumps. Using an explicit lookup table turns out to be more efficient.

This does mean that we lose out on nice diagnostics from nix::unreachable
calls, but this code is probably one of the hottests functions in the whole
evaluator, so I think the tradeoff is worth it. The nixUnreachableWhenHardened
boils down to nix::unreachable when UBSan is enabled so we still have good
coverage there.
2026-02-17 16:50:07 +03:00
Sergei Zimmerman
aaabe82483 libexpr: Make sure EvalState::lookupVar is inlined
This makes sure that ExprVar::eval inlines lookupVar call. In practice
this seems to reduce instruction count by ~2%, though it doesn't have
a statistically significant impact on the wall time.
2026-02-17 15:32:26 +03:00
Sergei Zimmerman
a81f83604b libexpr: Add marker values to InternalType enum
This reduces the churn when changing up the order of
values in a follow-up commit. This should have been done
from the start ideally to improve readability.
2026-02-17 13:32:45 +03:00
Sergei Zimmerman
c1bfa30303 libfetchers/git-utils: Add GitError class for deduplicating error message printing
Consolidates all the error message formatting in one place. It was very weird
and tiring to remember to call git_error_last() in all the places.
2026-02-17 12:18:37 +03:00
John Ericson
509694d5f0 Merge pull request #15267 from obsidiansystems/fix-external-builders-path
tests: quote `PATH` in external-builders test heredoc
2026-02-17 05:53:17 +00:00
Amaan Qureshi
0b7629da08 tests: quote PATH in external-builders test heredoc
The external-builders test expands `$PATH` into a heredoc without quotes,
so any `PATH` entry containing spaces causes bash to parse the line as a
command instead of an assignment, failing the test.
2026-02-16 23:20:10 -05: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
Sergei Zimmerman
e7e5eaaa37 Merge pull request #15255 from obsidiansystems/fix-repl-tab-crash
repl: catch all errors during tab completion
2026-02-16 21:58:22 +00:00
Jörg Thalheim
974545290e Merge pull request #15252 from obsidiansystems/fix-docker-compression
upload-release: disable containerd image store to preserve gzip layer compression
2026-02-16 21:26:31 +00:00
Amaan Qureshi
be6e72f11b repl: prevent exceptions from escaping editline callbacks
The tab completion handler in `completePrefix` only caught `ParseError`,
`EvalError`, `BadURL`, and `FileNotFound`. Other error types like
`JSONParseError` (which derives from `Error`, not `EvalError`) escaped
the catch block and propagated through editline's C code as undefined
behavior, crashing the REPL. This happened when tab-completing
expressions like `(builtins.fromJSON "invalid").` where evaluation
throws a non-`EvalError` exception.

This commit marks `completionCallback` and `listPossibleCallback` as
`noexcept` with function-try-blocks that catch all exceptions at the
C/C++ boundary, preventing any exception from reaching editline.

Fixes #15133.
2026-02-16 16:02:37 -05:00
Sergei Zimmerman
27782fcc42 Merge pull request #15253 from obsidiansystems/fix-url-assertion
libflake: fix assertion crash when malformed URL falls through to path scheme
2026-02-16 20:49:49 +00:00
John Ericson
06d4d5779f Merge pull request #15251 from obsidiansystems/file-system-at
Split `file-system-at.{cc,hh}` from `file-descriptor.{cc,hh}`
2026-02-16 20:10:28 +00:00
Amaan Qureshi
a32cd16f64 libflake: fix assertion crash when malformed URL falls through to path scheme
When a URL like `github:nixos/nixpkgs/nixpkgs.git?ref=<hash>` (using
`ref` instead of `rev`) failed the github input scheme, it fell
through to `parsePathFlakeRefWithFragment` which constructed a `path:`
`ParsedURL` with an empty authority but a relative path. This violated
RFC 3986 section 3.3 (authority present requires path starting with
`/`), causing an assertion failure in `renderAuthorityAndPath` when
`PathInputScheme` tried to format the URL for an error message.

This commit only sets the authority on absolute paths. Relative paths
get `std::nullopt` for authority, which is the correct representation
per the URL spec.

Fixes #15196. Fixes #14830.
2026-02-16 15:10:19 -05: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
Sergei Zimmerman
46a4a554ca Merge pull request #15237 from xokdvium/add-missing-temp-roots
Add missing temproots for cached sources and existing derivations
2026-02-16 19:35:15 +00:00
John Ericson
cc0b489967 Merge pull request #15250 from obsidiansystems/assume-lchown
Remove suppport for not having `lchown`
2026-02-16 19:29:08 +00:00
John Ericson
af7e585009 Split file-system-at.{cc,hh} from file-descriptor.{cc,hh}
`file-descriptor.{cc,hh}` was getting too big, split out
`file-system-at.{cc,hh}` for the FD-based file system stuff,
`file-descriptor.{cc,hh}` will only be for the fundamental primitives
that are file-system agnostic and work on almost all file types.

Review with `git show --color-moved` to see that this is indeed all
moving.
2026-02-16 14:21:52 -05:00
Amaan Qureshi
2ccb8a9a56 upload-release: disable containerd image store to preserve gzip layer compression
Docker 28+ defaults to the containerd image store, which pushes layers
uncompressed instead of gzip. The GHA runner image updated Docker to
29.x (actions/runner-images#13633), causing the `nixos/nix:2.33.3`
image to balloon from 138 MB to 505 MB, with all 70 layers pushed as
`application/vnd.docker.image.rootfs.diff.tar` instead of `.tar.gzip`.
OCI clients that only support gzip (e.g. `go-containerregistry`, used
by Concourse CI) fail with "gzip: invalid header".

This commit disables the containerd snapshotter in the release workflow
before any Docker operations, restoring the classic storage driver that
preserves gzip compression through the `docker load` / `docker push`
pipeline.

Fixes #15246
2026-02-16 14:08:08 -05:00
John Ericson
fefa66880a Remove suppport for not having lchown
Linux, macOS, and all 3 BSDs have it (according to man page google
search), so let's just drop this. Support for not having it was added in
d03f0d4117 in 2006, things have changed in
the last 20 years!
2026-02-16 13:40:29 -05:00
John Ericson
a53391fd0e Merge pull request #15247 from roberth/clarify-ref-upcasting
Better `ref` casting DX
2026-02-16 17:09:16 +00: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
Robert Hensing
771421a34e fix(ref): improve cast exception type and add demangled type names
When ref::cast() fails, the error message was cryptic ("null pointer
cast to ref"). Now it throws a proper bad_ref_cast (a std::bad_cast
subclass) with a clear message showing the actual types involved:

    ref<nix::Base> cannot be cast to ref<nix::Derived>

This also adds a demangle.hh utility.
2026-02-16 17:07:40 +01:00
Robert Hensing
5aaa0cc4a6 refactor(ref): clarify implicit conversion semantics with requires clause
ref<Derived> was already implicitly convertible to ref<Base>, but the
mechanism was unclear and error messages for rejected downcasts were
more cryptic than necessary. This change:

- Adds RefImplicitlyUpcastableTo concept to constrain the conversion
  operator, making the intent explicit and improving error messages
- Documents .cast() and .dynamic_pointer_cast() as alternatives for
  explicit downcasting
- Adds unit tests for covariance behavior
2026-02-16 16:43:08 +01:00
John Ericson
0749ec4e55 Merge pull request #15230 from obsidiansystems/new-wine
flake: Use Wine 11 for running mingw tests
2026-02-15 16:41:52 +00:00
Artemis Tosini
4cc97150df flake: Use Wine 11 for running mingw tests
Set wine_11 as the emulator for Windows.
2026-02-15 10:56:02 -05:00
John Ericson
2bbd1094a2 flake.lock: Update Nixpkgs
Flake lock file updates:

• Updated input 'nixpkgs':
    'https://releases.nixos.org/nixos/25.11/nixos-25.11.4506.078d69f03934/nixexprs.tar.xz?narHash=sha256-Xu%2B7iYcAuOvsI2wdkUcIEmkqEJbvvE6n7qR9QNjJyP4%3D' (2026-01-22)
  → 'https://releases.nixos.org/nixos/25.11/nixos-25.11.5960.3aadb7ca9eac/nixexprs.tar.xz?narHash=sha256-WoiezqWJQ3OHILah%2Bp6rzNXdJceEAmAhyDFZFZ6pZzY%3D' (2026-02-14)

This will be needed to get Wine 11.
2026-02-15 10:53:15 -05:00
John Ericson
95251a51dd Merge pull request #15241 from obsidiansystems/fix-isindir
libutil: fix `isInDir` rejecting paths starting with dot
2026-02-15 15:52:37 +00: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
02d9f4ecb4 Merge pull request #15239 from xokdvium/fix-warnings-no-intereference-size
meson: Only enable -Wno-interference-size with GCC
2026-02-15 15:06:54 +00:00
John Ericson
3269c71e9d Merge pull request #15240 from xokdvium/fix-mtls-redirect-test
libstore-tests: Fix mTLS test for redirect, correctly propagate tries
2026-02-15 15:04:50 +00:00
Amaan Qureshi
ad0055e67c libutil: fix isInDir rejecting paths starting with dot
The old check rejected any relative path whose first character was a
dot, producing false negatives for valid descendants like `.ssh` or
`.config`. This commit changes the logic such that now it inspects the
first path component via `path::begin()`, only rejects `.` and `..`
rather than anything dot-prefixed. Fixes #15207.
2026-02-15 10:04:08 -05:00
Sergei Zimmerman
d3d63a4b5b libstore-tests: Fix mTLS test for redirect, correctly propagate tries
The fake cacert didn't have subjectAltName for 127.0.0.1, so the test
was failing for a different reason. Also `tries` setting wasn't being respected.
There's no callsite specifying it in the request, so just use the one specified
in the FileTransferSettings and remove the fields from the FileTransferRequest.
2026-02-15 00:08:21 +03:00
Sergei Zimmerman
6a5ee08737 meson: Only enable -Wno-interference-size with GCC
Clang doesn't recognise this option.
2026-02-14 23:42:28 +03:00
Sergei Zimmerman
ac2dd58b6f Add missing temproots for cached sources and existing derivations 2026-02-14 12:09:24 +03: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
732 changed files with 15936 additions and 9389 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,69 +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: 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,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": 1769089682,
"narHash": "sha256-Xu+7iYcAuOvsI2wdkUcIEmkqEJbvvE6n7qR9QNjJyP4=",
"rev": "078d69f03934859a181e81ba987c2bb033eebfc5",
"lastModified": 1771903837,
"narHash": "sha256-jEA8WggGKtMFeNeCKq3NK8cLEjJmG6/RLUElYYbBZ0E=",
"rev": "e764fc9a405871f1f6ca3d1394fb422e0a0c3951",
"type": "tarball",
"url": "https://releases.nixos.org/nixos/25.11/nixos-25.11.4506.078d69f03934/nixexprs.tar.xz"
"url": "https://releases.nixos.org/nixos/25.11/nixos-25.11.6495.e764fc9a4058/nixexprs.tar.xz"
},
"original": {
"type": "tarball",

View File

@@ -115,6 +115,9 @@
}
// lib.optionalAttrs (crossSystem == "x86_64-unknown-freebsd13") {
useLLVM = true;
}
// lib.optionalAttrs (crossSystem == "x86_64-w64-mingw32") {
emulator = pkgs: "${pkgs.buildPackages.wineWow64Packages.stable_11}/bin/wine";
};
overlays = [
(overlayFor (pkgs: pkgs.${stdenv}))
@@ -406,7 +409,9 @@
"nix-cmd" = { };
"nix-nswrapper" = { };
"nix-nswrapper" = {
linuxOnly = true;
};
"nix-cli" = { };
@@ -428,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

@@ -27,13 +27,19 @@ add_project_arguments(
'-Wignored-qualifiers',
'-Wimplicit-fallthrough',
'-Wno-deprecated-declarations',
'-Wno-interference-size', # Used for C++ ABI only. We don't provide any guarantees about different march tunings.
language : 'cpp',
)
# GCC doesn't benefit much from precompiled headers.
do_pch = cxx.get_id() == 'clang'
if cxx.get_id() == 'gcc'
add_project_arguments(
'-Wno-interference-size', # Used for C++ ABI only. We don't provide any guarantees about different march tunings.
language : 'cpp',
)
endif
# This is a clang-only option for improving build times.
# It forces the instantiation of templates in the PCH itself and
# not every translation unit it's included in.

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

@@ -4,7 +4,6 @@
#include "nix/cmd/command.hh"
#include "nix/cmd/legacy.hh"
#include "nix/cmd/markdown.hh"
#include "nix/main/shared.hh"
#include "nix/store/globals.hh"
#include "nix/store/store-open.hh"
#include "nix/store/local-fs-store.hh"
@@ -76,7 +75,7 @@ ref<StoreConfig> StoreConfigCommand::getStoreConfig()
ref<StoreConfig> StoreConfigCommand::createStoreConfig()
{
return resolveStoreConfig(settings, StoreReference{settings.storeUri.get()});
return resolveStoreConfig(StoreReference{settings.storeUri.get()});
}
void StoreConfigCommand::run()
@@ -130,7 +129,7 @@ CopyCommand::CopyCommand()
ref<StoreConfig> CopyCommand::createStoreConfig()
{
return !srcUri ? StoreCommand::createStoreConfig() : resolveStoreConfig(settings, StoreReference{*srcUri});
return !srcUri ? StoreCommand::createStoreConfig() : resolveStoreConfig(StoreReference{*srcUri});
}
ref<Store> CopyCommand::getDstStore()
@@ -138,7 +137,7 @@ ref<Store> CopyCommand::getDstStore()
if (!srcUri && !dstUri)
throw UsageError("you must pass '--from' and/or '--to'");
return !dstUri ? openStore(settings) : openStore(settings, StoreReference{*dstUri});
return !dstUri ? openStore() : openStore(StoreReference{*dstUri});
}
EvalCommand::EvalCommand()
@@ -160,7 +159,7 @@ EvalCommand::~EvalCommand()
ref<Store> EvalCommand::getEvalStore()
{
if (!evalStore)
evalStore = evalStoreUrl ? openStore(settings, StoreReference{*evalStoreUrl}) : getStore();
evalStore = evalStoreUrl ? openStore(StoreReference{*evalStoreUrl}) : getStore();
return ref<Store>(evalStore);
}

View File

@@ -19,12 +19,12 @@
namespace nix {
fetchers::Settings fetchSettings{settings};
fetchers::Settings fetchSettings;
static GlobalConfig::Register rFetchSettings(&fetchSettings);
EvalSettings evalSettings{
settings,
settings.readOnlyMode,
{
{
"flake",
@@ -135,7 +135,7 @@ MixEvalArgs::MixEvalArgs()
fetchers::overrideRegistry(from.input, to.input, extraAttrs);
}},
.completer = {[&](AddCompletions & completions, size_t, std::string_view prefix) {
completeFlakeRef(completions, openStore(settings), prefix);
completeFlakeRef(completions, openStore(), prefix);
}},
});

View File

@@ -4,9 +4,9 @@
namespace nix {
std::string fetchBuildLog(Settings & settings, ref<Store> store, const StorePath & path, std::string_view what)
std::string fetchBuildLog(ref<Store> store, const StorePath & path, std::string_view what)
{
auto subs = getDefaultSubstituters(settings);
auto subs = getDefaultSubstituters();
subs.push_front(store);

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

@@ -8,8 +8,6 @@
namespace nix {
class Settings;
/**
* Fetch the build log for a store path, searching the store and its
* substituters.
@@ -20,6 +18,6 @@ class Settings;
* @return The build log content.
* @throws Error if the build log is not available.
*/
std::string fetchBuildLog(Settings & settings, ref<Store> store, const StorePath & path, std::string_view what);
std::string fetchBuildLog(ref<Store> store, const StorePath & path, std::string_view what);
} // namespace nix

View File

@@ -1,6 +1,7 @@
#pragma once
///@file
#include "nix/store/globals.hh"
#include "nix/cmd/installable-value.hh"
#include "nix/store/outputs-spec.hh"
#include "nix/cmd/command.hh"

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

@@ -40,8 +40,8 @@ void sigintHandler(int signo)
static detail::ReplCompleterMixin * curRepl; // ugly
#if !USE_READLINE
static char * completionCallback(char * s, int * match)
{
static char * completionCallback(char * s, int * match) noexcept
try {
auto possible = curRepl->completePrefix(s);
if (possible.size() == 1) {
*match = 1;
@@ -73,10 +73,12 @@ static char * completionCallback(char * s, int * match)
*match = 0;
return nullptr;
} catch (...) {
return nullptr;
}
static int listPossibleCallback(char * s, char *** avp)
{
static int listPossibleCallback(char * s, char *** avp) noexcept
try {
auto possible = curRepl->completePrefix(s);
if (possible.size() > (std::numeric_limits<int>::max() / sizeof(char *)))
@@ -105,6 +107,9 @@ static int listPossibleCallback(char * s, char *** avp)
*avp = vp;
return ac;
} catch (...) {
*avp = nullptr;
return 0;
}
#endif
@@ -113,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; });
@@ -203,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,15 +546,15 @@ 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; });
RunPager pager;
auto log = fetchBuildLog(settings, state->store, drvPath, drvPathRaw);
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();
@@ -710,7 +704,7 @@ void NixRepl::loadFlake(const std::string & flakeRefS)
try {
cwd = std::filesystem::current_path();
} catch (std::filesystem::filesystem_error & e) {
throw SysError("cannot determine current working directory");
throw SystemError(e.code(), "cannot determine current working directory");
}
auto flakeRef = parseFlakeRef(fetchSettings, flakeRefS, cwd.string(), true);
@@ -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

@@ -132,8 +132,9 @@ nix_eval_state_builder * nix_eval_state_builder_new(nix_c_context * context, Sto
return unsafe_new_with_self<nix_eval_state_builder>([&](auto * self) {
return nix_eval_state_builder{
.store = nix::ref<nix::Store>(store->ptr),
.settings = nix::EvalSettings{cStoreSettings},
.fetchSettings = nix::fetchers::Settings{cStoreSettings},
.settings = nix::EvalSettings{/* &bool */ self->readOnlyMode},
.fetchSettings = nix::fetchers::Settings{},
.readOnlyMode = true,
};
});
}
@@ -152,7 +153,8 @@ nix_err nix_eval_state_builder_load(nix_c_context * context, nix_eval_state_buil
if (context)
context->last_err_code = NIX_OK;
try {
loadConfFile(cStoreSettings);
// TODO: load in one go?
builder->settings.readOnlyMode = nix::settings.readOnlyMode;
loadConfFile(builder->settings);
loadConfFile(builder->fetchSettings);
}
@@ -179,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"
@@ -16,13 +18,17 @@ struct nix_eval_state_builder
nix::EvalSettings settings;
nix::fetchers::Settings fetchSettings;
nix::LookupPath lookupPath;
// TODO: make an EvalSettings setting own this instead?
bool readOnlyMode;
};
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

@@ -26,18 +26,18 @@ public:
}
protected:
LibExprTest(auto && makeEvalSettings, auto &&... args)
: LibStoreTest(args...)
, evalSettings(makeEvalSettings(settings))
LibExprTest(ref<Store> store, auto && makeEvalSettings)
: LibStoreTest()
, evalSettings(makeEvalSettings(readOnlyMode))
, state({}, store, fetchSettings, evalSettings, nullptr)
{
}
LibExprTest()
: LibExprTest([](Settings & settings) {
EvalSettings evalSettings{settings};
evalSettings.nixPath = {};
return evalSettings;
: LibExprTest(openStore("dummy://"), [](bool & readOnlyMode) {
EvalSettings settings{readOnlyMode};
settings.nixPath = {};
return settings;
})
{
}
@@ -66,8 +66,8 @@ protected:
}
bool readOnlyMode = true;
fetchers::Settings fetchSettings{settings};
EvalSettings evalSettings{settings};
fetchers::Settings fetchSettings{};
EvalSettings evalSettings{readOnlyMode};
EvalState state;
};

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

@@ -179,15 +179,12 @@ class PureEvalTest : public LibExprTest
{
public:
PureEvalTest()
: LibExprTest{
[](auto & settings) {
EvalSettings evalSettings{settings};
evalSettings.pureEval = true;
evalSettings.restrictEval = true;
return evalSettings;
},
[](auto & settings) { return openStore(settings, "dummy://", {{"read-only", "false"}}); },
}
: LibExprTest(openStore("dummy://", {{"read-only", "false"}}), [](bool & readOnlyMode) {
EvalSettings settings{readOnlyMode};
settings.pureEval = true;
settings.restrictEval = true;
return settings;
})
{
}
};

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);

View File

@@ -13,8 +13,7 @@ TEST_F(ValueTest, unsetValue)
{
Value unsetValue;
ASSERT_EQ(false, unsetValue.isValid());
ASSERT_EQ(nThunk, unsetValue.type(true));
ASSERT_DEATH(unsetValue.type(), "");
ASSERT_EQ(nThunk, unsetValue.type</*invalidIsThunk=*/true>());
}
TEST_F(ValueTest, vInt)

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)
{
@@ -63,19 +63,19 @@ struct AttrDb
SymbolTable & symbols;
AttrDb(bool useSQLiteWAL, const StoreDirConfig & cfg, const Hash & fingerprint, SymbolTable & symbols)
AttrDb(const StoreDirConfig & cfg, const Hash & fingerprint, SymbolTable & symbols)
: cfg(cfg)
, _state(std::make_unique<Sync<State>>())
, symbols(symbols)
{
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");
state->db = SQLite(dbPath, {.useWAL = useSQLiteWAL});
state->db = SQLite(dbPath, {.useWAL = settings.useSQLiteWAL});
state->db.isCache();
state->db.exec(schema);
@@ -287,11 +287,10 @@ struct AttrDb
}
};
static std::shared_ptr<AttrDb>
makeAttrDb(bool useSQLiteWAL, const StoreDirConfig & cfg, const Hash & fingerprint, SymbolTable & symbols)
static std::shared_ptr<AttrDb> makeAttrDb(const StoreDirConfig & cfg, const Hash & fingerprint, SymbolTable & symbols)
{
try {
return std::make_shared<AttrDb>(useSQLiteWAL, cfg, fingerprint, symbols);
return std::make_shared<AttrDb>(cfg, fingerprint, symbols);
} catch (SQLiteError &) {
ignoreExceptionExceptInterrupt();
return nullptr;
@@ -300,8 +299,7 @@ makeAttrDb(bool useSQLiteWAL, const StoreDirConfig & cfg, const Hash & fingerpri
EvalCache::EvalCache(
std::optional<std::reference_wrapper<const Hash>> useCache, EvalState & state, RootLoader rootLoader)
: db(useCache ? makeAttrDb(state.store->config.settings.useSQLiteWAL, *state.store, *useCache, state.symbols)
: nullptr)
: db(useCache ? makeAttrDb(*state.store, *useCache, state.symbols) : nullptr)
, state(state)
, rootLoader(rootLoader)
{
@@ -709,7 +707,7 @@ StorePath AttrCursor::forceDerivation()
auto aDrvPath = getAttr(root->state.s.drvPath);
auto drvPath = root->state.store->parseStorePath(aDrvPath->getString());
drvPath.requireDerivation();
if (!root->state.store->isValidPath(drvPath) && !root->state.store->config.settings.readOnlyMode) {
if (!root->state.store->isValidPath(drvPath) && !settings.readOnlyMode) {
/* The eval cache contains 'drvPath', but the actual path has
been garbage-collected. So force it to be regenerated. */
aDrvPath->forceValue();

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)
@@ -48,8 +69,8 @@ Strings EvalSettings::parseNixPath(const std::string & s)
return res;
}
EvalSettings::EvalSettings(nix::Settings & storeSettings, EvalSettings::LookupPathHooks lookupPathHooks)
: storeSettings{storeSettings}
EvalSettings::EvalSettings(bool & readOnlyMode, EvalSettings::LookupPathHooks lookupPathHooks)
: readOnlyMode{readOnlyMode}
, lookupPathHooks{lookupPathHooks}
{
auto var = getEnv("NIX_ABORT_ON_WARN");
@@ -57,7 +78,7 @@ EvalSettings::EvalSettings(nix::Settings & storeSettings, EvalSettings::LookupPa
builtinsAbortOnWarn = true;
}
Strings EvalSettings::getDefaultNixPath(nix::Settings & settings)
Strings EvalSettings::getDefaultNixPath()
{
Strings res;
auto add = [&](const std::filesystem::path & p, const std::string & s = std::string()) {
@@ -70,7 +91,7 @@ Strings EvalSettings::getDefaultNixPath(nix::Settings & settings)
}
};
add(std::filesystem::path{getNixDefExpr(settings)} / "channels");
add(getNixDefExpr() / "channels");
auto profilesDirOpts = settings.getProfileDirsOptions();
add(rootChannelsDir(profilesDirOpts) / "nixpkgs", "nixpkgs");
add(rootChannelsDir(profilesDirOpts));
@@ -101,10 +122,10 @@ std::string EvalSettings::resolvePseudoUrl(std::string_view url)
const std::string & EvalSettings::getCurrentSystem() const
{
const auto & evalSystem = currentSystem.get();
return evalSystem != "" ? evalSystem : storeSettings.thisSystem.get();
return evalSystem != "" ? evalSystem : settings.thisSystem.get();
}
std::filesystem::path getNixDefExpr(const Settings & settings)
std::filesystem::path getNixDefExpr()
{
return settings.useXDGBaseDirectories ? getStateDir() / "defexpr" : getHome() / ".nix-defexpr";
}

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"
@@ -15,7 +16,6 @@
#include "nix/store/filetransfer.hh"
#include "nix/expr/function-trace.hh"
#include "nix/store/profiles.hh"
#include "nix/store/globals.hh"
#include "nix/expr/print.hh"
#include "nix/fetchers/filtering-source-accessor.hh"
#include "nix/util/memory-source-accessor.hh"
@@ -32,6 +32,7 @@
#include <algorithm>
#include <cstddef>
#include <cstdlib>
#include <exception>
#include <iostream>
#include <sstream>
#include <cstring>
@@ -156,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();
}
@@ -225,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();
}
@@ -331,7 +330,7 @@ EvalState::EvalState(
lookupPath.elements.emplace_back(LookupPath::Elem::parse(i));
}
if (!settings.restrictEval) {
for (auto & i : EvalSettings::getDefaultNixPath(store->config.settings)) {
for (auto & i : EvalSettings::getDefaultNixPath()) {
lookupPath.elements.emplace_back(LookupPath::Elem::parse(i));
}
}
@@ -365,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));
@@ -423,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)
@@ -467,7 +473,7 @@ void EvalState::addConstant(const std::string & name, Value * v, Constant info)
We might know the type of a thunk in advance, so be allowed
to just write it down in that case. */
if (auto gotType = v->type(true); gotType != nThunk)
if (auto gotType = v->type</*invalidIsThunk=*/true>(); gotType != nThunk)
assert(info.type == gotType);
/* Install value the base environment. */
@@ -886,7 +892,7 @@ void Value::mkPath(const SourcePath & path, EvalMemory & mem)
mkPath(&*path.accessor, StringData::make(mem, path.path.abs()));
}
inline Value * EvalState::lookupVar(Env * env, const ExprVar & var, bool noEval)
[[gnu::always_inline]] inline Value * EvalState::lookupVar(Env * env, const ExprVar & var, bool noEval)
{
for (auto l = var.level; l; --l, env = env->up)
;
@@ -904,11 +910,11 @@ inline Value * EvalState::lookupVar(Env * env, const ExprVar & var, bool noEval)
while (1) {
forceAttrs(*env->values[0], fromWith->pos, "while evaluating the first subexpression of a with expression");
if (auto j = env->values[0]->attrs()->get(var.name)) {
if (countCalls)
if (countCalls) [[unlikely]]
attrSelects[j->pos]++;
return j->value;
}
if (!fromWith->parentWith)
if (!fromWith->parentWith) [[unlikely]]
error<UndefinedVarError>("undefined variable '%1%'", symbols[var.name])
.atPos(var.pos)
.withFrame(*env, var)
@@ -1654,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);
@@ -1704,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);
@@ -1988,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)
@@ -2175,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())
@@ -2183,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 (...) {
}
}
@@ -2356,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();
}
@@ -2509,7 +2569,7 @@ StorePath EvalState::copyPathToStore(NixStringContext & context, const SourcePat
fetchSettings,
*store,
path.resolveSymlinks(SymlinkResolution::Ancestors),
settings.storeSettings.readOnlyMode ? FetchMode::DryRun : FetchMode::Copy,
settings.readOnlyMode ? FetchMode::DryRun : FetchMode::Copy,
path.baseName(),
ContentAddressMethod::Raw::NixArchive,
nullptr,
@@ -2822,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),
@@ -2917,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)
@@ -3139,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
@@ -3280,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"
@@ -9,15 +10,39 @@ namespace nix {
class EvalState;
struct PrimOp;
class Settings;
/**
* 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
{
/**
* Reference to the "parent" store-layer settings.
*/
nix::Settings & storeSettings;
/**
* Function used to interpret look path entries of a given scheme.
*
@@ -42,11 +67,13 @@ 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(nix::Settings & settings, LookupPathHooks lookupPathHooks = {});
EvalSettings(bool & readOnlyMode, LookupPathHooks lookupPathHooks = {});
static Strings getDefaultNixPath(nix::Settings & settings);
bool & readOnlyMode;
static Strings getDefaultNixPath();
static bool isPseudoUrl(std::string_view s);
@@ -241,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",
@@ -332,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,
@@ -370,6 +471,6 @@ struct EvalSettings : Config
/**
* Conventionally part of the default nix path in impure mode.
*/
std::filesystem::path getNixDefExpr(const Settings & settings);
std::filesystem::path getNixDefExpr();
} // namespace nix

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