Compare commits

..

2 Commits

Author SHA1 Message Date
Sergei Zimmerman
6dfeff04c1 local-binary-cache-store: Check that paths don't escape the binary cache directory
Previously arguments to getFile, upsertFile weren't checked
to be inside the root directory. It's not a very big issue since
substituters/stores are already a trusted component and can't be
specified without being a trusted user. Still, it's nice to validate
the necessary preconditions. Also changes the binaryCacheDir to be a
std::filesystem::path. Note the gotcha with absolute paths and operator/.
2026-02-24 19:53:09 +03:00
Sergei Zimmerman
3d9174cec5 libutil: Accept std::filesystem::path in readFile 2026-02-24 19:53:08 +03:00
646 changed files with 7473 additions and 12634 deletions

37
.github/workflows/backport.yml vendored Normal file
View File

@@ -0,0 +1,37 @@
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@01619ebc9a6e3f6820274221b9956b3e7365000a # v4.1.0
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}.

252
.github/workflows/ci.yml vendored Normal file
View File

@@ -0,0 +1,252 @@
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@2126ae7fc54c9df00dd18f7f18754393182c73cd # v31.9.1
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@2126ae7fc54c9df00dd18f7f18754393182c73cd # v31.9.1
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

24
.github/workflows/labels.yml vendored Normal file
View File

@@ -0,0 +1,24 @@
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

80
.github/workflows/upload-release.yml vendored Normal file
View File

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

4
.gitignore vendored
View File

@@ -16,10 +16,6 @@ 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.35.0
2.34.0

View File

@@ -1,30 +0,0 @@
{
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,6 +1,5 @@
{
lib,
stdenv,
callPackage,
mkMesonDerivation,
runCommand,
@@ -94,11 +93,10 @@ mkMesonDerivation (finalAttrs: {
mdbook
json-schema-for-humans
]
++ lib.optionals (!officialRelease && buildHtmlManual && !stdenv.hostPlatform.isi686) [
++ lib.optionals (!officialRelease && buildHtmlManual) [
# 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

@@ -1,81 +0,0 @@
---
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

@@ -0,0 +1,9 @@
---
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,23 @@
---
synopsis: "C API: Errors returned from your primops are not treated as recoverable by default"
prs: [15286, 13930]
---
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);
```

View File

@@ -1,10 +0,0 @@
---
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

@@ -1,7 +0,0 @@
---
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

@@ -0,0 +1,10 @@
---
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

@@ -0,0 +1,15 @@
---
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

@@ -0,0 +1,6 @@
---
synopsis: New setting `narinfo-cache-meta-ttl`
prs: [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.

View File

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

View File

@@ -0,0 +1,32 @@
---
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,7 +125,6 @@
- [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)
@@ -154,7 +153,6 @@
- [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 derivations, collectively called [realisation][realise].
There are multiple ways of obtaining store objects from store derivatons, collectively called [realisation][realise].
[derivation]: #gloss-derivation

View File

@@ -16,29 +16,30 @@ If you are on Linux with systemd:
sudo systemctl daemon-reload
```
2. Remove files created by Nix:
Remove files created by 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
```
```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
```
3. Remove build users and their group:
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
```
4. There may also be references to Nix in
- `/etc/bash.bashrc`
- `/etc/bashrc`
- `/etc/profile`
- `/etc/zsh/zshrc`
- `/etc/zshrc`
There may also be references to Nix in
which you may remove.
- `/etc/bash.bashrc`
- `/etc/bashrc`
- `/etc/profile`
- `/etc/zsh/zshrc`
- `/etc/zshrc`
which you may remove.
### FreeBSD
@@ -53,7 +54,7 @@ If you are on Linux with systemd:
2. Remove files created by Nix:
```console
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
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
```
3. Remove build users and their group:
@@ -153,7 +154,7 @@ If you are on Linux with systemd:
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 ~/.local/share/nix ~/.local/state/nix ~/.cache/nix
sudo rm -rf /etc/nix /var/root/.nix-profile /var/root/.nix-defexpr /var/root/.nix-channels ~/.nix-profile ~/.nix-defexpr ~/.nix-channels
```
@@ -191,6 +192,6 @@ If you are on Linux with systemd:
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 ~/.local/share/nix ~/.local/state/nix ~/.cache/nix
rm -rf /nix ~/.nix-channels ~/.nix-defexpr ~/.nix-profile
```
You might also want to manually remove references to Nix from your `~/.profile`.

View File

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

View File

@@ -13,12 +13,11 @@ schemas = [
'hash-v1',
'content-address-v1',
'store-path-v1',
'signature-v2',
'store-object-info-v3',
'store-object-info-v2',
'derivation-v4',
'derivation-options-v1',
'deriving-path-v1',
'build-trace-entry-v3',
'build-trace-entry-v2',
'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-v3.yaml#/$defs/value"
"$ref": "build-trace-entry-v2.yaml"
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-v3.json"
"$id": "https://nix.dev/manual/nix/latest/protocols/json/schema/build-trace-entry-v2.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.
## Version History
Verision history:
- Version 1: Original format
- 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)
- Version 2: Remove `dependentRealisations`
type: object
required:
- key
- value
- id
- outPath
- signatures
allOf:
- "$ref": "#/$defs/key"
- "$ref": "#/$defs/value"
properties:
key:
"$ref": "#/$defs/key"
value:
"$ref": "#/$defs/value"
additionalProperties: false
id: {}
outPath: {}
signatures: {}
additionalProperties:
dependentRealisations:
description: deprecated field
type: object
"$defs":
key:
@@ -43,20 +43,23 @@ additionalProperties: false
This is the "key" part, refering to a derivation and output.
type: object
required:
- drvPath
- outputName
- id
properties:
drvPath:
"$ref": "store-path-v1.yaml"
title: Derivation Path
description: |
The store path of the derivation that was built.
outputName:
id:
type: string
title: Output Name
title: Derivation Output ID
pattern: "^sha256:[0-9a-f]{64}![a-zA-Z_][a-zA-Z0-9_-]*$"
description: |
Name of the specific output (e.g., "out", "dev", "doc")
additionalProperties: false
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"`
value:
title: Build Trace Value
@@ -74,10 +77,19 @@ additionalProperties: false
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:
"$ref": "signature-v2.yaml"
type: string
title: Signature
description: A single cryptographic signature

View File

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

View File

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

View File

@@ -1,33 +0,0 @@
"$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

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

View File

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

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

View File

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

View File

@@ -37,7 +37,7 @@ properties:
- contents
properties:
info:
"$ref": "./store-object-info-v3.yaml#/$defs/impure"
"$ref": "./store-object-info-v2.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-v3.yaml#/$defs/value"
"$ref": "./build-trace-entry-v2.yaml#/$defs/value"
additionalProperties: false
"$defs":

View File

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

View File

@@ -1,352 +0,0 @@
# 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)

8
flake.lock generated
View File

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

View File

@@ -409,9 +409,7 @@
"nix-cmd" = { };
"nix-nswrapper" = {
linuxOnly = true;
};
"nix-nswrapper" = { };
"nix-cli" = { };
@@ -433,37 +431,32 @@
pkgName:
{
supportsCross ? true,
linuxOnly ? false,
}:
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};
}
)
)
{
# 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 supportsCross (
flatMapAttrs (lib.genAttrs crossSystems (_: { })) (
crossSystem:
{ }:
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};
}
{
# 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,11 +95,6 @@
''^tests/functional/lang/eval-fail-bad-string-interpolation-3\.nix$''
''^tests/functional/lang/eval-fail-bad-string-interpolation-4\.nix$''
''^tests/functional/lang/eval-okay-regex-match2\.nix$''
# URL literal tests - nixfmt converts unquoted URLs to strings
''^tests/functional/lang/eval-fail-url-literal\.nix$''
''^tests/functional/lang/eval-okay-url-literal-warn\.nix$''
''^tests/functional/lang/eval-okay-url-literal-default\.nix$''
];
};
clang-format = {

View File

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

View File

@@ -30,19 +30,9 @@ scope: {
NIX_CFLAGS_COMPILE = "-DINITIAL_MARK_STACK_SIZE=1048576";
});
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;
};
curl = pkgs.curl.override {
http3Support = !pkgs.stdenv.hostPlatform.isWindows;
};
libblake3 = pkgs.libblake3.override {
useTBB = !(stdenv.hostPlatform.isWindows || stdenv.hostPlatform.isStatic);

View File

@@ -115,11 +115,7 @@ rec {
# Binary package for various platforms.
build = forAllPackages (
pkgName:
lib.filterAttrs (
system: _do_not_touch:
pkgName == "nix-nswrapper" -> nixpkgsFor.${system}.native.stdenv.hostPlatform.isLinux
) (forAllSystems (system: nixpkgsFor.${system}.native.nixComponents2.${pkgName}))
pkgName: forAllSystems (system: nixpkgsFor.${system}.native.nixComponents2.${pkgName})
);
shellInputs = removeAttrs (forAllSystems (
@@ -139,10 +135,6 @@ 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
)
@@ -179,13 +171,7 @@ rec {
)
);
in
forAllPackages (
pkgName:
lib.filterAttrs (
system: _do_not_touch:
pkgName == "nix-nswrapper" -> nixpkgsFor.${system}.native.stdenv.hostPlatform.isLinux
) (forAllSystems (system: components.${system}.${pkgName}))
);
forAllPackages (pkgName: forAllSystems (system: components.${system}.${pkgName}));
buildNoTests = forAllSystems (system: nixpkgsFor.${system}.native.nixComponents2.nix-cli);
@@ -205,13 +191,7 @@ rec {
)
);
in
forAllPackages (
pkgName:
lib.filterAttrs (
system: _do_not_touch:
pkgName == "nix-nswrapper" -> nixpkgsFor.${system}.native.stdenv.hostPlatform.isLinux
) (forAllSystems (system: components.${system}.${pkgName}))
);
forAllPackages (pkgName: forAllSystems (system: components.${system}.${pkgName}));
# Perl bindings for various platforms.
perlBindings = forAllSystems (system: nixpkgsFor.${system}.native.nixComponents2.nix-perl-bindings);

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -51,13 +51,6 @@ schemas = [
'simple.json',
],
},
{
'stem' : 'signature',
'schema' : schema_dir / 'signature-v2.yaml',
'files' : [
'simple.json',
],
},
{
'stem' : 'deriving-path',
'schema' : schema_dir / 'deriving-path-v1.yaml',
@@ -69,10 +62,13 @@ schemas = [
},
{
'stem' : 'build-trace-entry',
'schema' : schema_dir / 'build-trace-entry-v3.yaml',
'schema' : schema_dir / 'build-trace-entry-v2.yaml',
'files' : [
'simple.json',
'with-structured-signature.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',
],
},
{
@@ -158,20 +154,20 @@ schemas += [
# Match overall
{
'stem' : 'store-object-info',
'schema' : schema_dir / 'store-object-info-v3.yaml',
'schema' : schema_dir / 'store-object-info-v2.yaml',
'files' : [
'json-3' / 'pure.json',
'json-3' / 'impure.json',
'json-3' / 'empty_pure.json',
'json-3' / 'empty_impure.json',
'json-2' / 'pure.json',
'json-2' / 'impure.json',
'json-2' / 'empty_pure.json',
'json-2' / 'empty_impure.json',
],
},
{
'stem' : 'nar-info',
'schema' : schema_dir / 'store-object-info-v3.yaml',
'schema' : schema_dir / 'store-object-info-v2.yaml',
'files' : [
'json-3' / 'pure.json',
'json-3' / 'impure.json',
'json-2' / 'pure.json',
'json-2' / 'impure.json',
],
},
{
@@ -186,32 +182,32 @@ schemas += [
# Match exact variant
{
'stem' : 'store-object-info',
'schema' : schema_dir / 'store-object-info-v3.yaml#/$defs/base',
'schema' : schema_dir / 'store-object-info-v2.yaml#/$defs/base',
'files' : [
'json-3' / 'pure.json',
'json-3' / 'empty_pure.json',
'json-2' / 'pure.json',
'json-2' / 'empty_pure.json',
],
},
{
'stem' : 'store-object-info',
'schema' : schema_dir / 'store-object-info-v3.yaml#/$defs/impure',
'schema' : schema_dir / 'store-object-info-v2.yaml#/$defs/impure',
'files' : [
'json-3' / 'impure.json',
'json-3' / 'empty_impure.json',
'json-2' / 'impure.json',
'json-2' / 'empty_impure.json',
],
},
{
'stem' : 'nar-info',
'schema' : schema_dir / 'store-object-info-v3.yaml#/$defs/base',
'schema' : schema_dir / 'store-object-info-v2.yaml#/$defs/base',
'files' : [
'json-3' / 'pure.json',
'json-2' / 'pure.json',
],
},
{
'stem' : 'nar-info',
'schema' : schema_dir / 'store-object-info-v3.yaml#/$defs/narInfo',
'schema' : schema_dir / 'store-object-info-v2.yaml#/$defs/narInfo',
'files' : [
'json-3' / 'impure.json',
'json-2' / 'impure.json',
],
},
]

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -108,16 +108,20 @@ 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)) {
DrvOutput key{
.drvPath = p.drvPath->outPath(),
.outputName = outputName,
};
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};
auto thisRealisation = store.queryRealisation(key);
// Weve built it, so we must have the realisation.
assert(thisRealisation);
res.insert(Realisation{*thisRealisation, key});
assert(thisRealisation); // Weve built it, so we must
// have the realisation
res.insert(Realisation{*thisRealisation, std::move(key)});
} else {
res.insert(outputPath);
}

View File

@@ -300,11 +300,11 @@ struct StorePathCommand : public StorePathsCommand
*/
struct RegisterCommand
{
typedef std::map<std::vector<std::string>, fun<ref<Command>()>> Commands;
typedef std::map<std::vector<std::string>, std::function<ref<Command>()>> Commands;
static Commands & commands();
RegisterCommand(std::vector<std::string> && name, fun<ref<Command>()> command)
RegisterCommand(std::vector<std::string> && name, std::function<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. */
std::filesystem::path outLink;
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, std::filesystem::path{}},
.handler = {&outLink, Path("")},
});
}
};

View File

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

View File

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

View File

@@ -2,7 +2,6 @@
///@file
#include "nix/expr/eval.hh"
#include "nix/util/os-string.hh"
namespace nix {
@@ -28,7 +27,8 @@ 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, OsStrings args, const std::optional<std::string> & input);
using RunNix =
void(const std::string & programName, const Strings & 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,
fun<AnnotatedValues()> getValues,
std::function<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 = fun<void(AutoCloseFD socket, std::function<void()> closeListeners)>;
using UnixSocketHandler = std::function<void(AutoCloseFD socket, std::function<void()> closeListeners)>;
/**
* Options for the serve loop.
@@ -53,19 +53,6 @@ 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).string())};
lockFlags.referenceLockFilePath = {getFSSourceAccessor(), CanonPath(absPath(lockFilePath))};
}},
.completer = completePath,
});
@@ -400,7 +400,7 @@ void completeFlakeRefWithFragment(
}
}
} catch (Error & e) {
logWarning(e.info());
warn(e.msg());
}
}

View File

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

View File

@@ -1,7 +1,6 @@
#include <iostream>
#include <cstdlib>
#include <cstring>
#include <memory>
#include "nix/util/error.hh"
#include "nix/cmd/repl-interacter.hh"
@@ -29,8 +28,6 @@
#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 {
@@ -63,7 +60,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;
fun<AnnotatedValues()> getValues;
std::function<AnnotatedValues()> getValues;
const static int envSize = 32768;
std::shared_ptr<StaticEnv> staticEnv;
@@ -74,11 +71,15 @@ struct NixRepl : AbstractNixRepl, detail::ReplCompleterMixin, gc
RunNix * runNixPtr;
void runNix(const std::string & program, OsStrings args, const std::optional<std::string> & input = {});
void runNix(const std::string & program, const Strings & args, const std::optional<std::string> & input = {});
std::unique_ptr<ReplInteracter> interacter;
NixRepl(const LookupPath & lookupPath, ref<EvalState> state, fun<AnnotatedValues()> getValues, RunNix * runNix);
NixRepl(
const LookupPath & lookupPath,
ref<EvalState> state,
std::function<AnnotatedValues()> getValues,
RunNix * runNix);
virtual ~NixRepl() = default;
ReplExitStatus mainLoop() override;
@@ -97,7 +98,6 @@ 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,13 +130,16 @@ std::string removeWhitespace(std::string s)
}
NixRepl::NixRepl(
const LookupPath & lookupPath, ref<EvalState> state, fun<NixRepl::AnnotatedValues()> getValues, RunNix * runNix)
const LookupPath & lookupPath,
ref<EvalState> state,
std::function<NixRepl::AnnotatedValues()> getValues,
RunNix * runNix)
: AbstractNixRepl(state)
, debugTraceIndex(0)
, getValues(getValues)
, staticEnv(new StaticEnv(nullptr, state->staticBaseEnv))
, runNixPtr{runNix}
, interacter(std::make_unique<ReadlineLikeInteracter>(getDataDir() / "repl-history"))
, interacter(make_unique<ReadlineLikeInteracter>((getDataDir() / "repl-history").string()))
{
}
@@ -303,6 +306,21 @@ 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);
@@ -490,12 +508,7 @@ ProcessLineResult NixRepl::processLine(std::string line)
// runProgram redirects stdout to a StringSink,
// using runProgram2 to allow editors to display their UI
runProgram2({
.program = editor,
.lookupPath = true,
.args = toOsStrings(std::move(args)),
.isInteractive = true,
});
runProgram2(RunOptions{.program = editor, .lookupPath = true, .args = args, .isInteractive = true});
// Reload right after exiting the editor
state->resetFileCache();
@@ -515,7 +528,7 @@ ProcessLineResult NixRepl::processLine(std::string line)
state->callFunction(f, v, result, PosIdx());
StorePath drvPath = getDerivationPath(result);
runNix("nix-shell", toOsStrings({state->store->printStorePath(drvPath)}));
runNix("nix-shell", {state->store->printStorePath(drvPath)});
}
else if (command == ":b" || command == ":bl" || command == ":i" || command == ":sh" || command == ":log") {
@@ -546,7 +559,7 @@ ProcessLineResult NixRepl::processLine(std::string line)
}
}
} else if (command == ":i") {
runNix("nix-env", toOsStrings({"-i", drvPathRaw}));
runNix("nix-env", {"-i", drvPathRaw});
} else if (command == ":log") {
settings.readOnlyMode = true;
Finally roModeReset([&]() { settings.readOnlyMode = false; });
@@ -554,7 +567,7 @@ ProcessLineResult NixRepl::processLine(std::string line)
auto log = fetchBuildLog(state->store, drvPath, drvPathRaw);
logger->writeToStdout(log);
} else {
runNix("nix-shell", toOsStrings({drvPathRaw}));
runNix("nix-shell", {drvPathRaw});
}
}
@@ -655,22 +668,15 @@ ProcessLineResult NixRepl::processLine(std::string line)
throw Error("unknown command '%1%'", command);
else {
// 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);
}
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);
} else {
// Otherwise evaluate as expression
Value v;
evalString(line, v);
auto suspension = logger->suspend();
@@ -859,28 +865,6 @@ 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);
@@ -888,10 +872,10 @@ void NixRepl::evalString(std::string s, Value & v)
state->forceValue(v, v.determinePos(noPos));
}
void NixRepl::runNix(const std::string & program, OsStrings args, const std::optional<std::string> & input)
void NixRepl::runNix(const std::string & program, const Strings & args, const std::optional<std::string> & input)
{
if (runNixPtr)
(*runNixPtr)(program, std::move(args), input);
(*runNixPtr)(program, 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",
@@ -899,7 +883,7 @@ void NixRepl::runNix(const std::string & program, OsStrings args, const std::opt
}
std::unique_ptr<AbstractNixRepl> AbstractNixRepl::create(
const LookupPath & lookupPath, ref<EvalState> state, fun<AnnotatedValues()> getValues, RunNix * runNix)
const LookupPath & lookupPath, ref<EvalState> state, std::function<AnnotatedValues()> getValues, RunNix * runNix)
{
return std::make_unique<NixRepl>(lookupPath, state, getValues, runNix);
}

View File

@@ -9,8 +9,6 @@
#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>
@@ -85,9 +83,6 @@ 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 {
@@ -100,11 +95,7 @@ PeerInfo getPeerInfo(Descriptor remote)
throw SysError("polling for incoming connections");
}
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())) {
for (auto & fd : fds) {
if (!fd.revents)
continue;

View File

@@ -181,13 +181,15 @@ EvalState * nix_eval_state_build(nix_c_context * context, nix_eval_state_builder
if (context)
context->last_err_code = NIX_OK;
try {
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)};
return unsafe_new_with_self<EvalState>([&](auto * self) {
return EvalState{
.fetchSettings = std::move(builder->fetchSettings),
.settings = std::move(builder->settings),
.statePtr = std::make_shared<nix::EvalState>(
builder->lookupPath, builder->store, self->fetchSettings, self->settings),
.state = *self->statePtr,
};
});
}
NIXC_CATCH_ERRS_NULL
}

View File

@@ -1,8 +1,6 @@
#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"
@@ -24,11 +22,10 @@ struct nix_eval_state_builder
struct EvalState
{
nix::fetchers::Settings fetchSettings;
nix::EvalSettings settings;
std::shared_ptr<nix::EvalState> statePtr;
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,8 +137,7 @@ public:
}
nix_string_context ctx{context};
nix_string_return res{""};
EvalState wrapper{state};
desc.printValueAsJSON(v, &wrapper, strict, &ctx, copyToStore, &res);
desc.printValueAsJSON(v, (EvalState *) &state, strict, &ctx, copyToStore, &res);
if (res.str.empty()) {
return nix::ExternalValueBase::printValueAsJSON(state, strict, context, copyToStore);
}
@@ -154,16 +153,22 @@ public:
bool location,
nix::XMLWriter & doc,
nix::NixStringContext & context,
nix::StringSet & drvsSeen,
nix::PathSet & 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, &wrapper, strict, location, &doc, &ctx, &drvsSeen, *reinterpret_cast<const uint32_t *>(&pos));
v,
(EvalState *) &state,
strict,
location,
&doc,
&ctx,
&drvsSeen,
*reinterpret_cast<const uint32_t *>(&pos));
}
virtual ~NixCExternalValue() override {};

View File

@@ -145,7 +145,6 @@ 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

@@ -105,8 +105,7 @@ static void nix_c_primop_wrapper(
nix_value * external_arg = new_nix_value(args[i], state.mem);
external_args.push_back(external_arg);
}
EvalState wrapper{state};
f(userdata, &ctx, &wrapper, external_args.data(), vTmpPtr);
f(userdata, &ctx, (EvalState *) &state, external_args.data(), vTmpPtr);
if (ctx.last_err_code != NIX_OK) {
if (ctx.last_err_code == NIX_ERR_RECOVERABLE) {
@@ -160,7 +159,7 @@ PrimOp * nix_alloc_primop(
.args = {},
.arity = (size_t) arity,
.doc = doc,
.impl = std::bind(nix_c_primop_wrapper, fun, user_data, arity, _1, _2, _3, _4)};
.fun = 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);

View File

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

View File

@@ -84,8 +84,6 @@ 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,52 +476,6 @@ 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);

View File

@@ -66,44 +66,4 @@ 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

@@ -127,7 +127,7 @@ TEST_F(ValuePrintingTests, vLambda)
TEST_F(ValuePrintingTests, vPrimOp)
{
Value vPrimOp;
PrimOp primOp{.name = "puppy", .impl = [](EvalState &, const PosIdx, Value **, Value &) {}};
PrimOp primOp{.name = "puppy"};
vPrimOp.mkPrimOp(&primOp);
test(vPrimOp, "«primop puppy»");
@@ -135,7 +135,7 @@ TEST_F(ValuePrintingTests, vPrimOp)
TEST_F(ValuePrintingTests, vPrimOpApp)
{
PrimOp primOp{.name = "puppy", .impl = [](EvalState &, const PosIdx, Value **, Value &) {}};
PrimOp primOp{.name = "puppy"};
Value vPrimOp;
vPrimOp.mkPrimOp(&primOp);
@@ -531,7 +531,7 @@ TEST_F(ValuePrintingTests, ansiColorsLambda)
TEST_F(ValuePrintingTests, ansiColorsPrimOp)
{
PrimOp primOp{.name = "puppy", .impl = [](EvalState &, const PosIdx, Value **, Value &) {}};
PrimOp primOp{.name = "puppy"};
Value v;
v.mkPrimOp(&primOp);
@@ -540,7 +540,7 @@ TEST_F(ValuePrintingTests, ansiColorsPrimOp)
TEST_F(ValuePrintingTests, ansiColorsPrimOpApp)
{
PrimOp primOp{.name = "puppy", .impl = [](EvalState &, const PosIdx, Value **, Value &) {}};
PrimOp primOp{.name = "puppy"};
Value vPrimOp;
vPrimOp.mkPrimOp(&primOp);

View File

@@ -1,55 +0,0 @@
#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

@@ -1,16 +1,9 @@
#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)
{

View File

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

View File

@@ -1,5 +1,4 @@
#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"
@@ -7,26 +6,6 @@
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)

View File

@@ -228,6 +228,10 @@ static Symbol getName(const AttrName & name, EvalState & state, Env & env)
static constexpr size_t BASE_ENV_SIZE = 128;
EvalMemory::EvalMemory()
#if NIX_USE_BOEHMGC
: valueAllocCache(std::allocate_shared<void *>(traceable_allocator<void *>(), nullptr))
, env1AllocCache(std::allocate_shared<void *>(traceable_allocator<void *>(), nullptr))
#endif
{
assertGCInitialized();
}
@@ -364,7 +368,7 @@ EvalState::EvalState(
EvalState::~EvalState() {}
void EvalState::allowPathLegacy(const std::string & path)
void EvalState::allowPathLegacy(const Path & path)
{
if (auto rootFS2 = rootFS.dynamic_pointer_cast<AllowListSourceAccessor>())
rootFS2->allowPrefix(CanonPath(path));
@@ -422,36 +426,29 @@ bool isAllowedURI(std::string_view uri, const Strings & allowedUris)
return false;
}
void EvalState::checkURI(const std::string & uri0)
void EvalState::checkURI(const std::string & uri)
{
if (!settings.restrictEval)
return;
if (isAllowedURI(uri0, settings.allowedUris.get()))
if (isAllowedURI(uri, settings.allowedUris.get()))
return;
/* If the URI is a path, then check it against allowedPaths as
well. */
{
std::filesystem::path path(uri0);
if (path.is_absolute()) {
if (auto rootFS2 = rootFS.dynamic_pointer_cast<AllowListSourceAccessor>())
rootFS2->checkAccess(CanonPath(path.string()));
return;
}
if (isAbsolute(uri)) {
if (auto rootFS2 = rootFS.dynamic_pointer_cast<AllowListSourceAccessor>())
rootFS2->checkAccess(CanonPath(uri));
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 &) {
if (hasPrefix(uri, "file://")) {
if (auto rootFS2 = rootFS.dynamic_pointer_cast<AllowListSourceAccessor>())
rootFS2->checkAccess(CanonPath(uri.substr(7)));
return;
}
throw RestrictedPathError("access to URI '%s' is forbidden in restricted mode", uri0);
throw RestrictedPathError("access to URI '%s' is forbidden in restricted mode", uri);
}
Value * EvalState::addConstant(const std::string & name, Value & v, Constant info)
@@ -1660,7 +1657,7 @@ void EvalState::callFunction(Value & fun, std::span<Value *> args, Value & vRes,
primOpCalls[fn->name]++;
try {
fn->impl(*this, vCur.determinePos(noPos), args.data(), vCur);
fn->fun(*this, vCur.determinePos(noPos), args.data(), vCur);
} catch (Error & e) {
if (fn->addTrace)
addErrorTrace(e, pos, "while calling the '%1%' builtin", fn->name);
@@ -1710,7 +1707,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->impl(*this, vCur.determinePos(noPos), vArgs, vCur);
fn->fun(*this, vCur.determinePos(noPos), vArgs, vCur);
} catch (Error & e) {
if (fn->addTrace)
addErrorTrace(e, pos, "while calling the '%1%' builtin", fn->name);
@@ -2415,11 +2412,10 @@ 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(),
ctxElem.display(*store))
(*v.context()->begin())->view())
.withTrace(pos, errorCtx)
.debugThrow();
}
@@ -3205,21 +3201,6 @@ 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
@@ -3361,30 +3342,6 @@ 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 Error("derivation does not have attribute 'outPath'");
throw UnimplementedError("CA derivations are not yet supported");
return *outPath;
}

View File

@@ -1,76 +0,0 @@
#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

@@ -36,7 +36,7 @@ class EvalCache : public std::enable_shared_from_this<EvalCache>
std::shared_ptr<AttrDb> db;
EvalState & state;
typedef fun<Value *()> RootLoader;
typedef std::function<Value *()> RootLoader;
RootLoader rootLoader;
RootValue value;

View File

@@ -2,7 +2,6 @@
#include "nix/util/error.hh"
#include "nix/util/pos-idx.hh"
#include "nix/store/path.hh"
namespace nix {
@@ -82,9 +81,12 @@ MakeError(RecoverableEvalError, EvalBaseError);
struct InvalidPathError : public CloneableError<InvalidPathError, EvalError>
{
public:
StorePath path;
Path path;
InvalidPathError(EvalState & state, const StorePath & path);
InvalidPathError(EvalState & state, const Path & path)
: CloneableError(state, "path '%s' is not valid", path)
{
}
};
/**

View File

@@ -30,11 +30,6 @@ 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
@@ -68,10 +63,6 @@ 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 *));

View File

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

View File

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

View File

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

View File

@@ -155,7 +155,7 @@ public:
bool location,
XMLWriter & doc,
NixStringContext & context,
StringSet & drvsSeen,
PathSet & drvsSeen,
const PosIdx pos) const;
virtual ~ExternalValueBase() {};

View File

@@ -83,14 +83,6 @@ struct NixStringContextElem
static NixStringContextElem
parse(std::string_view s, const ExperimentalFeatureSettings & xpSettings = experimentalFeatureSettings);
std::string to_string() const;
/**
* Render for use in error messages and other user-facing output.
*
* Uses store paths and `DerivedPath` syntax, unlike `to_string()`
* which uses the internal encoding.
*/
std::string display(const StoreDirConfig & store) const;
};
/**

View File

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

View File

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

View File

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

View File

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

View File

@@ -11,7 +11,6 @@
#include "nix/store/path-references.hh"
#include "nix/store/store-api.hh"
#include "nix/util/util.hh"
#include "nix/util/os-string.hh"
#include "nix/util/processes.hh"
#include "nix/expr/value-to-json.hh"
#include "nix/expr/value-to-xml.hh"
@@ -75,7 +74,7 @@ StringMap EvalState::realiseContext(const NixStringContext & context, StorePathS
for (auto & c : context) {
auto ensureValid = [&](const StorePath & p) {
if (!store->isValidPath(p))
error<InvalidPathError>(p).debugThrow();
error<InvalidPathError>(store->printStorePath(p)).debugThrow();
};
std::visit(
overloaded{
@@ -356,7 +355,7 @@ static RegisterPrimOp primop_scopedImport(
Evaluation aborts if the file doesn't exist or contains an invalid Nix expression.
)",
.impl = [](EvalState & state, const PosIdx pos, Value ** args, Value & v) {
.fun = [](EvalState & state, const PosIdx pos, Value ** args, Value & v) {
import(state, pos, *args[1], args[0], v);
}});
@@ -431,7 +430,7 @@ static RegisterPrimOp primop_import(
>
> The function argument doesnt have to be called `x` in `foo.nix`; any name would work.
)",
.impl = [](EvalState & state, const PosIdx pos, Value ** args, Value & v) {
.fun = [](EvalState & state, const PosIdx pos, Value ** args, Value & v) {
import(state, pos, *args[0], nullptr, v);
}});
@@ -504,12 +503,12 @@ void prim_exec(EvalState & state, const PosIdx pos, Value ** args, Value & v)
try {
auto _ = state.realiseContext(context); // FIXME: Handle CA derivations
} catch (InvalidPathError & e) {
state.error<EvalError>("cannot execute '%1%', since store path '%2%' is not valid", program, e.path.to_string())
state.error<EvalError>("cannot execute '%1%', since path '%2%' is not valid", program, e.path)
.atPos(pos)
.debugThrow();
}
auto output = runProgram(program, true, toOsStrings(std::move(commandArgs)));
auto output = runProgram(program, true, commandArgs);
Expr * parsed;
try {
parsed = state.parseExprFromString(std::move(output), state.rootPath(CanonPath::root));
@@ -576,7 +575,7 @@ static RegisterPrimOp primop_typeOf({
`"int"`, `"bool"`, `"string"`, `"path"`, `"null"`, `"set"`,
`"list"`, `"lambda"` or `"float"`.
)",
.impl = prim_typeOf,
.fun = prim_typeOf,
});
/* Determine whether the argument is the null value. */
@@ -594,7 +593,7 @@ static RegisterPrimOp primop_isNull({
This is equivalent to `e == null`.
)",
.impl = prim_isNull,
.fun = prim_isNull,
});
/* Determine whether the argument is a function. */
@@ -610,7 +609,7 @@ static RegisterPrimOp primop_isFunction({
.doc = R"(
Return `true` if *e* evaluates to a function, and `false` otherwise.
)",
.impl = prim_isFunction,
.fun = prim_isFunction,
});
/* Determine whether the argument is an integer. */
@@ -626,7 +625,7 @@ static RegisterPrimOp primop_isInt({
.doc = R"(
Return `true` if *e* evaluates to an integer, and `false` otherwise.
)",
.impl = prim_isInt,
.fun = prim_isInt,
});
/* Determine whether the argument is a float. */
@@ -642,7 +641,7 @@ static RegisterPrimOp primop_isFloat({
.doc = R"(
Return `true` if *e* evaluates to a float, and `false` otherwise.
)",
.impl = prim_isFloat,
.fun = prim_isFloat,
});
/* Determine whether the argument is a string. */
@@ -658,7 +657,7 @@ static RegisterPrimOp primop_isString({
.doc = R"(
Return `true` if *e* evaluates to a string, and `false` otherwise.
)",
.impl = prim_isString,
.fun = prim_isString,
});
/* Determine whether the argument is a Boolean. */
@@ -674,7 +673,7 @@ static RegisterPrimOp primop_isBool({
.doc = R"(
Return `true` if *e* evaluates to a bool, and `false` otherwise.
)",
.impl = prim_isBool,
.fun = prim_isBool,
});
/* Determine whether the argument is a path. */
@@ -690,7 +689,7 @@ static RegisterPrimOp primop_isPath({
.doc = R"(
Return `true` if *e* evaluates to a path, and `false` otherwise.
)",
.impl = prim_isPath,
.fun = prim_isPath,
});
template<typename Callable>
@@ -953,7 +952,7 @@ static RegisterPrimOp primop_genericClosure(
> [ { key = 5; } { key = 16; } { key = 8; } { key = 4; } { key = 2; } { key = 1; } ]
> ```
)",
.impl = prim_genericClosure,
.fun = prim_genericClosure,
});
static RegisterPrimOp primop_break(
@@ -963,7 +962,7 @@ static RegisterPrimOp primop_break(
In debug mode (enabled using `--debugger`), pause Nix expression evaluation and enter the REPL.
Otherwise, return the argument `v`.
)",
.impl = [](EvalState & state, const PosIdx pos, Value ** args, Value & v) {
.fun = [](EvalState & state, const PosIdx pos, Value ** args, Value & v) {
if (state.canDebug()) {
auto error = Error(
ErrorInfo{
@@ -985,7 +984,7 @@ static RegisterPrimOp primop_abort(
.doc = R"(
Abort Nix expression evaluation and print the error message *s*.
)",
.impl = [](EvalState & state, const PosIdx pos, Value ** args, Value & v) {
.fun = [](EvalState & state, const PosIdx pos, Value ** args, Value & v) {
NixStringContext context;
auto s =
state.coerceToString(pos, *args[0], context, "while evaluating the error message passed to builtins.abort")
@@ -1005,7 +1004,7 @@ static RegisterPrimOp primop_throw(
derivations, a derivation that throws an error is silently skipped
(which is not the case for `abort`).
)",
.impl = [](EvalState & state, const PosIdx pos, Value ** args, Value & v) {
.fun = [](EvalState & state, const PosIdx pos, Value ** args, Value & v) {
NixStringContext context;
auto s =
state.coerceToString(pos, *args[0], context, "while evaluating the error message passed to builtin.throw")
@@ -1040,7 +1039,7 @@ static RegisterPrimOp primop_addErrorContext(
.arity = 2,
// The normal trace item is redundant
.addTrace = false,
.impl = prim_addErrorContext,
.fun = prim_addErrorContext,
});
static void prim_ceil(EvalState & state, const PosIdx pos, Value ** args, Value & v)
@@ -1095,7 +1094,7 @@ static RegisterPrimOp primop_ceil({
If the datatype of *number* is neither a NixInt (signed 64-bit integer) nor a NixFloat
(IEEE-754 double-precision floating-point number), an evaluation error is thrown.
)",
.impl = prim_ceil,
.fun = prim_ceil,
});
static void prim_floor(EvalState & state, const PosIdx pos, Value ** args, Value & v)
@@ -1150,7 +1149,7 @@ static RegisterPrimOp primop_floor({
If the datatype of *number* is neither a NixInt (signed 64-bit integer) nor a NixFloat
(IEEE-754 double-precision floating-point number), an evaluation error will be thrown.
)",
.impl = prim_floor,
.fun = prim_floor,
});
/* Try evaluating the argument. Success => {success=true; value=something;},
@@ -1207,7 +1206,7 @@ static RegisterPrimOp primop_tryEval({
`tryEval` intentionally does not return the error message, because that risks bringing non-determinism into the evaluation result, and it would become very difficult to improve error reporting without breaking existing expressions.
Instead, use [`builtins.addErrorContext`](@docroot@/language/builtins.md#builtins-addErrorContext) to add context to the error message, and use a Nix unit testing tool for testing.
)",
.impl = prim_tryEval,
.fun = prim_tryEval,
});
/* Return an environment variable. Use with care. */
@@ -1232,7 +1231,7 @@ static RegisterPrimOp primop_getEnv({
Packages. (That is, it does a `getEnv "HOME"` to locate the users
home directory.)
)",
.impl = prim_getEnv,
.fun = prim_getEnv,
});
/* Evaluate the first argument, then return the second argument. */
@@ -1250,7 +1249,7 @@ static RegisterPrimOp primop_seq({
Evaluate *e1*, then evaluate and return *e2*. This ensures that a
computation is strict in the value of *e1*.
)",
.impl = prim_seq,
.fun = prim_seq,
});
/* Evaluate the first argument deeply (i.e. recursing into lists and
@@ -1270,7 +1269,7 @@ static RegisterPrimOp primop_deepSeq({
if its a list or set, its elements or attributes are also
evaluated recursively.
)",
.impl = prim_deepSeq,
.fun = prim_deepSeq,
});
/* Evaluate the first expression and print it on standard error. Then
@@ -1303,7 +1302,7 @@ static RegisterPrimOp primop_trace({
interactive debugger is started when `trace` is called (like
[`break`](@docroot@/language/builtins.md#builtins-break)).
)",
.impl = prim_trace,
.fun = prim_trace,
});
static void prim_warn(EvalState & state, const PosIdx pos, Value ** args, Value & v)
@@ -1356,7 +1355,7 @@ static RegisterPrimOp primop_warn({
option is set, the evaluation is aborted after the warning is printed.
This is useful to reveal the stack trace of the warning, when the context is non-interactive and a debugger can not be launched.
)",
.impl = prim_warn,
.fun = prim_warn,
});
/* Takes two arguments and evaluates to the second one. Used as the
@@ -1854,7 +1853,7 @@ static RegisterPrimOp primop_derivationStrict(
PrimOp{
.name = "derivationStrict",
.arity = 1,
.impl = prim_derivationStrict,
.fun = prim_derivationStrict,
});
/* Return a placeholder string for the specified output that will be
@@ -1884,7 +1883,7 @@ static RegisterPrimOp primop_placeholder({
Typical outputs would be `"out"`, `"bin"` or `"dev"`.
)",
.impl = prim_placeholder,
.fun = prim_placeholder,
});
/*************************************************************
@@ -1908,7 +1907,7 @@ static RegisterPrimOp primop_toPath({
**DEPRECATED.** Use `/. + "/path"` to convert a string into an absolute
path. For relative paths, use `./. + "/path"`.
)",
.impl = prim_toPath,
.fun = prim_toPath,
});
/* Allow a valid store path to be used in an expression. This is
@@ -1934,7 +1933,7 @@ static void prim_storePath(EvalState & state, const PosIdx pos, Value ** args, V
directly in the store. The latter condition is necessary so
e.g. nix-push does the right thing. */
if (!state.store->isStorePath(path.abs()))
path = CanonPath(canonPath(path.abs(), true).string());
path = CanonPath(canonPath(path.abs(), true));
if (!state.store->isInStore(path.abs()))
state.error<EvalError>("path '%1%' is not in the Nix store", path).atPos(pos).debugThrow();
auto path2 = state.store->toStorePath(path.abs()).first;
@@ -1961,7 +1960,7 @@ static RegisterPrimOp primop_storePath({
See also [`builtins.fetchClosure`](#builtins-fetchClosure).
)",
.impl = prim_storePath,
.fun = prim_storePath,
});
static void prim_pathExists(EvalState & state, const PosIdx pos, Value ** args, Value & v)
@@ -1992,7 +1991,7 @@ static RegisterPrimOp primop_pathExists({
Return `true` if the path *path* exists at evaluation time, and
`false` otherwise.
)",
.impl = prim_pathExists,
.fun = prim_pathExists,
});
// Ideally, all trailing slashes should have been removed, but it's been like this for
@@ -2042,7 +2041,7 @@ static RegisterPrimOp primop_baseNameOf({
This is somewhat similar to the [GNU `basename`](https://www.gnu.org/software/coreutils/manual/html_node/basename-invocation.html) command, but GNU `basename` strips any number of trailing slashes.
)",
.impl = prim_baseNameOf,
.fun = prim_baseNameOf,
});
/* Return the directory of the given path, i.e., everything before the
@@ -2076,7 +2075,7 @@ static RegisterPrimOp primop_dirOf({
before the final slash in the string. This is similar to the GNU
`dirname` command.
)",
.impl = prim_dirOf,
.fun = prim_dirOf,
});
/* Return the contents of a file as a string. */
@@ -2115,7 +2114,7 @@ static RegisterPrimOp primop_readFile({
.doc = R"(
Return the contents of the file *path* as a string.
)",
.impl = prim_readFile,
.fun = prim_readFile,
});
/* Find a file in the Nix search path. Used to implement <x> paths,
@@ -2155,7 +2154,7 @@ static void prim_findFile(EvalState & state, const PosIdx pos, Value ** args, Va
auto rewrites = state.realiseContext(context);
path = rewriteStrings(std::move(path), rewrites);
} catch (InvalidPathError & e) {
state.error<EvalError>("cannot find '%1%', since path '%2%' is not valid", path, e.path.to_string())
state.error<EvalError>("cannot find '%1%', since path '%2%' is not valid", path, e.path)
.atPos(pos)
.debugThrow();
}
@@ -2305,7 +2304,7 @@ static RegisterPrimOp primop_findFile(
>
> makes `<nixpkgs>` refer to a particular branch of the `NixOS/nixpkgs` repository on GitHub.
)",
.impl = prim_findFile,
.fun = prim_findFile,
});
/* Return the cryptographic hash of a file in base-16. */
@@ -2330,7 +2329,7 @@ static RegisterPrimOp primop_hashFile({
file at path *p*. The hash algorithm specified by *type* must be one
of `"md5"`, `"sha1"`, `"sha256"` or `"sha512"`.
)",
.impl = prim_hashFile,
.fun = prim_hashFile,
});
static const Value & fileTypeToString(EvalState & state, SourceAccessor::Type type)
@@ -2381,7 +2380,7 @@ static RegisterPrimOp primop_readFileType({
Determine the directory entry type of a filesystem node, being
one of `"directory"`, `"regular"`, `"symlink"`, or `"unknown"`.
)",
.impl = prim_readFileType,
.fun = prim_readFileType,
});
/* Read a directory (without . or ..) */
@@ -2440,7 +2439,7 @@ static RegisterPrimOp primop_readDir({
The possible values for the file type are `"regular"`,
`"directory"`, `"symlink"` and `"unknown"`.
)",
.impl = prim_readDir,
.fun = prim_readDir,
});
/* Extend single element string context with another output. */
@@ -2486,7 +2485,7 @@ static RegisterPrimOp primop_outputOf({
This primop corresponds to the `^` sigil for [deriving paths](@docroot@/glossary.md#gloss-deriving-path), e.g. as part of installable syntax on the command line.
)",
.impl = prim_outputOf,
.fun = prim_outputOf,
.experimentalFeature = Xp::DynamicDerivations,
});
@@ -2599,7 +2598,7 @@ static RegisterPrimOp primop_toXML({
stylesheet is spliced into the builder using the syntax `xsltproc
${stylesheet}`.
)",
.impl = prim_toXML,
.fun = prim_toXML,
});
/* Convert the argument (which can be any Nix expression) to a JSON
@@ -2624,7 +2623,7 @@ static RegisterPrimOp primop_toJSON({
derivations output path. Paths are copied to the store and
represented as a JSON string of the resulting store path.
)",
.impl = prim_toJSON,
.fun = prim_toJSON,
});
/* Parse a JSON string to a value. */
@@ -2651,7 +2650,7 @@ static RegisterPrimOp primop_fromJSON({
returns the value `{ x = [ 1 2 3 ]; y = null; }`.
)",
.impl = prim_fromJSON,
.fun = prim_fromJSON,
});
/* Store a string in the Nix store as a source file that can be used
@@ -2779,7 +2778,7 @@ static RegisterPrimOp primop_toFile({
you are using Nixpkgs, the `writeTextFile` function is able to do
that.
)",
.impl = prim_toFile,
.fun = prim_toFile,
});
bool EvalState::callPathFilter(Value * filterFun, const SourcePath & path, PosIdx pos)
@@ -2827,7 +2826,7 @@ static void addPath(
std::unique_ptr<PathFilter> filter;
if (filterFun)
filter = std::make_unique<PathFilter>([&](const std::string & p) {
filter = std::make_unique<PathFilter>([&](const Path & p) {
auto p2 = CanonPath(p);
return state.callPathFilter(filterFun, {path.accessor, p2}, pos);
});
@@ -2935,7 +2934,7 @@ static RegisterPrimOp primop_filterSource({
`true` for them, the copy fails). If you exclude a directory,
the entire corresponding subtree of *e2* is excluded.
)",
.impl = prim_filterSource,
.fun = prim_filterSource,
});
static void prim_path(EvalState & state, const PosIdx pos, Value ** args, Value & v)
@@ -3020,7 +3019,7 @@ static RegisterPrimOp primop_path({
and providing a hash allows `builtins.path` to be used even
when the `pure-eval` nix config option is on.
)",
.impl = prim_path,
.fun = prim_path,
});
/*************************************************************
@@ -3051,7 +3050,7 @@ static RegisterPrimOp primop_attrNames({
alphabetically sorted list. For instance, `builtins.attrNames { y
= 1; x = "foo"; }` evaluates to `[ "x" "y" ]`.
)",
.impl = prim_attrNames,
.fun = prim_attrNames,
});
/* Return the values of the attributes in a set as a list, in the same
@@ -3083,7 +3082,7 @@ static RegisterPrimOp primop_attrValues({
Return the values of the attributes in the set *set* in the order
corresponding to the sorted attribute names.
)",
.impl = prim_attrValues,
.fun = prim_attrValues,
});
/* Dynamic version of the `.' operator. */
@@ -3108,7 +3107,7 @@ static RegisterPrimOp primop_getAttr({
the `.` operator, since *s* is an expression rather than an
identifier.
)",
.impl = prim_getAttr,
.fun = prim_getAttr,
});
/* Return position information of the specified attribute. */
@@ -3134,7 +3133,7 @@ static RegisterPrimOp primop_unsafeGetAttrPos(
from *set*. This is used by Nixpkgs to provide location information
in error messages.
)",
.impl = prim_unsafeGetAttrPos,
.fun = prim_unsafeGetAttrPos,
});
// access to exact position information (ie, line and column numbers) is deferred
@@ -3151,10 +3150,10 @@ static RegisterPrimOp primop_unsafeGetAttrPos(
// for in the very hot path that is forceValue.
static struct LazyPosAccessors
{
PrimOp primop_lineOfPos{.arity = 1, .impl = [](EvalState & state, PosIdx pos, Value ** args, Value & v) {
PrimOp primop_lineOfPos{.arity = 1, .fun = [](EvalState & state, PosIdx pos, Value ** args, Value & v) {
v.mkInt(state.positions[PosIdx(args[0]->integer().value)].line);
}};
PrimOp primop_columnOfPos{.arity = 1, .impl = [](EvalState & state, PosIdx pos, Value ** args, Value & v) {
PrimOp primop_columnOfPos{.arity = 1, .fun = [](EvalState & state, PosIdx pos, Value ** args, Value & v) {
v.mkInt(state.positions[PosIdx(args[0]->integer().value)].column);
}};
@@ -3196,7 +3195,7 @@ static RegisterPrimOp primop_hasAttr({
`false` otherwise. This is a dynamic version of the `?` operator,
since *s* is an expression rather than an identifier.
)",
.impl = prim_hasAttr,
.fun = prim_hasAttr,
});
/* Determine whether the argument is a set. */
@@ -3212,7 +3211,7 @@ static RegisterPrimOp primop_isAttrs({
.doc = R"(
Return `true` if *e* evaluates to a set, and `false` otherwise.
)",
.impl = prim_isAttrs,
.fun = prim_isAttrs,
});
static void prim_removeAttrs(EvalState & state, const PosIdx pos, Value ** args, Value & v)
@@ -3255,7 +3254,7 @@ static RegisterPrimOp primop_removeAttrs({
evaluates to `{ y = 2; }`.
)",
.impl = prim_removeAttrs,
.fun = prim_removeAttrs,
});
/* Builds a set from a list specifying (name, value) pairs. To be
@@ -3342,7 +3341,7 @@ static RegisterPrimOp primop_listToAttrs({
{ foo = 123; bar = 456; }
```
)",
.impl = prim_listToAttrs,
.fun = prim_listToAttrs,
});
static void prim_intersectAttrs(EvalState & state, const PosIdx pos, Value ** args, Value & v)
@@ -3419,7 +3418,7 @@ static RegisterPrimOp primop_intersectAttrs({
Performs in O(*n* log *m*) where *n* is the size of the smaller set and *m* the larger set's size.
)",
.impl = prim_intersectAttrs,
.fun = prim_intersectAttrs,
});
static void prim_catAttrs(EvalState & state, const PosIdx pos, Value ** args, Value & v)
@@ -3458,7 +3457,7 @@ static RegisterPrimOp primop_catAttrs({
evaluates to `[1 2]`.
)",
.impl = prim_catAttrs,
.fun = prim_catAttrs,
});
static void prim_functionArgs(EvalState & state, const PosIdx pos, Value ** args, Value & v)
@@ -3501,7 +3500,7 @@ static RegisterPrimOp primop_functionArgs({
the function. Plain lambdas are not included, e.g. `functionArgs (x:
...) = { }`.
)",
.impl = prim_functionArgs,
.fun = prim_functionArgs,
});
/* */
@@ -3533,7 +3532,7 @@ static RegisterPrimOp primop_mapAttrs({
evaluates to `{ a = 10; b = 20; }`.
)",
.impl = prim_mapAttrs,
.fun = prim_mapAttrs,
});
static void prim_zipAttrsWith(EvalState & state, const PosIdx pos, Value ** args, Value & v)
@@ -3620,7 +3619,7 @@ static RegisterPrimOp primop_zipAttrsWith({
}
```
)",
.impl = prim_zipAttrsWith,
.fun = prim_zipAttrsWith,
});
/*************************************************************
@@ -3640,7 +3639,7 @@ static RegisterPrimOp primop_isList({
.doc = R"(
Return `true` if *e* evaluates to a list, and `false` otherwise.
)",
.impl = prim_isList,
.fun = prim_isList,
});
/* Return the n-1'th element of a list. */
@@ -3664,7 +3663,7 @@ static RegisterPrimOp primop_elemAt({
Return element *n* from the list *xs*. Elements are counted starting
from 0. A fatal error occurs if the index is out of bounds.
)",
.impl = prim_elemAt,
.fun = prim_elemAt,
});
/* Return the first element of a list. */
@@ -3685,7 +3684,7 @@ static RegisterPrimOp primop_head({
isnt a list or is an empty list. You can test whether a list is
empty by comparing it with `[]`.
)",
.impl = prim_head,
.fun = prim_head,
});
/* Return a list consisting of everything but the first element of
@@ -3716,7 +3715,7 @@ static RegisterPrimOp primop_tail({
> unlike Haskell's `tail`, it takes O(n) time, so recursing over a
> list by repeatedly calling `tail` takes O(n^2) time.
)",
.impl = prim_tail,
.fun = prim_tail,
});
/* Apply a function to every element of a list. */
@@ -3750,7 +3749,7 @@ static RegisterPrimOp primop_map({
evaluates to `[ "foobar" "foobla" "fooabc" ]`.
)",
.impl = prim_map,
.fun = prim_map,
});
/* Filter a list using a predicate; that is, return a list containing
@@ -3799,7 +3798,7 @@ static RegisterPrimOp primop_filter({
Return a list consisting of the elements of *list* for which the
function *f* returns `true`.
)",
.impl = prim_filter,
.fun = prim_filter,
});
/* Return true if a list contains a given element. */
@@ -3822,7 +3821,7 @@ static RegisterPrimOp primop_elem({
Return `true` if a value equal to *x* occurs in the list *xs*, and
`false` otherwise.
)",
.impl = prim_elem,
.fun = prim_elem,
});
/* Concatenate a list of lists. */
@@ -3844,7 +3843,7 @@ static RegisterPrimOp primop_concatLists({
.doc = R"(
Concatenate a list of lists into a single list.
)",
.impl = prim_concatLists,
.fun = prim_concatLists,
});
/* Return the length of a list. This is an O(1) time operation. */
@@ -3860,7 +3859,7 @@ static RegisterPrimOp primop_length({
.doc = R"(
Return the length of the list *e*.
)",
.impl = prim_length,
.fun = prim_length,
});
/* Reduce a list by applying a binary operator, from left to
@@ -3903,7 +3902,7 @@ static RegisterPrimOp primop_foldlStrict({
of each application of `op` is evaluated immediately, even for
intermediate values.
)",
.impl = prim_foldlStrict,
.fun = prim_foldlStrict,
});
static void anyOrAll(bool any, EvalState & state, const PosIdx pos, Value ** args, Value & v)
@@ -3941,7 +3940,7 @@ static RegisterPrimOp primop_any({
Return `true` if the function *pred* returns `true` for at least one
element of *list*, and `false` otherwise.
)",
.impl = prim_any,
.fun = prim_any,
});
static void prim_all(EvalState & state, const PosIdx pos, Value ** args, Value & v)
@@ -3956,7 +3955,7 @@ static RegisterPrimOp primop_all({
Return `true` if the function *pred* returns `true` for all elements
of *list*, and `false` otherwise.
)",
.impl = prim_all,
.fun = prim_all,
});
static void prim_genList(EvalState & state, const PosIdx pos, Value ** args, Value & v)
@@ -3994,7 +3993,7 @@ static RegisterPrimOp primop_genList({
returns the list `[ 0 1 4 9 16 ]`.
)",
.impl = prim_genList,
.fun = prim_genList,
});
static void prim_lessThan(EvalState & state, const PosIdx pos, Value ** args, Value & v);
@@ -4019,7 +4018,7 @@ static void prim_sort(EvalState & state, const PosIdx pos, Value ** args, Value
/* Optimization: if the comparator is lessThan, bypass
callFunction. */
if (args[0]->isPrimOp()) {
auto ptr = args[0]->primOp()->impl.get_fn().target<decltype(&prim_lessThan)>();
auto ptr = args[0]->primOp()->fun.target<decltype(&prim_lessThan)>();
if (ptr && *ptr == prim_lessThan)
return CompareValues(state, noPos, "while evaluating the ordering function passed to builtins.sort")(
a, b);
@@ -4104,7 +4103,7 @@ static RegisterPrimOp primop_sort({
If the *comparator* violates any of these properties, then `builtins.sort`
reorders elements in an unspecified manner.
)",
.impl = prim_sort,
.fun = prim_sort,
});
static void prim_partition(EvalState & state, const PosIdx pos, Value ** args, Value & v)
@@ -4165,7 +4164,7 @@ static RegisterPrimOp primop_partition({
{ right = [ 23 42 ]; wrong = [ 1 9 3 ]; }
```
)",
.impl = prim_partition,
.fun = prim_partition,
});
static void prim_groupBy(EvalState & state, const PosIdx pos, Value ** args, Value & v)
@@ -4218,7 +4217,7 @@ static RegisterPrimOp primop_groupBy({
{ b = [ "bar" "baz" ]; f = [ "foo" ]; }
```
)",
.impl = prim_groupBy,
.fun = prim_groupBy,
});
static void prim_concatMap(EvalState & state, const PosIdx pos, Value ** args, Value & v)
@@ -4260,7 +4259,7 @@ static RegisterPrimOp primop_concatMap({
This function is equivalent to `builtins.concatLists (map f list)`
but is more efficient.
)",
.impl = prim_concatMap,
.fun = prim_concatMap,
});
/*************************************************************
@@ -4294,7 +4293,7 @@ static RegisterPrimOp primop_add({
.doc = R"(
Return the sum of the numbers *e1* and *e2*.
)",
.impl = prim_add,
.fun = prim_add,
});
static void prim_sub(EvalState & state, const PosIdx pos, Value ** args, Value & v)
@@ -4325,7 +4324,7 @@ static RegisterPrimOp primop_sub({
.doc = R"(
Return the difference between the numbers *e1* and *e2*.
)",
.impl = prim_sub,
.fun = prim_sub,
});
static void prim_mul(EvalState & state, const PosIdx pos, Value ** args, Value & v)
@@ -4356,7 +4355,7 @@ static RegisterPrimOp primop_mul({
.doc = R"(
Return the product of the numbers *e1* and *e2*.
)",
.impl = prim_mul,
.fun = prim_mul,
});
static void prim_div(EvalState & state, const PosIdx pos, Value ** args, Value & v)
@@ -4389,7 +4388,7 @@ static RegisterPrimOp primop_div({
.doc = R"(
Return the quotient of the numbers *e1* and *e2*.
)",
.impl = prim_div,
.fun = prim_div,
});
static void prim_bitAnd(EvalState & state, const PosIdx pos, Value ** args, Value & v)
@@ -4405,7 +4404,7 @@ static RegisterPrimOp primop_bitAnd({
.doc = R"(
Return the bitwise AND of the integers *e1* and *e2*.
)",
.impl = prim_bitAnd,
.fun = prim_bitAnd,
});
static void prim_bitOr(EvalState & state, const PosIdx pos, Value ** args, Value & v)
@@ -4422,7 +4421,7 @@ static RegisterPrimOp primop_bitOr({
.doc = R"(
Return the bitwise OR of the integers *e1* and *e2*.
)",
.impl = prim_bitOr,
.fun = prim_bitOr,
});
static void prim_bitXor(EvalState & state, const PosIdx pos, Value ** args, Value & v)
@@ -4439,7 +4438,7 @@ static RegisterPrimOp primop_bitXor({
.doc = R"(
Return the bitwise XOR of the integers *e1* and *e2*.
)",
.impl = prim_bitXor,
.fun = prim_bitXor,
});
static void prim_lessThan(EvalState & state, const PosIdx pos, Value ** args, Value & v)
@@ -4459,7 +4458,7 @@ static RegisterPrimOp primop_lessThan({
Evaluation aborts if either *e1* or *e2* does not evaluate to a number, string or path.
Furthermore, it aborts if *e2* does not match *e1*'s type according to the aforementioned classification of number, string or path.
)",
.impl = prim_lessThan,
.fun = prim_lessThan,
});
/*************************************************************
@@ -4498,7 +4497,7 @@ static RegisterPrimOp primop_toString({
- `null`, which yields the empty string.
)",
.impl = prim_toString,
.fun = prim_toString,
});
/* `substring start len str' returns the substring of `str' starting
@@ -4568,7 +4567,7 @@ static RegisterPrimOp primop_substring({
evaluates to `"nix"`.
)",
.impl = prim_substring,
.fun = prim_substring,
});
static void prim_stringLength(EvalState & state, const PosIdx pos, Value ** args, Value & v)
@@ -4586,7 +4585,7 @@ static RegisterPrimOp primop_stringLength({
Return the number of bytes of the string *e*. If *e* is not a string,
evaluation is aborted.
)",
.impl = prim_stringLength,
.fun = prim_stringLength,
});
/* Return the cryptographic hash of a string in base-16. */
@@ -4613,7 +4612,7 @@ static RegisterPrimOp primop_hashString({
*s*. The hash algorithm specified by *type* must be one of `"md5"`,
`"sha1"`, `"sha256"` or `"sha512"`.
)",
.impl = prim_hashString,
.fun = prim_hashString,
});
static void prim_convertHash(EvalState & state, const PosIdx pos, Value ** args, Value & v)
@@ -4711,7 +4710,7 @@ static RegisterPrimOp primop_convertHash({
>
> "sha256-47DEQpj8HBSa+/TImW+5JCeuQeRkm5NMpJWZG3hSuFU="
)",
.impl = prim_convertHash,
.fun = prim_convertHash,
});
struct RegexCache
@@ -4815,7 +4814,7 @@ static RegisterPrimOp primop_match({
Evaluates to `[ "FOO" ]`.
)s",
.impl = prim_match,
.fun = prim_match,
});
/* Split a string with a regular expression, and return a list of the
@@ -4919,7 +4918,7 @@ static RegisterPrimOp primop_split({
Evaluates to `[ " " [ "FOO" ] " " ]`.
)s",
.impl = prim_split,
.fun = prim_split,
});
static void prim_concatStringsSep(EvalState & state, const PosIdx pos, Value ** args, Value & v)
@@ -4963,7 +4962,7 @@ static RegisterPrimOp primop_concatStringsSep({
element, e.g. `concatStringsSep "/" ["usr" "local" "bin"] ==
"usr/local/bin"`.
)",
.impl = prim_concatStringsSep,
.fun = prim_concatStringsSep,
});
static void prim_replaceStrings(EvalState & state, const PosIdx pos, Value ** args, Value & v)
@@ -5047,7 +5046,7 @@ static RegisterPrimOp primop_replaceStrings({
evaluates to `"fabir"`.
)",
.impl = prim_replaceStrings,
.fun = prim_replaceStrings,
});
/*************************************************************
@@ -5076,7 +5075,7 @@ static RegisterPrimOp primop_parseDrvName({
`builtins.parseDrvName "nix-0.12pre12876"` returns `{ name =
"nix"; version = "0.12pre12876"; }`.
)",
.impl = prim_parseDrvName,
.fun = prim_parseDrvName,
});
static void prim_compareVersions(EvalState & state, const PosIdx pos, Value ** args, Value & v)
@@ -5099,7 +5098,7 @@ static RegisterPrimOp primop_compareVersions({
algorithm is the same as the one used by [`nix-env
-u`](../command-ref/nix-env/upgrade.md).
)",
.impl = prim_compareVersions,
.fun = prim_compareVersions,
});
static void prim_splitVersion(EvalState & state, const PosIdx pos, Value ** args, Value & v)
@@ -5128,7 +5127,7 @@ static RegisterPrimOp primop_splitVersion({
same version splitting logic underlying the version comparison in
[`nix-env -u`](../command-ref/nix-env/upgrade.md).
)",
.impl = prim_splitVersion,
.fun = prim_splitVersion,
});
/*************************************************************
@@ -5230,7 +5229,7 @@ void EvalState::createBaseEnv(const EvalSettings & evalSettings)
});
if (!settings.pureEval) {
v.mkInt(time(nullptr));
v.mkInt(time(0));
}
addConstant(
"__currentTime",
@@ -5356,12 +5355,12 @@ void EvalState::createBaseEnv(const EvalSettings & evalSettings)
addPrimOp({
.name = "__importNative",
.arity = 2,
.impl = prim_importNative,
.fun = prim_importNative,
});
addPrimOp({
.name = "__exec",
.arity = 1,
.impl = prim_exec,
.fun = prim_exec,
});
}
#endif
@@ -5375,7 +5374,7 @@ void EvalState::createBaseEnv(const EvalSettings & evalSettings)
error if `--trace-verbose` is enabled. Then return *e2*. This function
is useful for debugging.
)",
.impl = settings.traceVerbose ? prim_trace : prim_second,
.fun = settings.traceVerbose ? prim_trace : prim_second,
});
/* Add a value containing the current Nix expression search path. */

View File

@@ -20,7 +20,7 @@ static RegisterPrimOp primop_unsafeDiscardStringContext({
.doc = R"(
Discard the [string context](@docroot@/language/string-context.md) from a value that can be coerced to a string.
)",
.impl = prim_unsafeDiscardStringContext,
.fun = prim_unsafeDiscardStringContext,
});
static void prim_hasContext(EvalState & state, const PosIdx pos, Value ** args, Value & v)
@@ -51,7 +51,7 @@ static RegisterPrimOp primop_hasContext(
> else { ${name} = meta; }
> ```
)",
.impl = prim_hasContext});
.fun = prim_hasContext});
static void prim_unsafeDiscardOutputDependency(EvalState & state, const PosIdx pos, Value ** args, Value & v)
{
@@ -92,7 +92,7 @@ static RegisterPrimOp primop_unsafeDiscardOutputDependency(
[`builtins.addDrvOutputDependencies`]: #builtins-addDrvOutputDependencies
)",
.impl = prim_unsafeDiscardOutputDependency});
.fun = prim_unsafeDiscardOutputDependency});
static void prim_addDrvOutputDependencies(EvalState & state, const PosIdx pos, Value ** args, Value & v)
{
@@ -157,7 +157,7 @@ static RegisterPrimOp primop_addDrvOutputDependencies(
This is the opposite of [`builtins.unsafeDiscardOutputDependency`](#builtins-unsafeDiscardOutputDependency).
)",
.impl = prim_addDrvOutputDependencies});
.fun = prim_addDrvOutputDependencies});
/* Extract the context of a string as a structured Nix value.
@@ -249,7 +249,7 @@ static RegisterPrimOp primop_getContext(
{ "/nix/store/arhvjaf6zmlyn8vh8fgn55rpwnxq0n7l-a.drv" = { outputs = [ "out" ]; }; }
```
)",
.impl = prim_getContext});
.fun = prim_getContext});
/* Append the given context to a given string.
@@ -324,6 +324,6 @@ static void prim_appendContext(EvalState & state, const PosIdx pos, Value ** arg
v.mkString(orig, context, state.mem);
}
static RegisterPrimOp primop_appendContext({.name = "__appendContext", .arity = 2, .impl = prim_appendContext});
static RegisterPrimOp primop_appendContext({.name = "__appendContext", .arity = 2, .fun = prim_appendContext});
} // namespace nix

View File

@@ -291,7 +291,7 @@ static RegisterPrimOp primop_fetchClosure({
However, `fetchClosure` is more reproducible because it specifies a binary cache from which the path can be fetched.
Also, using content-addressed store paths does not require users to configure [`trusted-public-keys`](@docroot@/command-ref/conf-file.md#conf-trusted-public-keys) to ensure their authenticity.
)",
.impl = prim_fetchClosure,
.fun = prim_fetchClosure,
.experimentalFeature = Xp::FetchClosure,
});

View File

@@ -99,6 +99,6 @@ static void prim_fetchMercurial(EvalState & state, const PosIdx pos, Value ** ar
state.allowPath(storePath);
}
static RegisterPrimOp r_fetchMercurial({.name = "fetchMercurial", .arity = 1, .impl = prim_fetchMercurial});
static RegisterPrimOp r_fetchMercurial({.name = "fetchMercurial", .arity = 1, .fun = prim_fetchMercurial});
} // namespace nix

View File

@@ -357,7 +357,7 @@ static RegisterPrimOp primop_fetchTree({
return doc;
}(),
.impl = prim_fetchTree,
.fun = prim_fetchTree,
.experimentalFeature = Xp::FetchTree,
});
@@ -369,7 +369,7 @@ void prim_fetchFinalTree(EvalState & state, const PosIdx pos, Value ** args, Val
static RegisterPrimOp primop_fetchFinalTree({
.name = "fetchFinalTree",
.args = {"input"},
.impl = prim_fetchFinalTree,
.fun = prim_fetchFinalTree,
.internal = true,
});
@@ -531,7 +531,7 @@ static RegisterPrimOp primop_fetchurl({
Not available in [restricted evaluation mode](@docroot@/command-ref/conf-file.md#conf-restrict-eval).
)",
.impl = prim_fetchurl,
.fun = prim_fetchurl,
});
static void prim_fetchTarball(EvalState & state, const PosIdx pos, Value ** args, Value & v)
@@ -581,7 +581,7 @@ static RegisterPrimOp primop_fetchTarball({
Not available in [restricted evaluation mode](@docroot@/command-ref/conf-file.md#conf-restrict-eval).
)",
.impl = prim_fetchTarball,
.fun = prim_fetchTarball,
});
static void prim_fetchGit(EvalState & state, const PosIdx pos, Value ** args, Value & v)
@@ -797,7 +797,7 @@ static RegisterPrimOp primop_fetchGit({
files, even if they are not committed or added to Git's index. It
only considers files added to the Git repository, as listed by `git ls-files`.
)",
.impl = prim_fetchGit,
.fun = prim_fetchGit,
});
} // namespace nix

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