Compare commits
219 Commits
getflake-p
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6f3067817c | ||
|
|
604d3ab8d2 | ||
|
|
dd76afbf60 | ||
|
|
2192406fe1 | ||
|
|
124b277764 | ||
|
|
c28203f097 | ||
|
|
116af62b8e | ||
|
|
6be774a9ed | ||
|
|
9a30578624 | ||
|
|
ae955594d6 | ||
|
|
26679828f7 | ||
|
|
a8d51a0c59 | ||
|
|
17ced6f80c | ||
|
|
b516291bea | ||
|
|
57e0ad01e7 | ||
|
|
5e84b57594 | ||
|
|
6bedefea9e | ||
|
|
fa1a370c6f | ||
|
|
c0b4227573 | ||
|
|
a2f73abde3 | ||
|
|
c6562fd938 | ||
|
|
0bb0f69f6c | ||
|
|
5bedf61b83 | ||
|
|
70c6e0a399 | ||
|
|
f17d9dee9e | ||
|
|
9534543860 | ||
|
|
08efb2b454 | ||
|
|
72abbc4164 | ||
|
|
8f6c05ee7a | ||
|
|
3df8dbc07c | ||
|
|
05855f5d2a | ||
|
|
d7245ff8ac | ||
|
|
539c6a1aaf | ||
|
|
faa16841b6 | ||
|
|
2d0c5421a5 | ||
|
|
800d92449f | ||
|
|
6633c55f8a | ||
|
|
5f5b043524 | ||
|
|
4f8f581830 | ||
|
|
5f666eff14 | ||
|
|
62a78c86b0 | ||
|
|
35be9ef560 | ||
|
|
94dbeec833 | ||
|
|
3ed992a3fd | ||
|
|
855208ba24 | ||
|
|
ca42db38a3 | ||
|
|
faca7db633 | ||
|
|
450c1850c9 | ||
|
|
0d8ca7a888 | ||
|
|
9c59f62890 | ||
|
|
f3792cdad5 | ||
|
|
b26f2ca3e6 | ||
|
|
62d275d7c0 | ||
|
|
ee5381a376 | ||
|
|
e19c0a5a14 | ||
|
|
30b6bba0fe | ||
|
|
39e6f66775 | ||
|
|
116cfbc221 | ||
|
|
c806a241b8 | ||
|
|
cdab2b7a36 | ||
|
|
c88e517f5b | ||
|
|
8999af2236 | ||
|
|
756ea54470 | ||
|
|
9533aef459 | ||
|
|
ca07011ed2 | ||
|
|
eba81abab9 | ||
|
|
088df89457 | ||
|
|
0aa4879aaa | ||
|
|
decc061939 | ||
|
|
6244e5fcc1 | ||
|
|
26c1c8fb4a | ||
|
|
f5890f6d5e | ||
|
|
20b9bccb92 | ||
|
|
5a3e65826a | ||
|
|
74ec331a46 | ||
|
|
0acd0566e8 | ||
|
|
2a94f4d782 | ||
|
|
16f92c6547 | ||
|
|
cbca7dd47c | ||
|
|
5d695d4af7 | ||
|
|
152d7a9b48 | ||
|
|
4f5117f791 | ||
|
|
5207c2e4bd | ||
|
|
2b5a1a9730 | ||
|
|
b0c932d591 | ||
|
|
fc08c86a07 | ||
|
|
3b003b7245 | ||
|
|
718e4dbc02 | ||
|
|
c522f58947 | ||
|
|
6cae299bd9 | ||
|
|
c165ae939f | ||
|
|
9ab4740d44 | ||
|
|
65617395c6 | ||
|
|
69d86ce2e4 | ||
|
|
796f5cd724 | ||
|
|
bcf2cd4feb | ||
|
|
ed494f00ca | ||
|
|
eda9c014c5 | ||
|
|
1442133f1e | ||
|
|
fd984a3e9d | ||
|
|
128688db63 | ||
|
|
224c8182df | ||
|
|
db06ac411c | ||
|
|
d9dd677448 | ||
|
|
ee955b3206 | ||
|
|
89dd96efbf | ||
|
|
6481e75283 | ||
|
|
b9ef088e80 | ||
|
|
02bb3d032d | ||
|
|
89a4412673 | ||
|
|
06a1511bff | ||
|
|
f02bc896ef | ||
|
|
4cf6843acf | ||
|
|
5184f844bb | ||
|
|
fa07d9d055 | ||
|
|
ca0f40f29e | ||
|
|
ae33d09589 | ||
|
|
d4b9a81956 | ||
|
|
91688d4a34 | ||
|
|
b8caabe25f | ||
|
|
76eca8fef3 | ||
|
|
2334977863 | ||
|
|
c8b02e6ff3 | ||
|
|
1210100421 | ||
|
|
473d54ed5f | ||
|
|
937a076844 | ||
|
|
a2d1346852 | ||
|
|
7275556d61 | ||
|
|
2c6c3864ce | ||
|
|
82f471f48e | ||
|
|
16f10c1321 | ||
|
|
d5dafb35c3 | ||
|
|
f4dfbca04d | ||
|
|
cf1ead7872 | ||
|
|
56735e9d0f | ||
|
|
5b9c41f1b7 | ||
|
|
63845dd9b3 | ||
|
|
b84a42f4cc | ||
|
|
003b64476e | ||
|
|
1e499e616a | ||
|
|
246c3fe8d3 | ||
|
|
ade94a5c0e | ||
|
|
a5a256265f | ||
|
|
be7b9a33ed | ||
|
|
5ce241cbfd | ||
|
|
3bfd64c3cd | ||
|
|
3f419cfa4e | ||
|
|
994137dcf7 | ||
|
|
9242d74bc1 | ||
|
|
6cddf03b5a | ||
|
|
afccf1d2d3 | ||
|
|
de16ef8be6 | ||
|
|
7cd7930344 | ||
|
|
d89400d052 | ||
|
|
658c775f01 | ||
|
|
b1ad42e6d5 | ||
|
|
761139f31c | ||
|
|
c6d93828bd | ||
|
|
614072adcb | ||
|
|
42f6e9933d | ||
|
|
6808bfab92 | ||
|
|
92942071c7 | ||
|
|
8899af09c1 | ||
|
|
6363c1bf00 | ||
|
|
7e41983c06 | ||
|
|
5adb6a36b6 | ||
|
|
b10cb6596e | ||
|
|
c0e849b696 | ||
|
|
2470b7981a | ||
|
|
5c1939e315 | ||
|
|
3a4600cb0a | ||
|
|
a6fe37964a | ||
|
|
e19c685a7e | ||
|
|
42e17bd476 | ||
|
|
97929f657a | ||
|
|
259eab10c5 | ||
|
|
4f1d552953 | ||
|
|
f1b13c4035 | ||
|
|
991f0fca77 | ||
|
|
5232f17829 | ||
|
|
1b5d4258ec | ||
|
|
8417b77f86 | ||
|
|
9799023545 | ||
|
|
ae4e229c9f | ||
|
|
b63f5d1914 | ||
|
|
31d87afc5a | ||
|
|
204618c9d8 | ||
|
|
f0d90d3bdb | ||
|
|
0730dcb4a8 | ||
|
|
9b363e1e5c | ||
|
|
3df67a8347 | ||
|
|
dd4b73a44d | ||
|
|
fd4eee9d62 | ||
|
|
100e9a4436 | ||
|
|
17f344cdda | ||
|
|
c33d9e31cc | ||
|
|
89158eedb5 | ||
|
|
c33c82f345 | ||
|
|
40abcebbe1 | ||
|
|
3bf690a407 | ||
|
|
360ff05e73 | ||
|
|
100e7cc337 | ||
|
|
247cc7013a | ||
|
|
9e865ae4ff | ||
|
|
fef83c9f9c | ||
|
|
8491e26cd4 | ||
|
|
e5fa203d7f | ||
|
|
987ecca24a | ||
|
|
a98b43b994 | ||
|
|
6733f2e5ce | ||
|
|
5c42b84a17 | ||
|
|
6429f2fd6c | ||
|
|
1a2d73dc2b | ||
|
|
a3e99602c0 | ||
|
|
3083226336 | ||
|
|
0e39aa2068 | ||
|
|
4f91e9599f | ||
|
|
5ee5e6e8a0 | ||
|
|
0f2754a682 |
37
.github/workflows/backport.yml
vendored
37
.github/workflows/backport.yml
vendored
@@ -1,37 +0,0 @@
|
||||
name: Backport
|
||||
on:
|
||||
pull_request_target:
|
||||
types: [closed, labeled]
|
||||
permissions:
|
||||
contents: read
|
||||
jobs:
|
||||
backport:
|
||||
name: Backport Pull Request
|
||||
permissions:
|
||||
# for korthout/backport-action
|
||||
contents: write
|
||||
pull-requests: write
|
||||
if: github.repository_owner == 'NixOS' && github.event.pull_request.merged == true && (github.event_name != 'labeled' || startsWith('backport', github.event.label.name))
|
||||
runs-on: ubuntu-24.04-arm
|
||||
steps:
|
||||
- name: Generate GitHub App token
|
||||
id: generate-token
|
||||
uses: actions/create-github-app-token@v2
|
||||
with:
|
||||
app-id: ${{ vars.CI_APP_ID }}
|
||||
private-key: ${{ secrets.CI_APP_PRIVATE_KEY }}
|
||||
- uses: actions/checkout@v6
|
||||
with:
|
||||
ref: ${{ github.event.pull_request.head.sha }}
|
||||
# required to find all branches
|
||||
fetch-depth: 0
|
||||
- name: Create backport PRs
|
||||
uses: korthout/backport-action@c656f5d5851037b2b38fb5db2691a03fa229e3b2 # v4.0.1
|
||||
id: backport
|
||||
with:
|
||||
# Config README: https://github.com/korthout/backport-action#backport-action
|
||||
github_token: ${{ steps.generate-token.outputs.token }}
|
||||
github_workspace: ${{ github.workspace }}
|
||||
auto_merge_enabled: true
|
||||
pull_description: |-
|
||||
Automatic backport to `${target_branch}`, triggered by a label in #${pull_number}.
|
||||
252
.github/workflows/ci.yml
vendored
252
.github/workflows/ci.yml
vendored
@@ -1,252 +0,0 @@
|
||||
name: "CI"
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
merge_group:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
dogfood:
|
||||
description: 'Use dogfood Nix build'
|
||||
required: false
|
||||
default: true
|
||||
type: boolean
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
permissions: read-all
|
||||
|
||||
jobs:
|
||||
eval:
|
||||
runs-on: ubuntu-24.04
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- uses: ./.github/actions/install-nix-action
|
||||
with:
|
||||
dogfood: ${{ github.event_name == 'workflow_dispatch' && inputs.dogfood || github.event_name != 'workflow_dispatch' }}
|
||||
extra_nix_config:
|
||||
experimental-features = nix-command flakes
|
||||
github_token: ${{ secrets.GITHUB_TOKEN }}
|
||||
use_cache: false
|
||||
- run: nix flake show --all-systems --json
|
||||
|
||||
pre-commit-checks:
|
||||
name: pre-commit checks
|
||||
runs-on: ubuntu-24.04
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- uses: ./.github/actions/install-nix-action
|
||||
with:
|
||||
dogfood: ${{ github.event_name == 'workflow_dispatch' && inputs.dogfood || github.event_name != 'workflow_dispatch' }}
|
||||
extra_nix_config: experimental-features = nix-command flakes
|
||||
github_token: ${{ secrets.GITHUB_TOKEN }}
|
||||
- run: ./ci/gha/tests/pre-commit-checks
|
||||
|
||||
basic-checks:
|
||||
name: aggregate basic checks
|
||||
if: ${{ always() }}
|
||||
runs-on: ubuntu-24.04
|
||||
needs: [pre-commit-checks, eval]
|
||||
steps:
|
||||
- name: Exit with any errors
|
||||
if: ${{ contains(needs.*.result, 'failure') || contains(needs.*.result, 'cancelled') }}
|
||||
run: |
|
||||
exit 1
|
||||
|
||||
tests:
|
||||
needs: basic-checks
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
include:
|
||||
- scenario: on ubuntu
|
||||
runs-on: ubuntu-24.04
|
||||
os: linux
|
||||
instrumented: false
|
||||
primary: true
|
||||
stdenv: stdenv
|
||||
- scenario: on macos
|
||||
runs-on: macos-14
|
||||
os: darwin
|
||||
instrumented: false
|
||||
primary: true
|
||||
stdenv: stdenv
|
||||
- scenario: on ubuntu (with sanitizers / coverage)
|
||||
runs-on: ubuntu-24.04
|
||||
os: linux
|
||||
instrumented: true
|
||||
primary: false
|
||||
stdenv: clangStdenv
|
||||
name: tests ${{ matrix.scenario }}
|
||||
runs-on: ${{ matrix.runs-on }}
|
||||
timeout-minutes: 60
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- uses: ./.github/actions/install-nix-action
|
||||
with:
|
||||
github_token: ${{ secrets.GITHUB_TOKEN }}
|
||||
dogfood: ${{ github.event_name == 'workflow_dispatch' && inputs.dogfood || github.event_name != 'workflow_dispatch' }}
|
||||
# The sandbox would otherwise be disabled by default on Darwin
|
||||
extra_nix_config: "sandbox = true"
|
||||
# Since ubuntu 22.30, unprivileged usernamespaces are no longer allowed to map to the root user:
|
||||
# https://ubuntu.com/blog/ubuntu-23-10-restricted-unprivileged-user-namespaces
|
||||
- run: sudo sysctl -w kernel.apparmor_restrict_unprivileged_userns=0
|
||||
if: matrix.os == 'linux'
|
||||
- name: Run component tests
|
||||
run: |
|
||||
nix build --file ci/gha/tests/wrapper.nix componentTests -L \
|
||||
--arg withInstrumentation ${{ matrix.instrumented }} \
|
||||
--argstr stdenv "${{ matrix.stdenv }}"
|
||||
- name: Run VM tests
|
||||
run: |
|
||||
nix build --file ci/gha/tests/wrapper.nix vmTests -L \
|
||||
--arg withInstrumentation ${{ matrix.instrumented }} \
|
||||
--argstr stdenv "${{ matrix.stdenv }}"
|
||||
if: ${{ matrix.os == 'linux' }}
|
||||
- name: Run flake checks and prepare the installer tarball
|
||||
run: |
|
||||
ci/gha/tests/build-checks
|
||||
ci/gha/tests/prepare-installer-for-github-actions
|
||||
if: ${{ matrix.primary }}
|
||||
- name: Collect code coverage
|
||||
run: |
|
||||
nix build --file ci/gha/tests/wrapper.nix codeCoverage.coverageReports -L \
|
||||
--arg withInstrumentation ${{ matrix.instrumented }} \
|
||||
--argstr stdenv "${{ matrix.stdenv }}" \
|
||||
--out-link coverage-reports
|
||||
cat coverage-reports/index.txt >> $GITHUB_STEP_SUMMARY
|
||||
if: ${{ matrix.instrumented }}
|
||||
- name: Upload coverage reports
|
||||
uses: actions/upload-artifact@v6
|
||||
with:
|
||||
name: coverage-reports
|
||||
path: coverage-reports/
|
||||
if: ${{ matrix.instrumented }}
|
||||
- name: Upload installer tarball
|
||||
uses: actions/upload-artifact@v6
|
||||
with:
|
||||
name: installer-${{matrix.os}}
|
||||
path: out/*
|
||||
if: ${{ matrix.primary }}
|
||||
|
||||
installer_test:
|
||||
needs: [tests]
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
include:
|
||||
- scenario: on ubuntu
|
||||
runs-on: ubuntu-24.04
|
||||
os: linux
|
||||
experimental-installer: false
|
||||
- scenario: on macos
|
||||
runs-on: macos-14
|
||||
os: darwin
|
||||
experimental-installer: false
|
||||
- scenario: on ubuntu (experimental)
|
||||
runs-on: ubuntu-24.04
|
||||
os: linux
|
||||
experimental-installer: true
|
||||
- scenario: on macos (experimental)
|
||||
runs-on: macos-14
|
||||
os: darwin
|
||||
experimental-installer: true
|
||||
name: installer test ${{ matrix.scenario }}
|
||||
runs-on: ${{ matrix.runs-on }}
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- name: Download installer tarball
|
||||
uses: actions/download-artifact@37930b1c2abaa49bbe596cd826c3c89aef350131 # v7.0.0
|
||||
with:
|
||||
name: installer-${{matrix.os}}
|
||||
path: out
|
||||
- name: Looking up the installer tarball URL
|
||||
id: installer-tarball-url
|
||||
run: |
|
||||
echo "installer-url=file://$GITHUB_WORKSPACE/out" >> "$GITHUB_OUTPUT"
|
||||
TARBALL_PATH="$(find "$GITHUB_WORKSPACE/out" -name 'nix*.tar.xz' -print | head -n 1)"
|
||||
echo "tarball-path=file://$TARBALL_PATH" >> "$GITHUB_OUTPUT"
|
||||
- uses: cachix/install-nix-action@4e002c8ec80594ecd40e759629461e26c8abed15 # v31.9.0
|
||||
if: ${{ !matrix.experimental-installer }}
|
||||
with:
|
||||
install_url: ${{ format('{0}/install', steps.installer-tarball-url.outputs.installer-url) }}
|
||||
install_options: ${{ format('--tarball-url-prefix {0}', steps.installer-tarball-url.outputs.installer-url) }}
|
||||
- uses: ./.github/actions/install-nix-action
|
||||
if: ${{ matrix.experimental-installer }}
|
||||
with:
|
||||
dogfood: false
|
||||
experimental-installer: true
|
||||
tarball_url: ${{ steps.installer-tarball-url.outputs.tarball-path }}
|
||||
github_token: ${{ secrets.GITHUB_TOKEN }}
|
||||
- run: sudo apt install fish zsh
|
||||
if: matrix.os == 'linux'
|
||||
- run: brew install fish
|
||||
if: matrix.os == 'darwin'
|
||||
- run: exec bash -c "nix-instantiate -E 'builtins.currentTime' --eval"
|
||||
- run: exec sh -c "nix-instantiate -E 'builtins.currentTime' --eval"
|
||||
- run: exec zsh -c "nix-instantiate -E 'builtins.currentTime' --eval"
|
||||
- run: exec fish -c "nix-instantiate -E 'builtins.currentTime' --eval"
|
||||
- run: exec bash -c "nix-channel --add https://releases.nixos.org/nixos/unstable/nixos-23.05pre466020.60c1d71f2ba nixpkgs"
|
||||
- run: exec bash -c "nix-channel --update && nix-env -iA nixpkgs.hello && hello"
|
||||
|
||||
flake_regressions:
|
||||
needs: tests
|
||||
runs-on: ubuntu-24.04
|
||||
steps:
|
||||
- name: Checkout nix
|
||||
uses: actions/checkout@v6
|
||||
- name: Checkout flake-regressions
|
||||
uses: actions/checkout@v6
|
||||
with:
|
||||
repository: NixOS/flake-regressions
|
||||
path: flake-regressions
|
||||
- name: Checkout flake-regressions-data
|
||||
uses: actions/checkout@v6
|
||||
with:
|
||||
repository: NixOS/flake-regressions-data
|
||||
path: flake-regressions/tests
|
||||
- name: Download installer tarball
|
||||
uses: actions/download-artifact@37930b1c2abaa49bbe596cd826c3c89aef350131 # v7.0.0
|
||||
with:
|
||||
name: installer-linux
|
||||
path: out
|
||||
- name: Looking up the installer tarball URL
|
||||
id: installer-tarball-url
|
||||
run: |
|
||||
echo "installer-url=file://$GITHUB_WORKSPACE/out" >> "$GITHUB_OUTPUT"
|
||||
- uses: cachix/install-nix-action@4e002c8ec80594ecd40e759629461e26c8abed15 # v31.9.0
|
||||
with:
|
||||
install_url: ${{ format('{0}/install', steps.installer-tarball-url.outputs.installer-url) }}
|
||||
install_options: ${{ format('--tarball-url-prefix {0}', steps.installer-tarball-url.outputs.installer-url) }}
|
||||
- name: Run flake regressions tests
|
||||
run: MAX_FLAKES=25 flake-regressions/eval-all.sh
|
||||
|
||||
profile_build:
|
||||
needs: tests
|
||||
runs-on: ubuntu-24.04
|
||||
timeout-minutes: 60
|
||||
if: >-
|
||||
github.event_name == 'push' &&
|
||||
github.ref_name == 'master'
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- uses: ./.github/actions/install-nix-action
|
||||
with:
|
||||
github_token: ${{ secrets.GITHUB_TOKEN }}
|
||||
dogfood: ${{ github.event_name == 'workflow_dispatch' && inputs.dogfood || github.event_name != 'workflow_dispatch' }}
|
||||
extra_nix_config: |
|
||||
experimental-features = flakes nix-command ca-derivations impure-derivations
|
||||
max-jobs = 1
|
||||
- run: |
|
||||
nix build -L --file ./ci/gha/profile-build buildTimeReport --out-link build-time-report.md
|
||||
cat build-time-report.md >> $GITHUB_STEP_SUMMARY
|
||||
24
.github/workflows/labels.yml
vendored
24
.github/workflows/labels.yml
vendored
@@ -1,24 +0,0 @@
|
||||
name: "Label PR"
|
||||
|
||||
on:
|
||||
pull_request_target:
|
||||
types: [edited, opened, synchronize, reopened]
|
||||
|
||||
# WARNING:
|
||||
# When extending this action, be aware that $GITHUB_TOKEN allows some write
|
||||
# access to the GitHub API. This means that it should not evaluate user input in
|
||||
# a way that allows code injection.
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
pull-requests: write
|
||||
|
||||
jobs:
|
||||
labels:
|
||||
runs-on: ubuntu-24.04
|
||||
if: github.repository_owner == 'NixOS'
|
||||
steps:
|
||||
- uses: actions/labeler@v6
|
||||
with:
|
||||
repo-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
sync-labels: false
|
||||
80
.github/workflows/upload-release.yml
vendored
80
.github/workflows/upload-release.yml
vendored
@@ -1,80 +0,0 @@
|
||||
name: Upload Release
|
||||
on:
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
eval_id:
|
||||
description: "Hydra evaluation ID"
|
||||
required: true
|
||||
type: number
|
||||
is_latest:
|
||||
description: "Mark as latest release"
|
||||
required: false
|
||||
type: boolean
|
||||
default: false
|
||||
permissions:
|
||||
contents: read
|
||||
id-token: write
|
||||
packages: write
|
||||
jobs:
|
||||
release:
|
||||
runs-on: ubuntu-24.04
|
||||
environment: releases
|
||||
steps:
|
||||
- uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
|
||||
- uses: ./.github/actions/install-nix-action
|
||||
with:
|
||||
dogfood: false # Use stable version
|
||||
use_cache: false # Don't want any cache injection shenanigans
|
||||
extra_nix_config: |
|
||||
experimental-features = nix-command flakes
|
||||
- name: Set NIX_PATH from flake input
|
||||
run: |
|
||||
NIXPKGS_PATH=$(nix build --inputs-from .# nixpkgs#path --print-out-paths --no-link)
|
||||
# Shebangs with perl have issues. Pin nixpkgs this way. nix shell should maybe
|
||||
# get the same uberhack that nix-shell has to support it.
|
||||
echo "NIX_PATH=nixpkgs=$NIXPKGS_PATH" >> "$GITHUB_ENV"
|
||||
- name: Configure AWS credentials
|
||||
uses: aws-actions/configure-aws-credentials@61815dcd50bd041e203e49132bacad1fd04d2708 # v5.1.1
|
||||
with:
|
||||
role-to-assume: "arn:aws:iam::080433136561:role/nix-release"
|
||||
role-session-name: nix-release-oidc-${{ github.run_id }}
|
||||
aws-region: eu-west-1
|
||||
- name: Disable containerd image store
|
||||
run: |
|
||||
# Docker 28+ defaults to the containerd image store, which
|
||||
# pushes layers uncompressed instead of gzip. OCI clients
|
||||
# that only support gzip (e.g. go-containerregistry) fail
|
||||
# with "gzip: invalid header". Disabling the containerd
|
||||
# snapshotter restores the classic storage driver, which
|
||||
# preserves gzip-compressed layers through the
|
||||
# `docker load` / `docker push` pipeline.
|
||||
echo '{"features":{"containerd-snapshotter":false}}' | sudo tee /etc/docker/daemon.json > /dev/null
|
||||
sudo systemctl restart docker
|
||||
- name: Login to Docker Hub
|
||||
uses: docker/login-action@c94ce9fb468520275223c153574b00df6fe4bcc9 # v3.7.0
|
||||
with:
|
||||
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
||||
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||
- name: Login to GitHub Container Registry
|
||||
uses: docker/login-action@c94ce9fb468520275223c153574b00df6fe4bcc9 # v3.7.0
|
||||
with:
|
||||
registry: ghcr.io
|
||||
username: ${{ github.actor }}
|
||||
password: ${{ secrets.GITHUB_TOKEN }}
|
||||
- name: Upload release
|
||||
run: |
|
||||
./maintainers/upload-release.pl \
|
||||
${{ inputs.eval_id }} \
|
||||
--skip-git
|
||||
env:
|
||||
IS_LATEST: ${{ inputs.is_latest && '1' || '' }}
|
||||
- name: Push to GHCR
|
||||
run: |
|
||||
DOCKER_OWNER="ghcr.io/$(echo '${{ github.repository_owner }}' | tr '[A-Z]' '[a-z]')/nix"
|
||||
./maintainers/upload-release.pl \
|
||||
${{ inputs.eval_id }} \
|
||||
--skip-git \
|
||||
--skip-s3 \
|
||||
--docker-owner "$DOCKER_OWNER"
|
||||
env:
|
||||
IS_LATEST: ${{ inputs.is_latest && '1' || '' }}
|
||||
4
.gitignore
vendored
4
.gitignore
vendored
@@ -16,6 +16,10 @@ src/.wraplock
|
||||
/tests/functional/lang/*.err
|
||||
/tests/functional/lang/*.ast
|
||||
|
||||
# /tests/functional/cli-characterisation/
|
||||
/tests/functional/cli-characterisation/*.out
|
||||
/tests/functional/cli-characterisation/*.err
|
||||
|
||||
/outputs
|
||||
|
||||
*~
|
||||
|
||||
30
ci/gha/tests/windows.nix
Normal file
30
ci/gha/tests/windows.nix
Normal file
@@ -0,0 +1,30 @@
|
||||
{
|
||||
nixFlake ? builtins.getFlake ("git+file://" + toString ../../..),
|
||||
system ? builtins.currentSystem,
|
||||
pkgs ? nixFlake.inputs.nixpkgs.legacyPackages.${system},
|
||||
}:
|
||||
|
||||
let
|
||||
packages = nixFlake.packages.${system};
|
||||
|
||||
fixOutput =
|
||||
test:
|
||||
test.overrideAttrs (prev: {
|
||||
nativeBuildInputs = prev.nativeBuildInputs or [ ] ++ [ pkgs.colorized-logs ];
|
||||
env.GTEST_COLOR = "no";
|
||||
# Wine's console emulation wraps every character in ANSI cursor
|
||||
# hide/show sequences, making logs unreadable in GitHub Actions.
|
||||
buildCommand = ''
|
||||
set -o pipefail
|
||||
{
|
||||
${prev.buildCommand}
|
||||
} 2>&1 | ansi2txt
|
||||
'';
|
||||
});
|
||||
in
|
||||
|
||||
{
|
||||
unitTests = {
|
||||
"nix-util-tests" = fixOutput packages."nix-util-tests-x86_64-w64-mingw32".passthru.tests.run;
|
||||
};
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
{
|
||||
lib,
|
||||
stdenv,
|
||||
callPackage,
|
||||
mkMesonDerivation,
|
||||
runCommand,
|
||||
@@ -93,10 +94,11 @@ mkMesonDerivation (finalAttrs: {
|
||||
mdbook
|
||||
json-schema-for-humans
|
||||
]
|
||||
++ lib.optionals (!officialRelease && buildHtmlManual) [
|
||||
++ lib.optionals (!officialRelease && buildHtmlManual && !stdenv.hostPlatform.isi686) [
|
||||
# When not an official release, we likely have changelog entries that have
|
||||
# yet to be rendered.
|
||||
# When released, these are rendered into a committed file to save a dependency.
|
||||
# Broken on i686.
|
||||
changelog-d
|
||||
];
|
||||
|
||||
|
||||
81
doc/manual/rl-next/build-trace-rework.md
Normal file
81
doc/manual/rl-next/build-trace-rework.md
Normal file
@@ -0,0 +1,81 @@
|
||||
---
|
||||
synopsis: "Content-addressed derivations: realisations keyed by store path instead of hash modulo"
|
||||
issues: [11897]
|
||||
prs: [12464]
|
||||
---
|
||||
|
||||
The experimental content-addressed (CA) derivation feature has undergone a significant change to how build traces (formerly called "realisations") are identified. This affects the **binary cache protocol** and the **wire protocols**.
|
||||
|
||||
### What changed
|
||||
|
||||
Previously, a build trace entry (realisation) was keyed by the **hash modulo** of the derivation.
|
||||
A SHA-256 hash computed via the complex "derivation hash modulo" algorithm.
|
||||
This required implementations to understand ATerm serialisation and the full derivation hashing scheme just to look up or store build results.
|
||||
|
||||
Now, build trace entries are keyed by the **regular derivation store path** plus the output name. For example, instead of:
|
||||
|
||||
```
|
||||
sha256:ba7816bf8f01...!out
|
||||
```
|
||||
|
||||
The key is now:
|
||||
|
||||
```
|
||||
/nix/store/abc...-foo.drv^out
|
||||
```
|
||||
|
||||
This is simpler, more intuitive, and means that third-party tools implementing CA derivation support (e.g., Hydra)
|
||||
no longer need to implement the derivation hash modulo algorithm.
|
||||
|
||||
### Binary cache protocol
|
||||
|
||||
- The directory for build traces moved from `realisations/` to `build-trace-v2/`.
|
||||
- File paths changed from `realisations/<hash>!<output>.doi` to `build-trace-v2/<drvName>/<outputName>.doi`.
|
||||
- The JSON format of build trace entries is now split into `key` and `value` objects:
|
||||
```json
|
||||
{
|
||||
"key": {
|
||||
"drvPath": "abc...-foo.drv",
|
||||
"outputName": "out"
|
||||
},
|
||||
"value": {
|
||||
"outPath": "xyz...-foo",
|
||||
"signatures": [{ "keyName": "cache.example.com-1", "sig": "..." }]
|
||||
}
|
||||
}
|
||||
```
|
||||
Previously, these were flat objects with a string `id` field like `"sha256:...!out"`.
|
||||
- The deprecated `dependentRealisations` field has been removed.
|
||||
|
||||
Existing binary caches will need to be re-populated with the new format for CA derivation build traces.
|
||||
Old build traces at the previous URLs are simply abandoned.
|
||||
Non-CA builds are unaffected.
|
||||
|
||||
### Wire protocols
|
||||
|
||||
- **Worker protocol**:
|
||||
A new feature flag `realisation-with-path-not-hash` is negotiated during the handshake.
|
||||
Clients and daemons that both support this feature use the new binary serialisation for `DrvOutput`, `UnkeyedRealisation`, and related types.
|
||||
Fallback to older protocol versions gracefully degrades (realisations are unavailable).
|
||||
- **Serve protocol**:
|
||||
Bumped from 2.7 to 2.8 with native serialisers for the new types.
|
||||
Fallback to older protocol versions gracefully degrades in the same way.
|
||||
|
||||
Stable code paths do use the realization fields (`BuildResult::Success::builtOutputs`), but only the output name and outpath parts of that.
|
||||
For older protocols, we can fake enough of the realisation format to provide those two parts forthat map, which keeps operations like `--print-output-paths` working.
|
||||
|
||||
### Structured signatures
|
||||
|
||||
[Signatures](@docroot@/protocols/json/signature.md) in JSON formats are now represented as structured objects with `keyName` and `sig` fields, rather than colon-separated strings.
|
||||
`nix path-info --json --json-format 3` opts into the new version for this command.
|
||||
JSON parsing accepts both the old string format and new structured format for backwards compatibility.
|
||||
|
||||
### Impact
|
||||
|
||||
- **Non-CA derivation users**: No impact. This only affects the experimental `ca-derivations` feature.
|
||||
- **Binary cache operators**:
|
||||
Binary caches serving CA derivation build traces will need to be repopulated.
|
||||
Existing NARs and narinfo files are unaffected.
|
||||
- **Tool authors**:
|
||||
Implementations interfacing with the CA derivations protocol are simplified.
|
||||
The derivation hash modulo algorithm is no longer required to form build trace keys.
|
||||
@@ -1,9 +0,0 @@
|
||||
---
|
||||
synopsis: "C API: New store API methods"
|
||||
prs: [14766]
|
||||
---
|
||||
|
||||
The C API now includes additional methods:
|
||||
|
||||
- `nix_store_query_path_from_hash_part()` - Get the full store path given its hash part
|
||||
- `nix_store_copy_path()` - Copy a single store path between two stores, allows repairs and configuring signature checking
|
||||
10
doc/manual/rl-next/fix-primop-eval-state.md
Normal file
10
doc/manual/rl-next/fix-primop-eval-state.md
Normal file
@@ -0,0 +1,10 @@
|
||||
---
|
||||
synopsis: "C API: Fix `EvalState` pointer passed to primop callbacks"
|
||||
prs: [15300, 15383]
|
||||
---
|
||||
|
||||
The `EvalState *` passed to C API primop callbacks was incorrectly pointing to
|
||||
the internal `nix::EvalState` rather than the C API wrapper struct. This caused
|
||||
a segfault when the callback used the pointer with C API functions such as
|
||||
`nix_alloc_value()`. The same issue affected `printValueAsJSON` and
|
||||
`printValueAsXML` callbacks on external values.
|
||||
@@ -1,6 +0,0 @@
|
||||
---
|
||||
synopsis: "`builtins.getFlake` now supports path values"
|
||||
prs: [15290]
|
||||
---
|
||||
|
||||
`builtins.getFlake` now accepts path values in addition to flakerefs, allowing you to write `builtins.getFlake ./subflake` instead of having to use ugly workarounds to construct a pure flakeref.
|
||||
7
doc/manual/rl-next/github-fetcher-param-validation.md
Normal file
7
doc/manual/rl-next/github-fetcher-param-validation.md
Normal file
@@ -0,0 +1,7 @@
|
||||
---
|
||||
synopsis: GitHub fetcher now validates URL parameters
|
||||
prs: [15331]
|
||||
issues: [15304]
|
||||
---
|
||||
|
||||
The `github:` fetcher now validates URL parameters, and will error if an invalid parameter like `tag` is provided.
|
||||
@@ -1,10 +0,0 @@
|
||||
---
|
||||
synopsis: "New setting `ignore-gc-delete-failure` for local stores"
|
||||
prs: [15054]
|
||||
---
|
||||
|
||||
A new local store setting [`ignore-gc-delete-failure`](@docroot@/store/types/local-store.md#store-local-store-ignore-gc-delete-failure) has been added.
|
||||
When enabled, garbage collection will log warnings instead of failing when it cannot delete store paths.
|
||||
This is useful when running Nix as an unprivileged user that may not have write access to all paths in the store.
|
||||
|
||||
This setting is experimental and requires the [`local-overlay-store`](@docroot@/development/experimental-features.md#xp-feature-local-overlay-store) experimental feature.
|
||||
@@ -1,15 +0,0 @@
|
||||
---
|
||||
synopsis: Support HTTPS binary caches using mTLS (client certificate) authentication
|
||||
issues: [13002]
|
||||
prs: [13030]
|
||||
---
|
||||
|
||||
Added support for `tls-certificate` and `tls-private-key` options in substituter URLs.
|
||||
|
||||
Example:
|
||||
|
||||
```
|
||||
https://substituter.invalid?tls-certificate=/path/to/cert.pem&tls-private-key=/path/to/key.pem
|
||||
```
|
||||
|
||||
When these options are configured, Nix will use this certificate/private key pair to authenticate to the server.
|
||||
@@ -1,11 +0,0 @@
|
||||
---
|
||||
synopsis: New command `nix store roots-daemon` for serving GC roots
|
||||
prs: [15143]
|
||||
---
|
||||
|
||||
New command [`nix store roots-daemon`](@docroot@/command-ref/new-cli/nix3-store-roots-daemon.md) runs a daemon that serves garbage collector roots over a Unix domain socket.
|
||||
It enables the garbage collector to discover runtime roots when the main Nix daemon doesn't have `CAP_SYS_PTRACE` capability and therefore cannot scan `/proc`.
|
||||
|
||||
The garbage collector can be configured to use this daemon via the [`use-roots-daemon`](@docroot@/store/types/local-store.md#store-experimental-option-use-roots-daemon) store setting.
|
||||
|
||||
This feature requires the [`local-overlay-store` experimental feature](@docroot@/development/experimental-features.md#xp-feature-local-overlay-store).
|
||||
@@ -1,32 +0,0 @@
|
||||
---
|
||||
synopsis: S3 binary caches now use virtual-hosted-style addressing by default
|
||||
issues: [15208]
|
||||
---
|
||||
|
||||
S3 binary caches now use virtual-hosted-style URLs
|
||||
(`https://bucket.s3.region.amazonaws.com/key`) instead of path-style URLs
|
||||
(`https://s3.region.amazonaws.com/bucket/key`) when connecting to standard AWS
|
||||
S3 endpoints. This enables HTTP/2 multiplexing and fixes TCP connection
|
||||
exhaustion (TIME_WAIT socket accumulation) under high-concurrency workloads.
|
||||
|
||||
A new `addressing-style` store option controls this behavior:
|
||||
|
||||
- `auto` (default): virtual-hosted-style for standard AWS endpoints, path-style
|
||||
for custom endpoints.
|
||||
- `path`: forces path-style addressing (deprecated by AWS).
|
||||
- `virtual`: forces virtual-hosted-style addressing (bucket names must not
|
||||
contain dots).
|
||||
|
||||
Bucket names containing dots (e.g., `my.bucket.name`) automatically fall back
|
||||
to path-style addressing in `auto` mode, because dotted names create
|
||||
multi-level subdomains that break TLS wildcard certificate validation.
|
||||
|
||||
Example using path-style for backwards compatibility:
|
||||
|
||||
```
|
||||
s3://my-bucket/key?region=us-east-1&addressing-style=path
|
||||
```
|
||||
|
||||
Additionally, TCP keep-alive is now enabled on all HTTP connections, preventing
|
||||
idle connections from being silently dropped by intermediate network devices
|
||||
(NATs, firewalls, load balancers).
|
||||
@@ -125,6 +125,7 @@
|
||||
- [Hash](protocols/json/hash.md)
|
||||
- [Content Address](protocols/json/content-address.md)
|
||||
- [Store Path](protocols/json/store-path.md)
|
||||
- [Signature](protocols/json/signature.md)
|
||||
- [Store Object Info](protocols/json/store-object-info.md)
|
||||
- [Derivation](protocols/json/derivation/index.md)
|
||||
- [Derivation Options](protocols/json/derivation/options.md)
|
||||
@@ -153,6 +154,7 @@
|
||||
- [Contributing](development/contributing.md)
|
||||
- [Releases](release-notes/index.md)
|
||||
{{#include ./SUMMARY-rl-next.md}}
|
||||
- [Release 2.34 (2026-02-27)](release-notes/rl-2.34.md)
|
||||
- [Release 2.33 (2025-12-09)](release-notes/rl-2.33.md)
|
||||
- [Release 2.32 (2025-10-06)](release-notes/rl-2.32.md)
|
||||
- [Release 2.31 (2025-08-21)](release-notes/rl-2.31.md)
|
||||
|
||||
@@ -39,7 +39,7 @@
|
||||
This sandbox by default only allows reading from store objects specified as inputs, and only allows writing to designated [outputs][output] to be [captured as store objects](@docroot@/store/building.md#processing-outputs).
|
||||
|
||||
A derivation is typically specified as a [derivation expression] in the [Nix language], and [instantiated][instantiate] to a [store derivation].
|
||||
There are multiple ways of obtaining store objects from store derivatons, collectively called [realisation][realise].
|
||||
There are multiple ways of obtaining store objects from store derivations, collectively called [realisation][realise].
|
||||
|
||||
[derivation]: #gloss-derivation
|
||||
|
||||
|
||||
@@ -16,30 +16,29 @@ If you are on Linux with systemd:
|
||||
sudo systemctl daemon-reload
|
||||
```
|
||||
|
||||
Remove files created by Nix:
|
||||
2. Remove files created by Nix:
|
||||
|
||||
```console
|
||||
sudo rm -rf /etc/nix /etc/profile.d/nix.sh /etc/tmpfiles.d/nix-daemon.conf /nix ~root/.nix-channels ~root/.nix-defexpr ~root/.nix-profile ~root/.cache/nix
|
||||
```
|
||||
```console
|
||||
sudo rm -rf /etc/nix /etc/profile.d/nix.sh /etc/tmpfiles.d/nix-daemon.conf /nix ~/.local/share/nix ~/.local/state/nix ~/.cache/nix ~/.nix-defexpr ~/.nix-profile ~/.nix-channels ~root/.nix-channels ~root/.nix-defexpr ~root/.nix-profile ~root/.cache/nix
|
||||
```
|
||||
|
||||
Remove build users and their group:
|
||||
3. Remove build users and their group:
|
||||
|
||||
```console
|
||||
for i in $(seq 1 32); do
|
||||
sudo userdel nixbld$i
|
||||
done
|
||||
sudo groupdel nixbld
|
||||
```
|
||||
```console
|
||||
for i in $(seq 1 32); do
|
||||
sudo userdel nixbld$i
|
||||
done
|
||||
sudo groupdel nixbld
|
||||
```
|
||||
|
||||
There may also be references to Nix in
|
||||
4. There may also be references to Nix in
|
||||
- `/etc/bash.bashrc`
|
||||
- `/etc/bashrc`
|
||||
- `/etc/profile`
|
||||
- `/etc/zsh/zshrc`
|
||||
- `/etc/zshrc`
|
||||
|
||||
- `/etc/bash.bashrc`
|
||||
- `/etc/bashrc`
|
||||
- `/etc/profile`
|
||||
- `/etc/zsh/zshrc`
|
||||
- `/etc/zshrc`
|
||||
|
||||
which you may remove.
|
||||
which you may remove.
|
||||
|
||||
### FreeBSD
|
||||
|
||||
@@ -54,7 +53,7 @@ which you may remove.
|
||||
2. Remove files created by Nix:
|
||||
|
||||
```console
|
||||
sudo rm -rf /etc/nix /usr/local/etc/profile.d/nix.sh /nix ~root/.nix-channels ~root/.nix-defexpr ~root/.nix-profile ~root/.cache/nix
|
||||
sudo rm -rf /etc/nix /usr/local/etc/profile.d/nix.sh /nix ~/.local/share/nix ~/.local/state/nix ~/.cache/nix ~/.nix-defexpr ~/.nix-profile ~/.nix-channels ~root/.nix-channels ~root/.nix-defexpr ~root/.nix-profile ~root/.cache/nix
|
||||
```
|
||||
|
||||
3. Remove build users and their group:
|
||||
@@ -154,7 +153,7 @@ which you may remove.
|
||||
6. Remove the files Nix added to your system, except for the store:
|
||||
|
||||
```console
|
||||
sudo rm -rf /etc/nix /var/root/.nix-profile /var/root/.nix-defexpr /var/root/.nix-channels ~/.nix-profile ~/.nix-defexpr ~/.nix-channels
|
||||
sudo rm -rf /etc/nix /var/root/.nix-profile /var/root/.nix-defexpr /var/root/.nix-channels ~/.nix-profile ~/.nix-defexpr ~/.nix-channels ~/.local/share/nix ~/.local/state/nix ~/.cache/nix
|
||||
```
|
||||
|
||||
|
||||
@@ -192,6 +191,6 @@ which you may remove.
|
||||
To remove a [single-user installation](./installing-binary.md#single-user-installation) of Nix, run:
|
||||
|
||||
```console
|
||||
rm -rf /nix ~/.nix-channels ~/.nix-defexpr ~/.nix-profile
|
||||
rm -rf /nix ~/.nix-channels ~/.nix-defexpr ~/.nix-profile ~/.local/share/nix ~/.local/state/nix ~/.cache/nix
|
||||
```
|
||||
You might also want to manually remove references to Nix from your `~/.profile`.
|
||||
|
||||
@@ -1,21 +1,21 @@
|
||||
{{#include build-trace-entry-v2-fixed.md}}
|
||||
{{#include build-trace-entry-v3-fixed.md}}
|
||||
|
||||
## Examples
|
||||
|
||||
### Simple build trace entry
|
||||
|
||||
```json
|
||||
{{#include schema/build-trace-entry-v2/simple.json}}
|
||||
{{#include schema/build-trace-entry-v3/simple.json}}
|
||||
```
|
||||
|
||||
### Build trace entry with signature
|
||||
|
||||
```json
|
||||
{{#include schema/build-trace-entry-v2/with-signature.json}}
|
||||
{{#include schema/build-trace-entry-v3/with-structured-signature.json}}
|
||||
```
|
||||
|
||||
<!--
|
||||
## Raw Schema
|
||||
|
||||
[JSON Schema for Build Trace Entry v1](schema/build-trace-entry-v2.json)
|
||||
[JSON Schema for Build Trace Entry v1](schema/build-trace-entry-v3.json)
|
||||
-->
|
||||
|
||||
@@ -13,11 +13,12 @@ schemas = [
|
||||
'hash-v1',
|
||||
'content-address-v1',
|
||||
'store-path-v1',
|
||||
'store-object-info-v2',
|
||||
'signature-v2',
|
||||
'store-object-info-v3',
|
||||
'derivation-v4',
|
||||
'derivation-options-v1',
|
||||
'deriving-path-v1',
|
||||
'build-trace-entry-v2',
|
||||
'build-trace-entry-v3',
|
||||
'build-result-v1',
|
||||
'store-v1',
|
||||
]
|
||||
|
||||
@@ -83,7 +83,7 @@ properties:
|
||||
description: |
|
||||
A mapping from output names to their build trace entries.
|
||||
additionalProperties:
|
||||
"$ref": "build-trace-entry-v2.yaml"
|
||||
"$ref": "build-trace-entry-v3.yaml#/$defs/value"
|
||||
|
||||
failure:
|
||||
type: object
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
"$schema": "http://json-schema.org/draft-04/schema"
|
||||
"$id": "https://nix.dev/manual/nix/latest/protocols/json/schema/build-trace-entry-v2.json"
|
||||
"$id": "https://nix.dev/manual/nix/latest/protocols/json/schema/build-trace-entry-v3.json"
|
||||
title: Build Trace Entry
|
||||
description: |
|
||||
A record of a successful build outcome for a specific derivation output.
|
||||
@@ -12,28 +12,28 @@ description: |
|
||||
> [**experimental**](@docroot@/development/experimental-features.md#xp-feature-ca-derivations)
|
||||
> and subject to change.
|
||||
|
||||
Verision history:
|
||||
## Version History
|
||||
|
||||
- Version 1: Original format
|
||||
|
||||
- Version 2: Remove `dependentRealisations`
|
||||
- Version 2:
|
||||
- Remove `dependentRealisations`
|
||||
|
||||
- Version 3:
|
||||
- Use `drvPath` not `drvHash` to refer to derivation in a more conventional way.
|
||||
- Separate into `key` and `value`
|
||||
- Use 2nd version of signatures format (objects, not strings)
|
||||
|
||||
type: object
|
||||
required:
|
||||
- id
|
||||
- outPath
|
||||
- signatures
|
||||
allOf:
|
||||
- "$ref": "#/$defs/key"
|
||||
- "$ref": "#/$defs/value"
|
||||
- key
|
||||
- value
|
||||
properties:
|
||||
id: {}
|
||||
outPath: {}
|
||||
signatures: {}
|
||||
additionalProperties:
|
||||
dependentRealisations:
|
||||
description: deprecated field
|
||||
type: object
|
||||
key:
|
||||
"$ref": "#/$defs/key"
|
||||
value:
|
||||
"$ref": "#/$defs/value"
|
||||
additionalProperties: false
|
||||
|
||||
"$defs":
|
||||
key:
|
||||
@@ -43,23 +43,20 @@ additionalProperties:
|
||||
This is the "key" part, refering to a derivation and output.
|
||||
type: object
|
||||
required:
|
||||
- id
|
||||
- drvPath
|
||||
- outputName
|
||||
properties:
|
||||
id:
|
||||
type: string
|
||||
title: Derivation Output ID
|
||||
pattern: "^sha256:[0-9a-f]{64}![a-zA-Z_][a-zA-Z0-9_-]*$"
|
||||
drvPath:
|
||||
"$ref": "store-path-v1.yaml"
|
||||
title: Derivation Path
|
||||
description: |
|
||||
Unique identifier for the derivation output that was built.
|
||||
|
||||
Format: `{hash-quotient-drv}!{output-name}`
|
||||
|
||||
- **hash-quotient-drv**: SHA-256 [hash of the quotient derivation](@docroot@/store/derivation/outputs/input-address.md#hash-quotient-drv).
|
||||
Begins with `sha256:`.
|
||||
|
||||
- **output-name**: Name of the specific output (e.g., "out", "dev", "doc")
|
||||
|
||||
Example: `"sha256:ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad!foo"`
|
||||
The store path of the derivation that was built.
|
||||
outputName:
|
||||
type: string
|
||||
title: Output Name
|
||||
description: |
|
||||
Name of the specific output (e.g., "out", "dev", "doc")
|
||||
additionalProperties: false
|
||||
|
||||
value:
|
||||
title: Build Trace Value
|
||||
@@ -77,19 +74,10 @@ additionalProperties:
|
||||
description: |
|
||||
The path to the store object that resulted from building this derivation for the given output name.
|
||||
|
||||
patternProperties:
|
||||
"^sha256:[0-9a-f]{64}![a-zA-Z_][a-zA-Z0-9_-]*$":
|
||||
"$ref": "store-path-v1.yaml"
|
||||
title: Dependent Store Path
|
||||
description: Store path that this dependency resolved to during the build
|
||||
additionalProperties: false
|
||||
|
||||
signatures:
|
||||
type: array
|
||||
title: Build Signatures
|
||||
description: |
|
||||
A set of cryptographic signatures attesting to the authenticity of this build trace entry.
|
||||
items:
|
||||
type: string
|
||||
title: Signature
|
||||
description: A single cryptographic signature
|
||||
"$ref": "signature-v2.yaml"
|
||||
@@ -1 +1 @@
|
||||
../../../../../../src/libutil-tests/data/hash/
|
||||
../../../../../../src/libutil-tests/data/hash
|
||||
1
doc/manual/source/protocols/json/schema/nar-info-v3
Symbolic link
1
doc/manual/source/protocols/json/schema/nar-info-v3
Symbolic link
@@ -0,0 +1 @@
|
||||
../../../../../../src/libstore-tests/data/nar-info/json-3
|
||||
33
doc/manual/source/protocols/json/schema/signature-v2.yaml
Normal file
33
doc/manual/source/protocols/json/schema/signature-v2.yaml
Normal file
@@ -0,0 +1,33 @@
|
||||
"$schema": "http://json-schema.org/draft-07/schema"
|
||||
"$id": "https://nix.dev/manual/nix/latest/protocols/json/schema/signature-v2.json"
|
||||
title: Signature
|
||||
description: |
|
||||
A cryptographic signature along with the name of the key that produced it.
|
||||
|
||||
This schema describes the JSON representation of signatures as used in various Nix JSON APIs.
|
||||
|
||||
> **Warning**
|
||||
>
|
||||
> This JSON format is currently
|
||||
> [**experimental**](@docroot@/development/experimental-features.md#xp-feature-nix-command)
|
||||
> and subject to change.
|
||||
|
||||
## Version History
|
||||
|
||||
- Version 1: Colon-separated string in the format `<key-name>:<signature-in-Base64>`
|
||||
|
||||
- Version 2: Structured object with `keyName` and `sig` fields
|
||||
|
||||
type: object
|
||||
required:
|
||||
- keyName
|
||||
- sig
|
||||
properties:
|
||||
keyName:
|
||||
type: string
|
||||
title: Key Name
|
||||
description: The name of the key used to produce this signature
|
||||
sig:
|
||||
type: string
|
||||
title: Signature Data
|
||||
description: The raw signature bytes, Base64-encoded
|
||||
@@ -0,0 +1,4 @@
|
||||
{
|
||||
"keyName": "cache.nixos.org-1",
|
||||
"sig": "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
|
||||
}
|
||||
@@ -1 +0,0 @@
|
||||
../../../../../../src/libstore-tests/data/path-info/json-2
|
||||
1
doc/manual/source/protocols/json/schema/store-object-info-v3
Symbolic link
1
doc/manual/source/protocols/json/schema/store-object-info-v3
Symbolic link
@@ -0,0 +1 @@
|
||||
../../../../../../src/libstore-tests/data/path-info/json-3
|
||||
@@ -1,6 +1,6 @@
|
||||
"$schema": "http://json-schema.org/draft-04/schema"
|
||||
"$id": "https://nix.dev/manual/nix/latest/protocols/json/schema/store-object-info-v2.json"
|
||||
title: Store Object Info v2
|
||||
"$id": "https://nix.dev/manual/nix/latest/protocols/json/schema/store-object-info-v3.json"
|
||||
title: Store Object Info v3
|
||||
description: |
|
||||
Information about a [store object](@docroot@/store/store-object.md).
|
||||
|
||||
@@ -50,10 +50,10 @@ $defs:
|
||||
properties:
|
||||
version:
|
||||
type: integer
|
||||
const: 2
|
||||
title: Format version (must be 2)
|
||||
const: 3
|
||||
title: Format version (must be 3)
|
||||
description: |
|
||||
Must be `2`.
|
||||
Must be `3`.
|
||||
This is a guard that allows us to continue evolving this format.
|
||||
Here is the rough version history:
|
||||
|
||||
@@ -63,6 +63,8 @@ $defs:
|
||||
|
||||
- Version 2: Use structured JSON type for `ca`
|
||||
|
||||
- Version 3: Use structured JSON type for `signatures`
|
||||
|
||||
path:
|
||||
"$ref": "./store-path-v1.yaml"
|
||||
title: Store Path
|
||||
@@ -174,7 +176,7 @@ $defs:
|
||||
|
||||
> This is an "impure" field that may not be included in certain contexts.
|
||||
items:
|
||||
type: string
|
||||
"$ref": "./signature-v2.yaml"
|
||||
|
||||
# Computed closure fields
|
||||
closureSize:
|
||||
@@ -37,7 +37,7 @@ properties:
|
||||
- contents
|
||||
properties:
|
||||
info:
|
||||
"$ref": "./store-object-info-v2.yaml#/$defs/impure"
|
||||
"$ref": "./store-object-info-v3.yaml#/$defs/impure"
|
||||
title: Store Object Info
|
||||
description: |
|
||||
Metadata about the [store object](@docroot@/store/store-object.md) including hash, size, references, etc.
|
||||
@@ -70,7 +70,7 @@ properties:
|
||||
"^[A-Za-z0-9+/]{43}=$":
|
||||
type: object
|
||||
additionalProperties:
|
||||
"$ref": "./build-trace-entry-v2.yaml#/$defs/value"
|
||||
"$ref": "./build-trace-entry-v3.yaml#/$defs/value"
|
||||
additionalProperties: false
|
||||
|
||||
"$defs":
|
||||
|
||||
9
doc/manual/source/protocols/json/signature.md
Normal file
9
doc/manual/source/protocols/json/signature.md
Normal file
@@ -0,0 +1,9 @@
|
||||
{{#include signature-v2-fixed.md}}
|
||||
|
||||
## Examples
|
||||
|
||||
### Simple signature
|
||||
|
||||
```json
|
||||
{{#include schema/signature-v2/simple.json}}
|
||||
```
|
||||
@@ -1,45 +1,45 @@
|
||||
{{#include store-object-info-v2-fixed.md}}
|
||||
{{#include store-object-info-v3-fixed.md}}
|
||||
|
||||
## Examples
|
||||
|
||||
### Minimal store object (content-addressed)
|
||||
|
||||
```json
|
||||
{{#include schema/store-object-info-v2/pure.json}}
|
||||
{{#include schema/store-object-info-v3/pure.json}}
|
||||
```
|
||||
|
||||
### Store object with impure fields
|
||||
|
||||
```json
|
||||
{{#include schema/store-object-info-v2/impure.json}}
|
||||
{{#include schema/store-object-info-v3/impure.json}}
|
||||
```
|
||||
|
||||
### Minimal store object (empty)
|
||||
|
||||
```json
|
||||
{{#include schema/store-object-info-v2/empty_pure.json}}
|
||||
{{#include schema/store-object-info-v3/empty_pure.json}}
|
||||
```
|
||||
|
||||
### Store object with all impure fields
|
||||
|
||||
```json
|
||||
{{#include schema/store-object-info-v2/empty_impure.json}}
|
||||
{{#include schema/store-object-info-v3/empty_impure.json}}
|
||||
```
|
||||
|
||||
### NAR info (minimal)
|
||||
|
||||
```json
|
||||
{{#include schema/nar-info-v2/pure.json}}
|
||||
{{#include schema/nar-info-v3/pure.json}}
|
||||
```
|
||||
|
||||
### NAR info (with binary cache fields)
|
||||
|
||||
```json
|
||||
{{#include schema/nar-info-v2/impure.json}}
|
||||
{{#include schema/nar-info-v3/impure.json}}
|
||||
```
|
||||
|
||||
<!-- need to convert YAML to JSON first
|
||||
## Raw Schema
|
||||
|
||||
[JSON Schema for Store Object Info v1](schema/store-object-info-v2.json)
|
||||
[JSON Schema for Store Object Info v1](schema/store-object-info-v3.json)
|
||||
-->
|
||||
|
||||
352
doc/manual/source/release-notes/rl-2.34.md
Normal file
352
doc/manual/source/release-notes/rl-2.34.md
Normal file
@@ -0,0 +1,352 @@
|
||||
# Release 2.34.0 (2026-02-27)
|
||||
|
||||
## Highlights
|
||||
|
||||
- Rust nix-installer in beta
|
||||
|
||||
The Rust-based rewrite of the Nix installer is now in beta.
|
||||
We'd love help testing it out!
|
||||
|
||||
To test out the new installer, run:
|
||||
```
|
||||
curl -sSfL https://artifacts.nixos.org/nix-installer | sh -s -- install
|
||||
```
|
||||
|
||||
This installer can be run even when you have an existing, script-based Nix installation without any adjustments.
|
||||
|
||||
This new installer also comes with the ability to uninstall your Nix installation; run:
|
||||
```
|
||||
/nix/nix-installer uninstall
|
||||
```
|
||||
|
||||
This will get rid of your entire Nix installation (even if you installed over an existing, script-based installation).
|
||||
|
||||
This installer is a modified version of the [Determinate Nix Installer](https://github.com/DeterminateSystems/nix-installer) by Determinate Systems.
|
||||
Thanks to Determinate Systems for all the investment they've put into the installer.
|
||||
|
||||
Source for the installer is in <https://github.com/NixOS/nix-installer>.
|
||||
Report any issues in that repo.
|
||||
|
||||
For CI usage, a GitHub Action to install Nix using this installer is available at <https://github.com/NixOS/nix-installer-action>.
|
||||
|
||||
- Stabilisation of `no-url-literals` experimental feature and new diagnostics infrastructure, with `lint-url-literals`, `lint-short-path-literals`, and `lint-absolute-path-literals` settings [#8738](https://github.com/NixOS/nix/issues/8738) [#10048](https://github.com/NixOS/nix/issues/10048) [#10281](https://github.com/NixOS/nix/issues/10281) [#15326](https://github.com/NixOS/nix/pull/15326)
|
||||
|
||||
Experimental feature `no-url-literals` has been stabilised and is now controlled by the `lint-url-literals` option.
|
||||
New diagnostics infrastructure has been added for linting discouraged language features.
|
||||
|
||||
### New lint infrastructure
|
||||
|
||||
#### [`lint-url-literals`](@docroot@/command-ref/conf-file.md#conf-lint-url-literals)
|
||||
|
||||
The `no-url-literals` experimental feature has been stabilised and replaced with a new [`lint-url-literals`](@docroot@/command-ref/conf-file.md#conf-lint-url-literals) setting.
|
||||
|
||||
To migrate from the experimental feature, replace:
|
||||
```
|
||||
experimental-features = no-url-literals
|
||||
```
|
||||
with:
|
||||
```
|
||||
lint-url-literals = fatal
|
||||
```
|
||||
|
||||
#### [`lint-short-path-literals`](@docroot@/command-ref/conf-file.md#conf-lint-short-path-literals)
|
||||
|
||||
The [`warn-short-path-literals`](@docroot@/command-ref/conf-file.md#conf-warn-short-path-literals) boolean setting has been deprecated and replaced with [`lint-short-path-literals`](@docroot@/command-ref/conf-file.md#conf-lint-short-path-literals).
|
||||
|
||||
To migrate, replace:
|
||||
```
|
||||
warn-short-path-literals = true
|
||||
```
|
||||
with:
|
||||
```
|
||||
lint-short-path-literals = warn
|
||||
```
|
||||
|
||||
#### [`lint-absolute-path-literals`](@docroot@/command-ref/conf-file.md#conf-lint-absolute-path-literals)
|
||||
|
||||
A new [`lint-absolute-path-literals`](@docroot@/command-ref/conf-file.md#conf-lint-absolute-path-literals) setting has been added to control handling of absolute path literals (paths starting with `/`) and home path literals (paths starting with `~/`).
|
||||
|
||||
#### Setting values
|
||||
|
||||
All three settings accept three values:
|
||||
- `ignore`: Allow the feature without emitting any diagnostic (default)
|
||||
- `warn`: Emit a warning when the feature is used
|
||||
- `fatal`: Treat the feature as a parse error
|
||||
|
||||
The defaults may change in future versions.
|
||||
|
||||
- Improved parser error messages [#15092](https://github.com/NixOS/nix/pull/15092)
|
||||
|
||||
Parser error messages now use legible strings for tokens instead of internal names. For example, malformed expression `a ++ ++ b` now produces the following error:
|
||||
```
|
||||
error: syntax error, unexpected '++'
|
||||
at «string»:1:6:
|
||||
1| a ++ ++ b
|
||||
| ^
|
||||
```
|
||||
|
||||
Instead of:
|
||||
```
|
||||
error: syntax error, unexpected CONCAT
|
||||
at «string»:1:6:
|
||||
1| a ++ ++ b
|
||||
| ^
|
||||
```
|
||||
|
||||
## New features
|
||||
|
||||
- `nix repl` now supports `inherit` and multiple bindings [#15082](https://github.com/NixOS/nix/pull/15082)
|
||||
|
||||
The `nix repl` now supports `inherit` statements and multiple bindings per line:
|
||||
|
||||
```
|
||||
nix-repl> a = { x = 1; y = 2; }
|
||||
nix-repl> inherit (a) x y
|
||||
nix-repl> x + y
|
||||
3
|
||||
|
||||
nix-repl> p = 1; q = 2;
|
||||
nix-repl> p + q
|
||||
3
|
||||
|
||||
nix-repl> foo.bar.baz = 1;
|
||||
nix-repl> foo.bar
|
||||
{ baz = 1; }
|
||||
```
|
||||
|
||||
- New command `nix store roots-daemon` for serving GC roots [#15143](https://github.com/NixOS/nix/pull/15143)
|
||||
|
||||
New command [`nix store roots-daemon`](@docroot@/command-ref/new-cli/nix3-store-roots-daemon.md) runs a daemon that serves garbage collector roots over a Unix domain socket.
|
||||
It enables the garbage collector to discover runtime roots when the main Nix daemon doesn't have `CAP_SYS_PTRACE` capability and therefore cannot scan `/proc`.
|
||||
|
||||
The garbage collector can be configured to use this daemon via the [`use-roots-daemon`](@docroot@/store/types/local-store.md#store-experimental-option-use-roots-daemon) store setting.
|
||||
|
||||
This feature requires the [`local-overlay-store` experimental feature](@docroot@/development/experimental-features.md#xp-feature-local-overlay-store).
|
||||
|
||||
- New command `nix-nswrapper` in `libexec` [#15183](https://github.com/NixOS/nix/pull/15183)
|
||||
|
||||
The new command `libexec/nix-nswrapper` is used to run the Nix daemon in an unprivileged user namespace on Linux. In order to use this command, build user UIDs and GIDs must be allocated in `/etc/subuid` and `/etc/subgid`.
|
||||
|
||||
It can be used to run the Nix daemon with full sandboxing without executing as root. Support has been added to Nixpkgs with the new `nix.daemonUser` and `nix.daemonGroup` settings.
|
||||
|
||||
- New setting `ignore-gc-delete-failure` for local stores [#15054](https://github.com/NixOS/nix/pull/15054)
|
||||
|
||||
A new local store setting [`ignore-gc-delete-failure`](@docroot@/store/types/local-store.md#store-local-store-ignore-gc-delete-failure) has been added.
|
||||
When enabled, garbage collection will log warnings instead of failing when it cannot delete store paths.
|
||||
This is useful when running Nix as an unprivileged user that may not have write access to all paths in the store.
|
||||
|
||||
This setting is experimental and requires the [`local-overlay-store`](@docroot@/development/experimental-features.md#xp-feature-local-overlay-store) experimental feature.
|
||||
|
||||
- New setting `narinfo-cache-meta-ttl` [#15287](https://github.com/NixOS/nix/pull/15287)
|
||||
|
||||
The new setting `narinfo-cache-meta-ttl` controls how long binary cache metadata (i.e. `/nix-cache-info`) is cached locally, in seconds. This was previously hard-coded to 7 days, which is still the default. As a result, you can now use `nix store info --refresh` to check whether a binary cache is still valid.
|
||||
|
||||
- Support HTTPS binary caches using mTLS (client certificate) authentication [#13002](https://github.com/NixOS/nix/issues/13002) [#13030](https://github.com/NixOS/nix/pull/13030)
|
||||
|
||||
Added support for `tls-certificate` and `tls-private-key` options in substituter URLs.
|
||||
|
||||
Example:
|
||||
|
||||
```
|
||||
https://substituter.invalid?tls-certificate=/path/to/cert.pem&tls-private-key=/path/to/key.pem
|
||||
```
|
||||
|
||||
When these options are configured, Nix will use this certificate/private key pair to authenticate to the server.
|
||||
|
||||
- `nix store gc --dry-run` and `nix-collect-garbage --dry-run` now report the number of paths that would be freed [#15229](https://github.com/NixOS/nix/pull/15229) [#5704](https://github.com/NixOS/nix/issues/5704)
|
||||
|
||||
## Performance improvements
|
||||
|
||||
- Unpacking tarballs to `~/.cache/nix/tarball-cache-v2` is now multithreaded [#12087](https://github.com/NixOS/nix/pull/12087)
|
||||
|
||||
Content-addressed cache for `builtins.fetchTarball` and tarball-based flake inputs (e.g. `github:NixOS/nixpkgs`, `https://channels.nixos.org/nixos-25.11/nixexprs.tar.xz`) now writes git blobs (files) to the `tarball-cache-v2` repository concurrently, which significantly reduces the wall time for tarball unpacking (up to ~1.8x faster unpacking for `https://channels.nixos.org/nixos-25.11/nixexprs.tar.xz` in our testing).
|
||||
|
||||
Currently, Nix doesn't perform any maintenance on the `~/.cache/nix/tarball-cache-v2` repository, which will be addressed in future versions. Users that wish to reclaim disk space used by the tarball cache may want to run:
|
||||
|
||||
```
|
||||
rm -rf ~/.cache/nix/tarball-cache # Historical tarball-cache, not used by Nix >= 2.33
|
||||
cd ~/.cache/nix/tarball-cache-v2 && git multi-pack-index write && git multi-pack-index repack && git multi-pack-index expire
|
||||
```
|
||||
|
||||
- `nix nar ls` and other NAR listing operations have been optimised further [#15163](https://github.com/NixOS/nix/pull/15163)
|
||||
|
||||
- Evaluator hot-path optimizations [#15270](https://github.com/NixOS/nix/pull/15270) [#15271](https://github.com/NixOS/nix/pull/15271)
|
||||
|
||||
## C API Changes
|
||||
|
||||
- New store API methods [#14766](https://github.com/NixOS/nix/pull/14766) [#14768](https://github.com/NixOS/nix/pull/14768)
|
||||
|
||||
The C API now includes additional methods:
|
||||
|
||||
- `nix_store_query_path_from_hash_part()` - Get the full store path given its hash part
|
||||
- `nix_store_copy_path()` - Copy a single store path between two stores, allows repairs and configuring signature checking
|
||||
|
||||
- Errors returned from your primops are not treated as recoverable by default [#13930](https://github.com/NixOS/nix/pull/13930) [#15286](https://github.com/NixOS/nix/pull/15286)
|
||||
|
||||
Nix 2.34 by default remembers the error in the thunk that triggered it.
|
||||
|
||||
Previously the following sequence of events worked:
|
||||
|
||||
1. Have a thunk that invokes a primop that's defined through the C API
|
||||
2. The primop returns an error
|
||||
3. Force the thunk again
|
||||
4. The primop returns a value
|
||||
5. The thunk evaluated successfully
|
||||
|
||||
**Resolution**
|
||||
|
||||
C API consumers that rely on this must change their recoverable error calls:
|
||||
|
||||
```diff
|
||||
-nix_set_err_msg(context, NIX_ERR_*, msg);
|
||||
+nix_set_err_msg(context, NIX_ERR_RECOVERABLE, msg);
|
||||
```
|
||||
|
||||
## Bug fixes
|
||||
|
||||
- Avoid dropping ssh connections with `ssh-ng://` stores for store path copying [#14998](https://github.com/NixOS/nix/pull/14998) [#6950](https://github.com/NixOS/nix/issues/6950)
|
||||
|
||||
Due to a bug in how Nix handled Boost.Coroutine2 suspension and resumption, copying from `ssh-ng://` stores would drop the SSH connection for each copied path. This issue has been fixed, which improves performance by avoiding multiple SSH/Nix Worker Protocol handshakes.
|
||||
|
||||
- S3 binary caches now use virtual-hosted-style addressing by default [#15208](https://github.com/NixOS/nix/issues/15208) [#15216](https://github.com/NixOS/nix/pull/15216)
|
||||
|
||||
S3 binary caches now use virtual-hosted-style URLs
|
||||
(`https://bucket.s3.region.amazonaws.com/key`) instead of path-style URLs
|
||||
(`https://s3.region.amazonaws.com/bucket/key`) when connecting to standard AWS
|
||||
S3 endpoints. This enables HTTP/2 multiplexing and fixes TCP connection
|
||||
exhaustion (TIME_WAIT socket accumulation) under high-concurrency workloads.
|
||||
|
||||
A new `addressing-style` store option controls this behavior:
|
||||
|
||||
- `auto` (default): virtual-hosted-style for standard AWS endpoints, path-style
|
||||
for custom endpoints.
|
||||
- `path`: forces path-style addressing (deprecated by AWS).
|
||||
- `virtual`: forces virtual-hosted-style addressing (bucket names must not
|
||||
contain dots).
|
||||
|
||||
Bucket names containing dots (e.g., `my.bucket.name`) automatically fall back
|
||||
to path-style addressing in `auto` mode, because dotted names create
|
||||
multi-level subdomains that break TLS wildcard certificate validation.
|
||||
|
||||
Example using path-style for backwards compatibility:
|
||||
|
||||
```
|
||||
s3://my-bucket/key?region=us-east-1&addressing-style=path
|
||||
```
|
||||
|
||||
Additionally, TCP keep-alive is now enabled on all HTTP connections, preventing
|
||||
idle connections from being silently dropped by intermediate network devices
|
||||
(NATs, firewalls, load balancers).
|
||||
|
||||
- `nix-prefetch-url --unpack` now properly checks for empty archives [#15242](https://github.com/NixOS/nix/pull/15242)
|
||||
|
||||
Prior versions failed to check for empty archives and would crash with a `nullptr` dereference when unpacking empty archives.
|
||||
This is now fixed.
|
||||
|
||||
- Prevent runaway processes when Nix is killed with `SIGKILL` when building in a local store with build users [#15193](https://github.com/NixOS/nix/pull/15193)
|
||||
|
||||
When run as root, Nix doesn't run builds via the daemon and is a parent of the forked build processes. Prior versions of Nix failed to preserve the `PR_SET_PDEATHSIG` parent-death signal across `setuid` calls. This could lead to build processes being reparented and continue running in the background. This has been fixed.
|
||||
|
||||
- Fix crash when interrupting `--log-format internal-json` [#15335](https://github.com/NixOS/nix/pull/15335)
|
||||
|
||||
Pressing Ctrl-C during `--log-format internal-json` (used by [nix-output-monitor](https://github.com/maralorn/nix-output-monitor)) no longer causes a spurious "Nix crashed. This is a bug." report.
|
||||
|
||||
- Fix percent-encoding in `file://` and `local://` store URIs [#15280](https://github.com/NixOS/nix/pull/15280)
|
||||
|
||||
Store URIs with special characters like `+` in the path (e.g. `file:///tmp/a+b`) no longer incorrectly create percent-encoded directories (e.g. `/tmp/a%2Bb`).
|
||||
|
||||
- Fix crash during tab completion in `nix repl` [#15255](https://github.com/NixOS/nix/pull/15255)
|
||||
|
||||
- Fix "Too many open files" on macOS [#15205](https://github.com/NixOS/nix/pull/15205)
|
||||
|
||||
Nix now raises the open file soft limit to the hard limit at startup, fixing "Too many open files" errors on macOS where the default soft limit is low.
|
||||
|
||||
- `nix develop` no longer fails when `inputs.nixpkgs` has `flake = false` [#15175](https://github.com/NixOS/nix/pull/15175)
|
||||
|
||||
- `builtins.flakeRefToString` no longer fails with "attribute is a thunk" [#15160](https://github.com/NixOS/nix/pull/15160)
|
||||
|
||||
- Fix `QueryPathInfo` throwing on invalid paths in the daemon [#15134](https://github.com/NixOS/nix/pull/15134)
|
||||
|
||||
- `nix-store --generate-binary-cache-key` now fsyncs key files to prevent corruption [#15107](https://github.com/NixOS/nix/pull/15107)
|
||||
|
||||
- Fix `build-hook` setting in `nix.conf` being ignored [#15083](https://github.com/NixOS/nix/pull/15083)
|
||||
|
||||
- Fix empty error messages when builds are cancelled due to a dependency failure [#14972](https://github.com/NixOS/nix/pull/14972)
|
||||
|
||||
When a build fails without `--keep-going`, other in-progress builds are cancelled. Previously, these cancelled builds were incorrectly reported as failed with empty error messages. This affected `buildPathsWithResults` callers such as `nix flake check`.
|
||||
|
||||
## Miscellaneous changes
|
||||
|
||||
- Content-Encoding decompression is now handled by libcurl [#14324](https://github.com/NixOS/nix/issues/14324) [#15336](https://github.com/NixOS/nix/pull/15336)
|
||||
|
||||
Transparent decompression of HTTP downloads specifying `Content-Encoding` header now uses libcurl. This adds support for previously advertised, but not supported `deflate` encoding as well as deprecated `x-gzip` alias.
|
||||
Non-standard `xz`, `bzip2` encodings that were previously advertised are no longer supported, as they do not commonly appear in the wild and should not be sent by compliant servers.
|
||||
|
||||
`br`, `zstd`, `gzip` continue to be supported. Distro packaging should ensure that the `libcurl` dependency is linked against required libraries to support these encodings. By default, the build system now requires libcurl >= 8.17.0, which is not known to have issues around [pausing and decompression](https://github.com/curl/curl/issues/16280).
|
||||
|
||||
- Static builds now support S3 features (`libstore:s3-aws-auth` meson option) [#15076](https://github.com/NixOS/nix/pull/15076)
|
||||
|
||||
- Improved package-related error messages [#15349](https://github.com/NixOS/nix/pull/15349)
|
||||
|
||||
Store path context is now rendered in the user-facing `hash^out` format instead of the internal `!out!hash` format.
|
||||
A misleading error message in `nix-env` that incorrectly blamed content-addressed derivations has been fixed.
|
||||
|
||||
- Improved error message for empty derivation files [#15298](https://github.com/NixOS/nix/pull/15298)
|
||||
|
||||
Parsing an empty `.drv` file (e.g. due to store corruption after an unclean shutdown) now produces a clear error message instead of the cryptic `expected string 'D'`.
|
||||
|
||||
- Relative `file:` paths for tarballs are now rejected with a clear error [#14983](https://github.com/NixOS/nix/pull/14983)
|
||||
|
||||
- Continued progress on the Windows port, including build fixes, CI improvements, and platform abstractions.
|
||||
|
||||
- Nix docker images are now uploaded to [GHCR](https://github.com/NixOS/nix/pkgs/container/nix) as part of the release process
|
||||
|
||||
Historically, only pre-release builds of `amd64` docker images have been uploaded to ghcr.io with the `latest` tag pointing to the last built image from `master` branch. This has been fixed and going forward, <https://github.com/NixOS/nix/pkgs/container/nix> will include the same images as <https://hub.docker.com/r/nixos/nix/> that are built by [Hydra](https://hydra.nixos.org/project/nix) for [arm64](https://hydra.nixos.org/job/nix/maintenance-2.34/dockerImage.aarch64-linux) and [amd64](https://hydra.nixos.org/job/nix/maintenance-2.34/dockerImage.x86_64-linux). Pre-release versions are no longer pushed to the registry.
|
||||
|
||||
## Contributors
|
||||
|
||||
This release was made possible by the following 43 contributors:
|
||||
|
||||
- Taeer Bar-Yam [**(@Radvendii)**](https://github.com/Radvendii)
|
||||
- Sergei Zimmerman [**(@xokdvium)**](https://github.com/xokdvium)
|
||||
- Jörg Thalheim [**(@Mic92)**](https://github.com/Mic92)
|
||||
- Graham Dennis [**(@GrahamDennis)**](https://github.com/GrahamDennis)
|
||||
- Damien Diederen [**(@ztzg)**](https://github.com/ztzg)
|
||||
- koberbe-jh [**(@koberbe-jh)**](https://github.com/koberbe-jh)
|
||||
- Robert Hensing [**(@roberth)**](https://github.com/roberth)
|
||||
- Bouke van der Bijl [**(@bouk)**](https://github.com/bouk)
|
||||
- Lisanna Dettwyler [**(@lisanna-dettwyler)**](https://github.com/lisanna-dettwyler)
|
||||
- kiara [**(@KiaraGrouwstra)**](https://github.com/KiaraGrouwstra)
|
||||
- Side Effect [**(@YawKar)**](https://github.com/YawKar)
|
||||
- dram [**(@dramforever)**](https://github.com/dramforever)
|
||||
- tomf [**(@tomfitzhenry)**](https://github.com/tomfitzhenry)
|
||||
- Kamil Monicz [**(@Zaczero)**](https://github.com/Zaczero)
|
||||
- Cosima Neidahl [**(@OPNA2608)**](https://github.com/OPNA2608)
|
||||
- Siddhant Kumar [**(@siddhantk232)**](https://github.com/siddhantk232)
|
||||
- Jens Petersen [**(@juhp)**](https://github.com/juhp)
|
||||
- Johannes Kirschbauer [**(@hsjobeki)**](https://github.com/hsjobeki)
|
||||
- tomberek [**(@tomberek)**](https://github.com/tomberek)
|
||||
- Eelco Dolstra [**(@edolstra)**](https://github.com/edolstra)
|
||||
- Artemis Tosini [**(@artemist)**](https://github.com/artemist)
|
||||
- David McFarland [**(@corngood)**](https://github.com/corngood)
|
||||
- Tucker Shea [**(@NoRePercussions)**](https://github.com/NoRePercussions)
|
||||
- Connor Baker [**(@ConnorBaker)**](https://github.com/ConnorBaker)
|
||||
- Cole Helbling [**(@cole-h)**](https://github.com/cole-h)
|
||||
- Eveeifyeve [**(@Eveeifyeve)**](https://github.com/Eveeifyeve)
|
||||
- John Ericson [**(@Ericson2314)**](https://github.com/Ericson2314)
|
||||
- Graham Christensen [**(@grahamc)**](https://github.com/grahamc)
|
||||
- Ilja [**(@iljah)**](https://github.com/iljah)
|
||||
- Pol Dellaiera [**(@drupol)**](https://github.com/drupol)
|
||||
- steelman [**(@steelman)**](https://github.com/steelman)
|
||||
- Brian McKenna [**(@puffnfresh)**](https://github.com/puffnfresh)
|
||||
- JustAGuyTryingHisBest [**(@JustAGuyTryingHisBest)**](https://github.com/JustAGuyTryingHisBest)
|
||||
- zowoq [**(@zowoq)**](https://github.com/zowoq)
|
||||
- Agustín Covarrubias [**(@agucova)**](https://github.com/agucova)
|
||||
- Sergei Trofimovich [**(@trofi)**](https://github.com/trofi)
|
||||
- Bernardo Meurer [**(@lovesegfault)**](https://github.com/lovesegfault)
|
||||
- Peter Bynum [**(@pkpbynum)**](https://github.com/pkpbynum)
|
||||
- Amaan Qureshi [**(@amaanq)**](https://github.com/amaanq)
|
||||
- Michael Hoang [**(@Enzime)**](https://github.com/Enzime)
|
||||
- Michael Daniels [**(@mdaniels5757)**](https://github.com/mdaniels5757)
|
||||
- Matthew Kenigsberg [**(@mkenigs)**](https://github.com/mkenigs)
|
||||
- Shea Levy [**(@shlevy)**](https://github.com/shlevy)
|
||||
@@ -358,7 +358,6 @@ dockerTools.buildLayeredImageWithNixDb {
|
||||
|
||||
extraCommands = ''
|
||||
rm -rf nix-support
|
||||
ln -s /nix/var/nix/profiles nix/var/nix/gcroots/profiles
|
||||
'';
|
||||
fakeRootCommands = ''
|
||||
chmod 1777 tmp
|
||||
|
||||
8
flake.lock
generated
8
flake.lock
generated
@@ -63,11 +63,11 @@
|
||||
},
|
||||
"nixpkgs": {
|
||||
"locked": {
|
||||
"lastModified": 1771043024,
|
||||
"narHash": "sha256-WoiezqWJQ3OHILah+p6rzNXdJceEAmAhyDFZFZ6pZzY=",
|
||||
"rev": "3aadb7ca9eac2891d52a9dec199d9580a6e2bf44",
|
||||
"lastModified": 1771903837,
|
||||
"narHash": "sha256-jEA8WggGKtMFeNeCKq3NK8cLEjJmG6/RLUElYYbBZ0E=",
|
||||
"rev": "e764fc9a405871f1f6ca3d1394fb422e0a0c3951",
|
||||
"type": "tarball",
|
||||
"url": "https://releases.nixos.org/nixos/25.11/nixos-25.11.5960.3aadb7ca9eac/nixexprs.tar.xz"
|
||||
"url": "https://releases.nixos.org/nixos/25.11/nixos-25.11.6495.e764fc9a4058/nixexprs.tar.xz"
|
||||
},
|
||||
"original": {
|
||||
"type": "tarball",
|
||||
|
||||
47
flake.nix
47
flake.nix
@@ -409,7 +409,9 @@
|
||||
|
||||
"nix-cmd" = { };
|
||||
|
||||
"nix-nswrapper" = { };
|
||||
"nix-nswrapper" = {
|
||||
linuxOnly = true;
|
||||
};
|
||||
|
||||
"nix-cli" = { };
|
||||
|
||||
@@ -431,32 +433,37 @@
|
||||
pkgName:
|
||||
{
|
||||
supportsCross ? true,
|
||||
linuxOnly ? false,
|
||||
}:
|
||||
{
|
||||
# These attributes go right into `packages.<system>`.
|
||||
"${pkgName}" = nixpkgsFor.${system}.native.nixComponents2.${pkgName};
|
||||
"${pkgName}-static" = nixpkgsFor.${system}.native.pkgsStatic.nixComponents2.${pkgName};
|
||||
"${pkgName}-llvm" = nixpkgsFor.${system}.native.pkgsLLVM.nixComponents2.${pkgName};
|
||||
}
|
||||
lib.optionalAttrs (linuxOnly -> nixpkgsFor.${system}.native.stdenv.hostPlatform.isLinux) (
|
||||
{
|
||||
# These attributes go right into `packages.<system>`.
|
||||
"${pkgName}" = nixpkgsFor.${system}.native.nixComponents2.${pkgName};
|
||||
"${pkgName}-static" = nixpkgsFor.${system}.native.pkgsStatic.nixComponents2.${pkgName};
|
||||
"${pkgName}-llvm" = nixpkgsFor.${system}.native.pkgsLLVM.nixComponents2.${pkgName};
|
||||
}
|
||||
// flatMapAttrs (lib.genAttrs stdenvs (_: { })) (
|
||||
stdenvName:
|
||||
{ }:
|
||||
{
|
||||
# These attributes go right into `packages.<system>`.
|
||||
"${pkgName}-${stdenvName}" =
|
||||
nixpkgsFor.${system}.nativeForStdenv.${stdenvName}.nixComponents2.${pkgName};
|
||||
}
|
||||
)
|
||||
)
|
||||
// lib.optionalAttrs supportsCross (
|
||||
flatMapAttrs (lib.genAttrs crossSystems (_: { })) (
|
||||
crossSystem:
|
||||
{ }:
|
||||
{
|
||||
# These attributes go right into `packages.<system>`.
|
||||
"${pkgName}-${crossSystem}" = nixpkgsFor.${system}.cross.${crossSystem}.nixComponents2.${pkgName};
|
||||
}
|
||||
lib.optionalAttrs
|
||||
(linuxOnly -> nixpkgsFor.${system}.cross.${crossSystem}.stdenv.hostPlatform.isLinux)
|
||||
{
|
||||
# These attributes go right into `packages.<system>`.
|
||||
"${pkgName}-${crossSystem}" = nixpkgsFor.${system}.cross.${crossSystem}.nixComponents2.${pkgName};
|
||||
}
|
||||
)
|
||||
)
|
||||
// flatMapAttrs (lib.genAttrs stdenvs (_: { })) (
|
||||
stdenvName:
|
||||
{ }:
|
||||
{
|
||||
# These attributes go right into `packages.<system>`.
|
||||
"${pkgName}-${stdenvName}" =
|
||||
nixpkgsFor.${system}.nativeForStdenv.${stdenvName}.nixComponents2.${pkgName};
|
||||
}
|
||||
)
|
||||
)
|
||||
// lib.optionalAttrs (builtins.elem system linux64BitSystems) {
|
||||
dockerImage =
|
||||
|
||||
@@ -95,6 +95,11 @@
|
||||
''^tests/functional/lang/eval-fail-bad-string-interpolation-3\.nix$''
|
||||
''^tests/functional/lang/eval-fail-bad-string-interpolation-4\.nix$''
|
||||
''^tests/functional/lang/eval-okay-regex-match2\.nix$''
|
||||
|
||||
# URL literal tests - nixfmt converts unquoted URLs to strings
|
||||
''^tests/functional/lang/eval-fail-url-literal\.nix$''
|
||||
''^tests/functional/lang/eval-okay-url-literal-warn\.nix$''
|
||||
''^tests/functional/lang/eval-okay-url-literal-default\.nix$''
|
||||
];
|
||||
};
|
||||
clang-format = {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
runCommand,
|
||||
system,
|
||||
stdenv,
|
||||
buildPackages,
|
||||
cacert,
|
||||
nix,
|
||||
@@ -9,6 +9,8 @@
|
||||
|
||||
let
|
||||
|
||||
inherit (stdenv.hostPlatform) system;
|
||||
|
||||
installerClosureInfo = buildPackages.closureInfo {
|
||||
rootPaths = [
|
||||
nix
|
||||
|
||||
@@ -30,9 +30,19 @@ scope: {
|
||||
NIX_CFLAGS_COMPILE = "-DINITIAL_MARK_STACK_SIZE=1048576";
|
||||
});
|
||||
|
||||
curl = pkgs.curl.override {
|
||||
http3Support = !pkgs.stdenv.hostPlatform.isWindows;
|
||||
};
|
||||
curl =
|
||||
(pkgs.curl.override {
|
||||
http3Support = !pkgs.stdenv.hostPlatform.isWindows;
|
||||
# Make sure we enable all the dependencies for Content-Encoding/Transfer-Encoding decompression.
|
||||
zstdSupport = true;
|
||||
brotliSupport = true;
|
||||
zlibSupport = true;
|
||||
}).overrideAttrs
|
||||
{
|
||||
# TODO: Fix in nixpkgs. Static build with brotli is marked as broken, but it's not the case.
|
||||
# Remove once https://github.com/NixOS/nixpkgs/pull/494111 lands in the 25.11 channel.
|
||||
meta.broken = false;
|
||||
};
|
||||
|
||||
libblake3 = pkgs.libblake3.override {
|
||||
useTBB = !(stdenv.hostPlatform.isWindows || stdenv.hostPlatform.isStatic);
|
||||
|
||||
@@ -115,7 +115,11 @@ rec {
|
||||
|
||||
# Binary package for various platforms.
|
||||
build = forAllPackages (
|
||||
pkgName: forAllSystems (system: nixpkgsFor.${system}.native.nixComponents2.${pkgName})
|
||||
pkgName:
|
||||
lib.filterAttrs (
|
||||
system: _do_not_touch:
|
||||
pkgName == "nix-nswrapper" -> nixpkgsFor.${system}.native.stdenv.hostPlatform.isLinux
|
||||
) (forAllSystems (system: nixpkgsFor.${system}.native.nixComponents2.${pkgName}))
|
||||
);
|
||||
|
||||
shellInputs = removeAttrs (forAllSystems (
|
||||
@@ -135,6 +139,10 @@ rec {
|
||||
(
|
||||
if pkgName == "nix-functional-tests" then
|
||||
lib.flip builtins.removeAttrs [ "x86_64-w64-mingw32" ]
|
||||
else if pkgName == "nix-nswrapper" then
|
||||
lib.filterAttrs (
|
||||
crossSystem: _do_not_touch: nixpkgsFor.x86_64-linux.cross.${crossSystem}.stdenv.hostPlatform.isLinux
|
||||
)
|
||||
else
|
||||
lib.id
|
||||
)
|
||||
@@ -171,7 +179,13 @@ rec {
|
||||
)
|
||||
);
|
||||
in
|
||||
forAllPackages (pkgName: forAllSystems (system: components.${system}.${pkgName}));
|
||||
forAllPackages (
|
||||
pkgName:
|
||||
lib.filterAttrs (
|
||||
system: _do_not_touch:
|
||||
pkgName == "nix-nswrapper" -> nixpkgsFor.${system}.native.stdenv.hostPlatform.isLinux
|
||||
) (forAllSystems (system: components.${system}.${pkgName}))
|
||||
);
|
||||
|
||||
buildNoTests = forAllSystems (system: nixpkgsFor.${system}.native.nixComponents2.nix-cli);
|
||||
|
||||
@@ -191,7 +205,13 @@ rec {
|
||||
)
|
||||
);
|
||||
in
|
||||
forAllPackages (pkgName: forAllSystems (system: components.${system}.${pkgName}));
|
||||
forAllPackages (
|
||||
pkgName:
|
||||
lib.filterAttrs (
|
||||
system: _do_not_touch:
|
||||
pkgName == "nix-nswrapper" -> nixpkgsFor.${system}.native.stdenv.hostPlatform.isLinux
|
||||
) (forAllSystems (system: components.${system}.${pkgName}))
|
||||
);
|
||||
|
||||
# Perl bindings for various platforms.
|
||||
perlBindings = forAllSystems (system: nixpkgsFor.${system}.native.nixComponents2.nix-perl-bindings);
|
||||
|
||||
@@ -1 +1 @@
|
||||
../../src/libstore-tests/data/build-result
|
||||
../libstore-tests/data/build-result
|
||||
@@ -1 +1 @@
|
||||
../../src/libstore-tests/data/realisation
|
||||
../libstore-tests/data/realisation
|
||||
@@ -1 +1 @@
|
||||
../../src/libstore-tests/data/content-address
|
||||
../libstore-tests/data/content-address
|
||||
@@ -1 +1 @@
|
||||
../../src/libstore-tests/data/derivation
|
||||
../libstore-tests/data/derivation
|
||||
@@ -1 +1 @@
|
||||
../../src/libstore-tests/data/derived-path
|
||||
../libstore-tests/data/derived-path
|
||||
@@ -1 +1 @@
|
||||
../../src/libutil-tests/data/memory-source-accessor
|
||||
../libutil-tests/data/memory-source-accessor
|
||||
@@ -1 +1 @@
|
||||
../../src/libutil-tests/data/hash
|
||||
../libutil-tests/data/hash
|
||||
@@ -51,6 +51,13 @@ schemas = [
|
||||
'simple.json',
|
||||
],
|
||||
},
|
||||
{
|
||||
'stem' : 'signature',
|
||||
'schema' : schema_dir / 'signature-v2.yaml',
|
||||
'files' : [
|
||||
'simple.json',
|
||||
],
|
||||
},
|
||||
{
|
||||
'stem' : 'deriving-path',
|
||||
'schema' : schema_dir / 'deriving-path-v1.yaml',
|
||||
@@ -62,13 +69,10 @@ schemas = [
|
||||
},
|
||||
{
|
||||
'stem' : 'build-trace-entry',
|
||||
'schema' : schema_dir / 'build-trace-entry-v2.yaml',
|
||||
'schema' : schema_dir / 'build-trace-entry-v3.yaml',
|
||||
'files' : [
|
||||
'simple.json',
|
||||
# The field is no longer supported, but we want to show that we
|
||||
# ignore it during parsing.
|
||||
'with-dependent-realisations.json',
|
||||
'with-signature.json',
|
||||
'with-structured-signature.json',
|
||||
],
|
||||
},
|
||||
{
|
||||
@@ -154,20 +158,20 @@ schemas += [
|
||||
# Match overall
|
||||
{
|
||||
'stem' : 'store-object-info',
|
||||
'schema' : schema_dir / 'store-object-info-v2.yaml',
|
||||
'schema' : schema_dir / 'store-object-info-v3.yaml',
|
||||
'files' : [
|
||||
'json-2' / 'pure.json',
|
||||
'json-2' / 'impure.json',
|
||||
'json-2' / 'empty_pure.json',
|
||||
'json-2' / 'empty_impure.json',
|
||||
'json-3' / 'pure.json',
|
||||
'json-3' / 'impure.json',
|
||||
'json-3' / 'empty_pure.json',
|
||||
'json-3' / 'empty_impure.json',
|
||||
],
|
||||
},
|
||||
{
|
||||
'stem' : 'nar-info',
|
||||
'schema' : schema_dir / 'store-object-info-v2.yaml',
|
||||
'schema' : schema_dir / 'store-object-info-v3.yaml',
|
||||
'files' : [
|
||||
'json-2' / 'pure.json',
|
||||
'json-2' / 'impure.json',
|
||||
'json-3' / 'pure.json',
|
||||
'json-3' / 'impure.json',
|
||||
],
|
||||
},
|
||||
{
|
||||
@@ -182,32 +186,32 @@ schemas += [
|
||||
# Match exact variant
|
||||
{
|
||||
'stem' : 'store-object-info',
|
||||
'schema' : schema_dir / 'store-object-info-v2.yaml#/$defs/base',
|
||||
'schema' : schema_dir / 'store-object-info-v3.yaml#/$defs/base',
|
||||
'files' : [
|
||||
'json-2' / 'pure.json',
|
||||
'json-2' / 'empty_pure.json',
|
||||
'json-3' / 'pure.json',
|
||||
'json-3' / 'empty_pure.json',
|
||||
],
|
||||
},
|
||||
{
|
||||
'stem' : 'store-object-info',
|
||||
'schema' : schema_dir / 'store-object-info-v2.yaml#/$defs/impure',
|
||||
'schema' : schema_dir / 'store-object-info-v3.yaml#/$defs/impure',
|
||||
'files' : [
|
||||
'json-2' / 'impure.json',
|
||||
'json-2' / 'empty_impure.json',
|
||||
'json-3' / 'impure.json',
|
||||
'json-3' / 'empty_impure.json',
|
||||
],
|
||||
},
|
||||
{
|
||||
'stem' : 'nar-info',
|
||||
'schema' : schema_dir / 'store-object-info-v2.yaml#/$defs/base',
|
||||
'schema' : schema_dir / 'store-object-info-v3.yaml#/$defs/base',
|
||||
'files' : [
|
||||
'json-2' / 'pure.json',
|
||||
'json-3' / 'pure.json',
|
||||
],
|
||||
},
|
||||
{
|
||||
'stem' : 'nar-info',
|
||||
'schema' : schema_dir / 'store-object-info-v2.yaml#/$defs/narInfo',
|
||||
'schema' : schema_dir / 'store-object-info-v3.yaml#/$defs/narInfo',
|
||||
'files' : [
|
||||
'json-2' / 'impure.json',
|
||||
'json-3' / 'impure.json',
|
||||
],
|
||||
},
|
||||
]
|
||||
|
||||
@@ -1 +1 @@
|
||||
../../src/libstore-tests/data/nar-info
|
||||
../libstore-tests/data/nar-info
|
||||
4
src/json-schema-checks/signature/simple.json
Normal file
4
src/json-schema-checks/signature/simple.json
Normal file
@@ -0,0 +1,4 @@
|
||||
{
|
||||
"keyName": "cache.nixos.org-1",
|
||||
"sig": "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
|
||||
}
|
||||
@@ -1 +1 @@
|
||||
../../src/libstore-tests/data/dummy-store
|
||||
../libstore-tests/data/dummy-store
|
||||
@@ -1 +1 @@
|
||||
../../src/libstore-tests/data/path-info
|
||||
../libstore-tests/data/path-info
|
||||
@@ -1 +1 @@
|
||||
../../src/libstore-tests/data/store-path
|
||||
../libstore-tests/data/store-path
|
||||
@@ -108,20 +108,16 @@ RealisedPath::Set BuiltPath::toRealisedPaths(Store & store) const
|
||||
overloaded{
|
||||
[&](const BuiltPath::Opaque & p) { res.insert(p.path); },
|
||||
[&](const BuiltPath::Built & p) {
|
||||
auto drvHashes = staticOutputHashes(store, store.readDerivation(p.drvPath->outPath()));
|
||||
for (auto & [outputName, outputPath] : p.outputs) {
|
||||
if (experimentalFeatureSettings.isEnabled(Xp::CaDerivations)) {
|
||||
auto drvOutput = get(drvHashes, outputName);
|
||||
if (!drvOutput)
|
||||
throw Error(
|
||||
"the derivation '%s' has unrealised output '%s' (derived-path.cc/toRealisedPaths)",
|
||||
store.printStorePath(p.drvPath->outPath()),
|
||||
outputName);
|
||||
DrvOutput key{*drvOutput, outputName};
|
||||
DrvOutput key{
|
||||
.drvPath = p.drvPath->outPath(),
|
||||
.outputName = outputName,
|
||||
};
|
||||
auto thisRealisation = store.queryRealisation(key);
|
||||
assert(thisRealisation); // We’ve built it, so we must
|
||||
// have the realisation
|
||||
res.insert(Realisation{*thisRealisation, std::move(key)});
|
||||
// We’ve built it, so we must have the realisation.
|
||||
assert(thisRealisation);
|
||||
res.insert(Realisation{*thisRealisation, key});
|
||||
} else {
|
||||
res.insert(outputPath);
|
||||
}
|
||||
|
||||
@@ -300,11 +300,11 @@ struct StorePathCommand : public StorePathsCommand
|
||||
*/
|
||||
struct RegisterCommand
|
||||
{
|
||||
typedef std::map<std::vector<std::string>, std::function<ref<Command>()>> Commands;
|
||||
typedef std::map<std::vector<std::string>, fun<ref<Command>()>> Commands;
|
||||
|
||||
static Commands & commands();
|
||||
|
||||
RegisterCommand(std::vector<std::string> && name, std::function<ref<Command>()> command)
|
||||
RegisterCommand(std::vector<std::string> && name, fun<ref<Command>()> command)
|
||||
{
|
||||
commands().emplace(name, command);
|
||||
}
|
||||
@@ -407,7 +407,7 @@ void createOutLinks(const std::filesystem::path & outLink, const BuiltPaths & bu
|
||||
struct MixOutLinkBase : virtual Args
|
||||
{
|
||||
/** Prefix for any output symlinks. Empty means do not write an output symlink. */
|
||||
Path outLink;
|
||||
std::filesystem::path outLink;
|
||||
|
||||
MixOutLinkBase(const std::string & defaultOutLink)
|
||||
: outLink(defaultOutLink)
|
||||
@@ -435,7 +435,7 @@ struct MixOutLinkByDefault : MixOutLinkBase, virtual Args
|
||||
addFlag({
|
||||
.longName = "no-link",
|
||||
.description = "Do not create symlinks to the build results.",
|
||||
.handler = {&outLink, Path("")},
|
||||
.handler = {&outLink, std::filesystem::path{}},
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
@@ -1,13 +1,14 @@
|
||||
#pragma once
|
||||
///@file
|
||||
|
||||
#include <functional>
|
||||
#include "nix/util/fun.hh"
|
||||
|
||||
#include <map>
|
||||
#include <string>
|
||||
|
||||
namespace nix {
|
||||
|
||||
typedef std::function<void(int, char **)> MainFunction;
|
||||
typedef fun<void(int, char **)> MainFunction;
|
||||
|
||||
struct RegisterLegacyCommand
|
||||
{
|
||||
@@ -15,9 +16,9 @@ struct RegisterLegacyCommand
|
||||
|
||||
static Commands & commands();
|
||||
|
||||
RegisterLegacyCommand(const std::string & name, MainFunction fun)
|
||||
RegisterLegacyCommand(const std::string & name, MainFunction command)
|
||||
{
|
||||
commands()[name] = fun;
|
||||
commands().insert_or_assign(name, command);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -2,7 +2,9 @@
|
||||
/// @file
|
||||
|
||||
#include "nix/util/finally.hh"
|
||||
#include "nix/util/fun.hh"
|
||||
#include "nix/util/types.hh"
|
||||
#include <filesystem>
|
||||
#include <functional>
|
||||
#include <string>
|
||||
|
||||
@@ -26,7 +28,7 @@ enum class ReplPromptType {
|
||||
class ReplInteracter
|
||||
{
|
||||
public:
|
||||
using Guard = Finally<std::function<void()>>;
|
||||
using Guard = Finally<fun<void()>>;
|
||||
|
||||
virtual Guard init(detail::ReplCompleterMixin * repl) = 0;
|
||||
/** Returns a boolean of whether the interacter got EOF */
|
||||
@@ -36,10 +38,10 @@ public:
|
||||
|
||||
class ReadlineLikeInteracter : public virtual ReplInteracter
|
||||
{
|
||||
std::string historyFile;
|
||||
std::filesystem::path historyFile;
|
||||
public:
|
||||
ReadlineLikeInteracter(std::string historyFile)
|
||||
: historyFile(historyFile)
|
||||
ReadlineLikeInteracter(std::filesystem::path historyFile)
|
||||
: historyFile(std::move(historyFile))
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
///@file
|
||||
|
||||
#include "nix/expr/eval.hh"
|
||||
#include "nix/util/os-string.hh"
|
||||
|
||||
namespace nix {
|
||||
|
||||
@@ -27,8 +28,7 @@ struct AbstractNixRepl
|
||||
* @param programName Name of the command, e.g. `nix` or `nix-env`.
|
||||
* @param args arguments to the command.
|
||||
*/
|
||||
using RunNix =
|
||||
void(const std::string & programName, const Strings & args, const std::optional<std::string> & input);
|
||||
using RunNix = void(const std::string & programName, OsStrings args, const std::optional<std::string> & input);
|
||||
|
||||
/**
|
||||
* @param runNix Function to run the nix CLI to support various
|
||||
@@ -38,7 +38,7 @@ struct AbstractNixRepl
|
||||
static std::unique_ptr<AbstractNixRepl> create(
|
||||
const LookupPath & lookupPath,
|
||||
ref<EvalState> state,
|
||||
std::function<AnnotatedValues()> getValues,
|
||||
fun<AnnotatedValues()> getValues,
|
||||
RunNix * runNix = nullptr);
|
||||
|
||||
static ReplExitStatus runSimple(ref<EvalState> evalState, const ValMap & extraEnv);
|
||||
|
||||
@@ -35,7 +35,7 @@ PeerInfo getPeerInfo(Descriptor remote);
|
||||
* @param closeListeners A callback to close the listening sockets.
|
||||
* Useful in forked child processes to release the bound sockets.
|
||||
*/
|
||||
using UnixSocketHandler = std::function<void(AutoCloseFD socket, std::function<void()> closeListeners)>;
|
||||
using UnixSocketHandler = fun<void(AutoCloseFD socket, std::function<void()> closeListeners)>;
|
||||
|
||||
/**
|
||||
* Options for the serve loop.
|
||||
@@ -53,6 +53,19 @@ struct ServeUnixSocketOptions
|
||||
* Mode for the created socket file.
|
||||
*/
|
||||
mode_t socketMode = 0666;
|
||||
|
||||
#ifndef _WIN32
|
||||
/**
|
||||
* Additional file descriptor to poll. Useful for doing a self-pipe trick
|
||||
* https://cr.yp.to/docs/selfpipe.html.
|
||||
*/
|
||||
Descriptor auxiliaryFd = INVALID_DESCRIPTOR;
|
||||
|
||||
/**
|
||||
* Optional callback invoked on POLLIN event for auxiliaryFd.
|
||||
*/
|
||||
std::function<void()> onAuxiliaryFdPollin = nullptr;
|
||||
#endif
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
@@ -157,7 +157,7 @@ MixFlakeOptions::MixFlakeOptions()
|
||||
.category = category,
|
||||
.labels = {"flake-lock-path"},
|
||||
.handler = {[&](std::string lockFilePath) {
|
||||
lockFlags.referenceLockFilePath = {getFSSourceAccessor(), CanonPath(absPath(lockFilePath))};
|
||||
lockFlags.referenceLockFilePath = {getFSSourceAccessor(), CanonPath(absPath(lockFilePath).string())};
|
||||
}},
|
||||
.completer = completePath,
|
||||
});
|
||||
@@ -400,7 +400,7 @@ void completeFlakeRefWithFragment(
|
||||
}
|
||||
}
|
||||
} catch (Error & e) {
|
||||
warn(e.msg());
|
||||
logWarning(e.info());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -118,14 +118,14 @@ ReadlineLikeInteracter::Guard ReadlineLikeInteracter::init(detail::ReplCompleter
|
||||
// Allow nix-repl specific settings in .inputrc
|
||||
rl_readline_name = "nix-repl";
|
||||
try {
|
||||
createDirs(dirOf(historyFile));
|
||||
createDirs(historyFile.parent_path());
|
||||
} catch (SystemError & e) {
|
||||
logWarning(e.info());
|
||||
}
|
||||
#if !USE_READLINE
|
||||
el_hist_size = 1000;
|
||||
#endif
|
||||
read_history(historyFile.c_str());
|
||||
read_history(historyFile.string().c_str());
|
||||
auto oldRepl = curRepl;
|
||||
curRepl = repl;
|
||||
Guard restoreRepl([oldRepl] { curRepl = oldRepl; });
|
||||
@@ -208,7 +208,7 @@ bool ReadlineLikeInteracter::getLine(std::string & input, ReplPromptType promptT
|
||||
|
||||
ReadlineLikeInteracter::~ReadlineLikeInteracter()
|
||||
{
|
||||
write_history(historyFile.c_str());
|
||||
write_history(historyFile.string().c_str());
|
||||
}
|
||||
|
||||
}; // namespace nix
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#include <iostream>
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
#include <memory>
|
||||
|
||||
#include "nix/util/error.hh"
|
||||
#include "nix/cmd/repl-interacter.hh"
|
||||
@@ -28,6 +29,8 @@
|
||||
#include "nix/util/ref.hh"
|
||||
#include "nix/expr/value.hh"
|
||||
|
||||
#include "nix/util/os-string.hh"
|
||||
#include "nix/util/processes.hh"
|
||||
#include "nix/util/strings.hh"
|
||||
|
||||
namespace nix {
|
||||
@@ -60,7 +63,7 @@ struct NixRepl : AbstractNixRepl, detail::ReplCompleterMixin, gc
|
||||
std::list<std::filesystem::path> loadedFiles;
|
||||
// Arguments passed to :load-flake, saved so they can be reloaded with :reload
|
||||
Strings loadedFlakes;
|
||||
std::function<AnnotatedValues()> getValues;
|
||||
fun<AnnotatedValues()> getValues;
|
||||
|
||||
const static int envSize = 32768;
|
||||
std::shared_ptr<StaticEnv> staticEnv;
|
||||
@@ -71,15 +74,11 @@ struct NixRepl : AbstractNixRepl, detail::ReplCompleterMixin, gc
|
||||
|
||||
RunNix * runNixPtr;
|
||||
|
||||
void runNix(const std::string & program, const Strings & args, const std::optional<std::string> & input = {});
|
||||
void runNix(const std::string & program, OsStrings args, const std::optional<std::string> & input = {});
|
||||
|
||||
std::unique_ptr<ReplInteracter> interacter;
|
||||
|
||||
NixRepl(
|
||||
const LookupPath & lookupPath,
|
||||
ref<EvalState> state,
|
||||
std::function<AnnotatedValues()> getValues,
|
||||
RunNix * runNix);
|
||||
NixRepl(const LookupPath & lookupPath, ref<EvalState> state, fun<AnnotatedValues()> getValues, RunNix * runNix);
|
||||
virtual ~NixRepl() = default;
|
||||
|
||||
ReplExitStatus mainLoop() override;
|
||||
@@ -98,6 +97,7 @@ struct NixRepl : AbstractNixRepl, detail::ReplCompleterMixin, gc
|
||||
void addAttrsToScope(Value & attrs);
|
||||
void addVarToScope(const Symbol name, Value & v);
|
||||
Expr * parseString(std::string s);
|
||||
ExprAttrs * parseReplBindings(std::string s);
|
||||
void evalString(std::string s, Value & v);
|
||||
void loadDebugTraceEnv(DebugTrace & dt);
|
||||
|
||||
@@ -130,16 +130,13 @@ std::string removeWhitespace(std::string s)
|
||||
}
|
||||
|
||||
NixRepl::NixRepl(
|
||||
const LookupPath & lookupPath,
|
||||
ref<EvalState> state,
|
||||
std::function<NixRepl::AnnotatedValues()> getValues,
|
||||
RunNix * runNix)
|
||||
const LookupPath & lookupPath, ref<EvalState> state, fun<NixRepl::AnnotatedValues()> getValues, RunNix * runNix)
|
||||
: AbstractNixRepl(state)
|
||||
, debugTraceIndex(0)
|
||||
, getValues(getValues)
|
||||
, staticEnv(new StaticEnv(nullptr, state->staticBaseEnv))
|
||||
, runNixPtr{runNix}
|
||||
, interacter(make_unique<ReadlineLikeInteracter>((getDataDir() / "repl-history").string()))
|
||||
, interacter(std::make_unique<ReadlineLikeInteracter>(getDataDir() / "repl-history"))
|
||||
{
|
||||
}
|
||||
|
||||
@@ -306,21 +303,6 @@ StringSet NixRepl::completePrefix(const std::string & prefix)
|
||||
return completions;
|
||||
}
|
||||
|
||||
// FIXME: DRY and match or use the parser
|
||||
static bool isVarName(std::string_view s)
|
||||
{
|
||||
if (s.size() == 0)
|
||||
return false;
|
||||
char c = s[0];
|
||||
if ((c >= '0' && c <= '9') || c == '-' || c == '\'')
|
||||
return false;
|
||||
for (auto & i : s)
|
||||
if (!((i >= 'a' && i <= 'z') || (i >= 'A' && i <= 'Z') || (i >= '0' && i <= '9') || i == '_' || i == '-'
|
||||
|| i == '\''))
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
StorePath NixRepl::getDerivationPath(Value & v)
|
||||
{
|
||||
auto packageInfo = getDerivation(*state, v, false);
|
||||
@@ -508,7 +490,12 @@ ProcessLineResult NixRepl::processLine(std::string line)
|
||||
|
||||
// runProgram redirects stdout to a StringSink,
|
||||
// using runProgram2 to allow editors to display their UI
|
||||
runProgram2(RunOptions{.program = editor, .lookupPath = true, .args = args, .isInteractive = true});
|
||||
runProgram2({
|
||||
.program = editor,
|
||||
.lookupPath = true,
|
||||
.args = toOsStrings(std::move(args)),
|
||||
.isInteractive = true,
|
||||
});
|
||||
|
||||
// Reload right after exiting the editor
|
||||
state->resetFileCache();
|
||||
@@ -528,7 +515,7 @@ ProcessLineResult NixRepl::processLine(std::string line)
|
||||
state->callFunction(f, v, result, PosIdx());
|
||||
|
||||
StorePath drvPath = getDerivationPath(result);
|
||||
runNix("nix-shell", {state->store->printStorePath(drvPath)});
|
||||
runNix("nix-shell", toOsStrings({state->store->printStorePath(drvPath)}));
|
||||
}
|
||||
|
||||
else if (command == ":b" || command == ":bl" || command == ":i" || command == ":sh" || command == ":log") {
|
||||
@@ -559,7 +546,7 @@ ProcessLineResult NixRepl::processLine(std::string line)
|
||||
}
|
||||
}
|
||||
} else if (command == ":i") {
|
||||
runNix("nix-env", {"-i", drvPathRaw});
|
||||
runNix("nix-env", toOsStrings({"-i", drvPathRaw}));
|
||||
} else if (command == ":log") {
|
||||
settings.readOnlyMode = true;
|
||||
Finally roModeReset([&]() { settings.readOnlyMode = false; });
|
||||
@@ -567,7 +554,7 @@ ProcessLineResult NixRepl::processLine(std::string line)
|
||||
auto log = fetchBuildLog(state->store, drvPath, drvPathRaw);
|
||||
logger->writeToStdout(log);
|
||||
} else {
|
||||
runNix("nix-shell", {drvPathRaw});
|
||||
runNix("nix-shell", toOsStrings({drvPathRaw}));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -668,15 +655,22 @@ ProcessLineResult NixRepl::processLine(std::string line)
|
||||
throw Error("unknown command '%1%'", command);
|
||||
|
||||
else {
|
||||
size_t p = line.find('=');
|
||||
std::string name;
|
||||
if (p != std::string::npos && p < line.size() && line[p + 1] != '='
|
||||
&& isVarName(name = removeWhitespace(line.substr(0, p)))) {
|
||||
Expr * e = parseString(line.substr(p + 1));
|
||||
Value & v(*state->allocValue());
|
||||
v.mkThunk(env, e);
|
||||
addVarToScope(state->symbols.create(name), v);
|
||||
// Try parsing as bindings first (handles `x = 1`, `inherit ...`, etc.)
|
||||
ExprAttrs * bindings = nullptr;
|
||||
try {
|
||||
bindings = parseReplBindings(line);
|
||||
} catch (ParseError &) {
|
||||
}
|
||||
|
||||
if (bindings) {
|
||||
Env * inheritEnv = bindings->inheritFromExprs ? bindings->buildInheritFromEnv(*state, *env) : nullptr;
|
||||
for (auto & [symbol, def] : *bindings->attrs) {
|
||||
Value & v(*state->allocValue());
|
||||
v.mkThunk(def.chooseByKind(env, env, inheritEnv), def.e);
|
||||
addVarToScope(symbol, v);
|
||||
}
|
||||
} else {
|
||||
// Otherwise evaluate as expression
|
||||
Value v;
|
||||
evalString(line, v);
|
||||
auto suspension = logger->suspend();
|
||||
@@ -865,6 +859,28 @@ Expr * NixRepl::parseString(std::string s)
|
||||
}
|
||||
}
|
||||
|
||||
ExprAttrs * NixRepl::parseReplBindings(std::string s)
|
||||
{
|
||||
auto basePath = state->rootPath(".");
|
||||
|
||||
// Try parsing as bindings
|
||||
std::exception_ptr bindingsError;
|
||||
try {
|
||||
return state->parseReplBindings(s, basePath, staticEnv);
|
||||
} catch (ParseError &) {
|
||||
bindingsError = std::current_exception();
|
||||
}
|
||||
|
||||
// Try with semicolon appended (for `inherit foo` shorthand)
|
||||
// Use original source (s) for error messages, not s + ";"
|
||||
try {
|
||||
return state->parseReplBindings(s + ";", s, basePath, staticEnv);
|
||||
} catch (ParseError &) {
|
||||
// Semicolon retry failed; rethrow the original bindings error
|
||||
std::rethrow_exception(bindingsError);
|
||||
}
|
||||
}
|
||||
|
||||
void NixRepl::evalString(std::string s, Value & v)
|
||||
{
|
||||
Expr * e = parseString(s);
|
||||
@@ -872,10 +888,10 @@ void NixRepl::evalString(std::string s, Value & v)
|
||||
state->forceValue(v, v.determinePos(noPos));
|
||||
}
|
||||
|
||||
void NixRepl::runNix(const std::string & program, const Strings & args, const std::optional<std::string> & input)
|
||||
void NixRepl::runNix(const std::string & program, OsStrings args, const std::optional<std::string> & input)
|
||||
{
|
||||
if (runNixPtr)
|
||||
(*runNixPtr)(program, args, input);
|
||||
(*runNixPtr)(program, std::move(args), input);
|
||||
else
|
||||
throw Error(
|
||||
"Cannot run '%s' because no method of calling the Nix CLI was provided. This is a configuration problem pertaining to how this program was built. See Nix 2.25 release notes",
|
||||
@@ -883,7 +899,7 @@ void NixRepl::runNix(const std::string & program, const Strings & args, const st
|
||||
}
|
||||
|
||||
std::unique_ptr<AbstractNixRepl> AbstractNixRepl::create(
|
||||
const LookupPath & lookupPath, ref<EvalState> state, std::function<AnnotatedValues()> getValues, RunNix * runNix)
|
||||
const LookupPath & lookupPath, ref<EvalState> state, fun<AnnotatedValues()> getValues, RunNix * runNix)
|
||||
{
|
||||
return std::make_unique<NixRepl>(lookupPath, state, getValues, runNix);
|
||||
}
|
||||
|
||||
@@ -9,6 +9,8 @@
|
||||
#include "nix/util/unix-domain-socket.hh"
|
||||
#include "nix/util/util.hh"
|
||||
|
||||
#include <ranges>
|
||||
|
||||
#include <sys/socket.h>
|
||||
#include <sys/un.h>
|
||||
#include <poll.h>
|
||||
@@ -83,6 +85,9 @@ PeerInfo getPeerInfo(Descriptor remote)
|
||||
for (auto & i : listeningSockets)
|
||||
fds.push_back({.fd = i.get(), .events = POLLIN});
|
||||
|
||||
if (options.auxiliaryFd != INVALID_DESCRIPTOR)
|
||||
fds.push_back({.fd = options.auxiliaryFd, .events = POLLIN});
|
||||
|
||||
// Loop accepting connections.
|
||||
while (1) {
|
||||
try {
|
||||
@@ -95,7 +100,11 @@ PeerInfo getPeerInfo(Descriptor remote)
|
||||
throw SysError("polling for incoming connections");
|
||||
}
|
||||
|
||||
for (auto & fd : fds) {
|
||||
if (options.auxiliaryFd != INVALID_DESCRIPTOR && options.onAuxiliaryFdPollin && fds.back().revents & POLLIN)
|
||||
/* Useful for reaping children. */
|
||||
options.onAuxiliaryFdPollin();
|
||||
|
||||
for (auto & fd : std::views::take(fds, listeningSockets.size())) {
|
||||
if (!fd.revents)
|
||||
continue;
|
||||
|
||||
|
||||
@@ -181,13 +181,13 @@ EvalState * nix_eval_state_build(nix_c_context * context, nix_eval_state_builder
|
||||
if (context)
|
||||
context->last_err_code = NIX_OK;
|
||||
try {
|
||||
return unsafe_new_with_self<EvalState>([&](auto * self) {
|
||||
return EvalState{
|
||||
.fetchSettings = std::move(builder->fetchSettings),
|
||||
.settings = std::move(builder->settings),
|
||||
.state = nix::EvalState(builder->lookupPath, builder->store, self->fetchSettings, self->settings),
|
||||
};
|
||||
});
|
||||
auto fetchSettings = std::make_unique<nix::fetchers::Settings>(std::move(builder->fetchSettings));
|
||||
auto settings = std::make_unique<nix::EvalSettings>(std::move(builder->settings));
|
||||
auto ownedState =
|
||||
std::make_shared<nix::EvalState>(builder->lookupPath, builder->store, *fetchSettings, *settings);
|
||||
auto & stateRef = *ownedState;
|
||||
void * p = ::operator new(sizeof(EvalState), static_cast<std::align_val_t>(alignof(EvalState)));
|
||||
return new (p) EvalState{stateRef, std::move(fetchSettings), std::move(settings), std::move(ownedState)};
|
||||
}
|
||||
NIXC_CATCH_ERRS_NULL
|
||||
}
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
#ifndef NIX_API_EXPR_INTERNAL_H
|
||||
#define NIX_API_EXPR_INTERNAL_H
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include "nix/fetchers/fetch-settings.hh"
|
||||
#include "nix/expr/eval.hh"
|
||||
#include "nix/expr/eval-settings.hh"
|
||||
@@ -22,9 +24,11 @@ struct nix_eval_state_builder
|
||||
|
||||
struct EvalState
|
||||
{
|
||||
nix::fetchers::Settings fetchSettings;
|
||||
nix::EvalSettings settings;
|
||||
nix::EvalState state;
|
||||
nix::EvalState & state;
|
||||
// Owned resources; null for temporary wrappers created in C API callbacks.
|
||||
std::unique_ptr<nix::fetchers::Settings> ownedFetchSettings;
|
||||
std::unique_ptr<nix::EvalSettings> ownedSettings;
|
||||
std::shared_ptr<nix::EvalState> ownedState;
|
||||
};
|
||||
|
||||
struct BindingsBuilder
|
||||
|
||||
@@ -137,7 +137,8 @@ public:
|
||||
}
|
||||
nix_string_context ctx{context};
|
||||
nix_string_return res{""};
|
||||
desc.printValueAsJSON(v, (EvalState *) &state, strict, &ctx, copyToStore, &res);
|
||||
EvalState wrapper{state};
|
||||
desc.printValueAsJSON(v, &wrapper, strict, &ctx, copyToStore, &res);
|
||||
if (res.str.empty()) {
|
||||
return nix::ExternalValueBase::printValueAsJSON(state, strict, context, copyToStore);
|
||||
}
|
||||
@@ -153,22 +154,16 @@ public:
|
||||
bool location,
|
||||
nix::XMLWriter & doc,
|
||||
nix::NixStringContext & context,
|
||||
nix::PathSet & drvsSeen,
|
||||
nix::StringSet & drvsSeen,
|
||||
const nix::PosIdx pos) const override
|
||||
{
|
||||
if (!desc.printValueAsXML) {
|
||||
return nix::ExternalValueBase::printValueAsXML(state, strict, location, doc, context, drvsSeen, pos);
|
||||
}
|
||||
nix_string_context ctx{context};
|
||||
EvalState wrapper{state};
|
||||
desc.printValueAsXML(
|
||||
v,
|
||||
(EvalState *) &state,
|
||||
strict,
|
||||
location,
|
||||
&doc,
|
||||
&ctx,
|
||||
&drvsSeen,
|
||||
*reinterpret_cast<const uint32_t *>(&pos));
|
||||
v, &wrapper, strict, location, &doc, &ctx, &drvsSeen, *reinterpret_cast<const uint32_t *>(&pos));
|
||||
}
|
||||
|
||||
virtual ~NixCExternalValue() override {};
|
||||
|
||||
@@ -145,6 +145,7 @@ typedef struct NixCExternalValueDesc
|
||||
* Optional, the default is to throw an error
|
||||
* @todo The mechanisms for this call are incomplete. There are no C
|
||||
* bindings to work with XML, pathsets and positions.
|
||||
* This callback also has no test coverage.
|
||||
* @param[in] self the void* passed to nix_create_external_value
|
||||
* @param[in] state The evaluator state
|
||||
* @param[in] strict boolean Whether to force the value before printing
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
#include "nix/expr/attr-set.hh"
|
||||
#include "nix/expr/eval-error.hh"
|
||||
#include "nix/util/configuration.hh"
|
||||
#include "nix/expr/eval.hh"
|
||||
#include "nix/store/globals.hh"
|
||||
@@ -104,11 +105,17 @@ static void nix_c_primop_wrapper(
|
||||
nix_value * external_arg = new_nix_value(args[i], state.mem);
|
||||
external_args.push_back(external_arg);
|
||||
}
|
||||
f(userdata, &ctx, (EvalState *) &state, external_args.data(), vTmpPtr);
|
||||
EvalState wrapper{state};
|
||||
f(userdata, &ctx, &wrapper, external_args.data(), vTmpPtr);
|
||||
|
||||
if (ctx.last_err_code != NIX_OK) {
|
||||
/* TODO: Throw different errors depending on the error code */
|
||||
state.error<nix::EvalError>("Error from custom function: %s", *ctx.last_err).atPos(pos).debugThrow();
|
||||
if (ctx.last_err_code == NIX_ERR_RECOVERABLE) {
|
||||
state.error<nix::RecoverableEvalError>("Recoverable error from custom function: %s", *ctx.last_err)
|
||||
.atPos(pos)
|
||||
.debugThrow();
|
||||
} else {
|
||||
state.error<nix::EvalError>("Error from custom function: %s", *ctx.last_err).atPos(pos).debugThrow();
|
||||
}
|
||||
}
|
||||
|
||||
if (!vTmp.isValid()) {
|
||||
@@ -153,7 +160,7 @@ PrimOp * nix_alloc_primop(
|
||||
.args = {},
|
||||
.arity = (size_t) arity,
|
||||
.doc = doc,
|
||||
.fun = std::bind(nix_c_primop_wrapper, fun, user_data, arity, _1, _2, _3, _4)};
|
||||
.impl = std::bind(nix_c_primop_wrapper, fun, user_data, arity, _1, _2, _3, _4)};
|
||||
if (args)
|
||||
for (size_t i = 0; args[i]; i++)
|
||||
p->args.emplace_back(*args);
|
||||
@@ -194,6 +201,8 @@ ValueType nix_get_type(nix_c_context * context, const nix_value * value)
|
||||
switch (v.type()) {
|
||||
case nThunk:
|
||||
return NIX_TYPE_THUNK;
|
||||
case nFailed:
|
||||
return NIX_TYPE_FAILED;
|
||||
case nInt:
|
||||
return NIX_TYPE_INT;
|
||||
case nFloat:
|
||||
|
||||
@@ -100,7 +100,8 @@ typedef enum {
|
||||
/** @brief External value from C++ plugins or C API
|
||||
* @see Externals
|
||||
*/
|
||||
NIX_TYPE_EXTERNAL
|
||||
NIX_TYPE_EXTERNAL,
|
||||
NIX_TYPE_FAILED,
|
||||
} ValueType;
|
||||
|
||||
// forward declarations
|
||||
|
||||
@@ -12,21 +12,22 @@ class nix_api_expr_test : public nix_api_store_test
|
||||
{
|
||||
protected:
|
||||
|
||||
nix_api_expr_test()
|
||||
void SetUp() override
|
||||
{
|
||||
nix_api_store_test::SetUp();
|
||||
nix_libexpr_init(ctx);
|
||||
state = nix_state_create(nullptr, nullptr, store);
|
||||
value = nix_alloc_value(nullptr, state);
|
||||
}
|
||||
|
||||
~nix_api_expr_test()
|
||||
void TearDown() override
|
||||
{
|
||||
nix_gc_decref(nullptr, value);
|
||||
nix_state_free(state);
|
||||
}
|
||||
|
||||
EvalState * state;
|
||||
nix_value * value;
|
||||
EvalState * state = nullptr;
|
||||
nix_value * value = nullptr;
|
||||
};
|
||||
|
||||
} // namespace nixC
|
||||
|
||||
@@ -37,7 +37,8 @@ static void BM_EvalDynamicAttrs(benchmark::State & state)
|
||||
EvalSettings evalSettings{readOnlyMode};
|
||||
evalSettings.nixPath = {};
|
||||
|
||||
EvalState st({}, store, fetchSettings, evalSettings, nullptr);
|
||||
auto stPtr = std::make_shared<EvalState>(LookupPath{}, store, fetchSettings, evalSettings, nullptr);
|
||||
auto & st = *stPtr;
|
||||
Expr * expr = st.parseExprFromString(exprStr, st.rootPath(CanonPath::root));
|
||||
|
||||
Value v;
|
||||
|
||||
@@ -16,7 +16,8 @@ struct GetDerivationsEnv
|
||||
fetchers::Settings fetchSettings{};
|
||||
bool readOnlyMode = true;
|
||||
EvalSettings evalSettings{readOnlyMode};
|
||||
EvalState state;
|
||||
std::shared_ptr<EvalState> statePtr;
|
||||
EvalState & state;
|
||||
|
||||
Bindings * autoArgs = nullptr;
|
||||
Value attrsValue;
|
||||
@@ -27,7 +28,8 @@ struct GetDerivationsEnv
|
||||
settings.nixPath = {};
|
||||
return settings;
|
||||
}())
|
||||
, state({}, store, fetchSettings, evalSettings, nullptr)
|
||||
, statePtr(std::make_shared<EvalState>(LookupPath{}, store, fetchSettings, evalSettings, nullptr))
|
||||
, state(*statePtr)
|
||||
{
|
||||
autoArgs = state.buildBindings(0).finish();
|
||||
|
||||
|
||||
@@ -84,6 +84,8 @@ test(
|
||||
this_exe,
|
||||
env : {
|
||||
'_NIX_TEST_UNIT_DATA' : meson.current_source_dir() / 'data',
|
||||
'HOME' : meson.current_build_dir() / 'test-home',
|
||||
'NIX_STORE' : '',
|
||||
},
|
||||
protocol : 'gtest',
|
||||
)
|
||||
|
||||
@@ -476,6 +476,52 @@ TEST_F(nix_api_expr_test, nix_expr_primop_nix_err_key_conversion)
|
||||
nix_gc_decref(ctx, result);
|
||||
}
|
||||
|
||||
static void
|
||||
primop_alloc_value(void * user_data, nix_c_context * context, EvalState * state, nix_value ** args, nix_value * ret)
|
||||
{
|
||||
assert(context);
|
||||
assert(state);
|
||||
|
||||
// Regression test: nix_c_primop_wrapper previously cast the inner
|
||||
// nix::EvalState* directly to EvalState* (C wrapper). C API functions
|
||||
// like nix_alloc_value() then accessed state->state at the wrong offset,
|
||||
// causing a segfault.
|
||||
nix_value * v = nix_alloc_value(context, state);
|
||||
assert(v != nullptr);
|
||||
nix_init_int(context, v, 42);
|
||||
nix_copy_value(context, ret, v);
|
||||
nix_gc_decref(nullptr, v);
|
||||
}
|
||||
|
||||
TEST_F(nix_api_expr_test, nix_primop_can_use_state_in_callback)
|
||||
{
|
||||
PrimOp * primop =
|
||||
nix_alloc_primop(ctx, primop_alloc_value, 1, "allocValue", nullptr, "test alloc_value in callback", nullptr);
|
||||
assert_ctx_ok();
|
||||
nix_value * primopValue = nix_alloc_value(ctx, state);
|
||||
assert_ctx_ok();
|
||||
nix_init_primop(ctx, primopValue, primop);
|
||||
assert_ctx_ok();
|
||||
|
||||
nix_value * dummy = nix_alloc_value(ctx, state);
|
||||
assert_ctx_ok();
|
||||
nix_init_int(ctx, dummy, 0);
|
||||
assert_ctx_ok();
|
||||
|
||||
nix_value * result = nix_alloc_value(ctx, state);
|
||||
assert_ctx_ok();
|
||||
nix_value_call(ctx, state, primopValue, dummy, result);
|
||||
assert_ctx_ok();
|
||||
|
||||
auto r = nix_get_int(ctx, result);
|
||||
ASSERT_EQ(42, r);
|
||||
|
||||
nix_gc_decref(ctx, dummy);
|
||||
nix_gc_decref(ctx, result);
|
||||
nix_gc_decref(ctx, primopValue);
|
||||
nix_gc_decref(ctx, primop);
|
||||
}
|
||||
|
||||
TEST_F(nix_api_expr_test, nix_value_call_multi_no_args)
|
||||
{
|
||||
nix_value * n = nix_alloc_value(ctx, state);
|
||||
@@ -517,4 +563,106 @@ TEST_F(nix_api_expr_test, nix_expr_attrset_update)
|
||||
assert_ctx_ok();
|
||||
}
|
||||
|
||||
// The following is a test case for retryable thunks. This is a requirement
|
||||
// for the current way in which NixOps4 evaluates its deployment expressions.
|
||||
// An alternative strategy could be implemented, but unwinding the stack may
|
||||
// be a more efficient way to deal with many suspensions/resumptions, compared
|
||||
// to e.g. using a thread or coroutine stack for each suspended dependency.
|
||||
// This test models the essential bits of a deployment tool that uses such
|
||||
// a strategy.
|
||||
|
||||
// State for the retryable primop - simulates deployment resource availability
|
||||
struct DeploymentResourceState
|
||||
{
|
||||
bool vm_created = false;
|
||||
};
|
||||
|
||||
static void primop_load_resource_input(
|
||||
void * user_data, nix_c_context * context, EvalState * state, nix_value ** args, nix_value * ret)
|
||||
{
|
||||
assert(context);
|
||||
assert(state);
|
||||
auto * resource_state = static_cast<DeploymentResourceState *>(user_data);
|
||||
|
||||
// Get the resource input name argument
|
||||
std::string input_name;
|
||||
if (nix_get_string(context, args[0], OBSERVE_STRING(input_name)) != NIX_OK)
|
||||
return;
|
||||
|
||||
// Only handle "vm_id" input - throw for anything else
|
||||
if (input_name != "vm_id") {
|
||||
std::string error_msg = "unknown resource input: " + input_name;
|
||||
nix_set_err_msg(context, NIX_ERR_NIX_ERROR, error_msg.c_str());
|
||||
return;
|
||||
}
|
||||
|
||||
if (resource_state->vm_created) {
|
||||
// VM has been created, return the ID
|
||||
nix_init_string(context, ret, "vm-12345");
|
||||
} else {
|
||||
// VM not created yet, fail with dependency error
|
||||
nix_set_err_msg(context, NIX_ERR_RECOVERABLE, "VM not yet created");
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(nix_api_expr_test, nix_expr_thunk_re_evaluation_after_deployment)
|
||||
{
|
||||
// This test demonstrates NixOps4's requirement: a thunk calling a primop should be
|
||||
// re-evaluable when deployment resources become available that were not available initially.
|
||||
|
||||
DeploymentResourceState resource_state;
|
||||
|
||||
PrimOp * primop = nix_alloc_primop(
|
||||
ctx,
|
||||
primop_load_resource_input,
|
||||
1,
|
||||
"loadResourceInput",
|
||||
nullptr,
|
||||
"load a deployment resource input",
|
||||
&resource_state);
|
||||
assert_ctx_ok();
|
||||
|
||||
nix_value * primopValue = nix_alloc_value(ctx, state);
|
||||
assert_ctx_ok();
|
||||
nix_init_primop(ctx, primopValue, primop);
|
||||
assert_ctx_ok();
|
||||
|
||||
nix_value * inputName = nix_alloc_value(ctx, state);
|
||||
assert_ctx_ok();
|
||||
nix_init_string(ctx, inputName, "vm_id");
|
||||
assert_ctx_ok();
|
||||
|
||||
// Create a single thunk by using nix_init_apply instead of nix_value_call
|
||||
// This creates a lazy application that can be forced multiple times
|
||||
nix_value * thunk = nix_alloc_value(ctx, state);
|
||||
assert_ctx_ok();
|
||||
nix_init_apply(ctx, thunk, primopValue, inputName);
|
||||
assert_ctx_ok();
|
||||
|
||||
// First force: VM not created yet, should fail
|
||||
nix_value_force(ctx, state, thunk);
|
||||
ASSERT_EQ(NIX_ERR_NIX_ERROR, nix_err_code(ctx));
|
||||
ASSERT_THAT(nix_err_msg(nullptr, ctx, nullptr), testing::HasSubstr("VM not yet created"));
|
||||
|
||||
// Clear the error context for the next attempt
|
||||
nix_c_context_free(ctx);
|
||||
ctx = nix_c_context_create();
|
||||
|
||||
// Simulate deployment process: VM gets created
|
||||
resource_state.vm_created = true;
|
||||
|
||||
// Second force of the SAME thunk: this is where the "failed" value issue appears
|
||||
// With failed value caching, this should fail because the thunk is marked as permanently failed
|
||||
// Without failed value caching (or with retryable failures), this should succeed
|
||||
nix_value_force(ctx, state, thunk);
|
||||
|
||||
// If we get here without error, the thunk was successfully re-evaluated
|
||||
assert_ctx_ok();
|
||||
|
||||
std::string result;
|
||||
nix_get_string(ctx, thunk, OBSERVE_STRING(result));
|
||||
assert_ctx_ok();
|
||||
ASSERT_STREQ("vm-12345", result.c_str());
|
||||
}
|
||||
|
||||
} // namespace nixC
|
||||
|
||||
@@ -66,4 +66,44 @@ TEST_F(nix_api_expr_test, nix_expr_eval_external)
|
||||
nix_state_free(stateFn);
|
||||
}
|
||||
|
||||
static void print_value_as_json_using_state(
|
||||
void * self, EvalState * state, bool strict, nix_string_context * c, bool copyToStore, nix_string_return * res)
|
||||
{
|
||||
// Regression test: same cast bug as in nix_c_primop_wrapper (see primop_alloc_value).
|
||||
nix_value * v = nix_alloc_value(nullptr, state);
|
||||
assert(v != nullptr);
|
||||
nix_gc_decref(nullptr, v);
|
||||
|
||||
nix_set_string_return(res, "42");
|
||||
}
|
||||
|
||||
TEST_F(nix_api_expr_test, nix_external_printValueAsJSON_can_use_state)
|
||||
{
|
||||
NixCExternalValueDesc desc{};
|
||||
desc.print = [](void *, nix_printer *) {};
|
||||
desc.showType = [](void *, nix_string_return *) {};
|
||||
desc.typeOf = [](void *, nix_string_return *) {};
|
||||
desc.printValueAsJSON = print_value_as_json_using_state;
|
||||
|
||||
ExternalValue * val = nix_create_external_value(ctx, &desc, nullptr);
|
||||
assert_ctx_ok();
|
||||
nix_init_external(ctx, value, val);
|
||||
assert_ctx_ok();
|
||||
|
||||
nix_value * toJsonFn = nix_alloc_value(ctx, state);
|
||||
nix_expr_eval_from_string(ctx, state, "builtins.toJSON", ".", toJsonFn);
|
||||
assert_ctx_ok();
|
||||
|
||||
nix_value * result = nix_alloc_value(ctx, state);
|
||||
nix_value_call(ctx, state, toJsonFn, value, result);
|
||||
assert_ctx_ok();
|
||||
|
||||
std::string json_str;
|
||||
nix_get_string(ctx, result, OBSERVE_STRING(json_str));
|
||||
ASSERT_EQ("42", json_str);
|
||||
|
||||
nix_gc_decref(ctx, result);
|
||||
nix_gc_decref(ctx, toJsonFn);
|
||||
}
|
||||
|
||||
} // namespace nixC
|
||||
|
||||
@@ -27,7 +27,8 @@ static void BM_EvalManyBuiltinsMatchSameRegex(benchmark::State & state)
|
||||
EvalSettings evalSettings{readOnlyMode};
|
||||
evalSettings.nixPath = {};
|
||||
|
||||
EvalState st({}, store, fetchSettings, evalSettings, nullptr);
|
||||
auto stPtr = std::make_shared<EvalState>(LookupPath{}, store, fetchSettings, evalSettings, nullptr);
|
||||
auto & st = *stPtr;
|
||||
Expr * expr = st.parseExprFromString(std::string(exprStr), st.rootPath(CanonPath::root));
|
||||
|
||||
Value v;
|
||||
|
||||
@@ -127,7 +127,7 @@ TEST_F(ValuePrintingTests, vLambda)
|
||||
TEST_F(ValuePrintingTests, vPrimOp)
|
||||
{
|
||||
Value vPrimOp;
|
||||
PrimOp primOp{.name = "puppy"};
|
||||
PrimOp primOp{.name = "puppy", .impl = [](EvalState &, const PosIdx, Value **, Value &) {}};
|
||||
vPrimOp.mkPrimOp(&primOp);
|
||||
|
||||
test(vPrimOp, "«primop puppy»");
|
||||
@@ -135,7 +135,7 @@ TEST_F(ValuePrintingTests, vPrimOp)
|
||||
|
||||
TEST_F(ValuePrintingTests, vPrimOpApp)
|
||||
{
|
||||
PrimOp primOp{.name = "puppy"};
|
||||
PrimOp primOp{.name = "puppy", .impl = [](EvalState &, const PosIdx, Value **, Value &) {}};
|
||||
Value vPrimOp;
|
||||
vPrimOp.mkPrimOp(&primOp);
|
||||
|
||||
@@ -188,6 +188,22 @@ TEST_F(ValuePrintingTests, vBlackhole)
|
||||
test(vBlackhole, "«potential infinite recursion»");
|
||||
}
|
||||
|
||||
TEST_F(ValuePrintingTests, vFailed)
|
||||
{
|
||||
Value v;
|
||||
try {
|
||||
throw Error("nope");
|
||||
} catch (...) {
|
||||
v.mkFailed(std::current_exception(), nullptr);
|
||||
}
|
||||
|
||||
// Historically, a tried and then ignored value (e.g. through tryEval) was
|
||||
// reverted to the original thunk.
|
||||
|
||||
test(v, "«thunk»");
|
||||
test(v, ANSI_MAGENTA "«thunk»" ANSI_NORMAL, PrintOptions{.ansiColors = true});
|
||||
}
|
||||
|
||||
TEST_F(ValuePrintingTests, depthAttrs)
|
||||
{
|
||||
Value vOne;
|
||||
@@ -515,7 +531,7 @@ TEST_F(ValuePrintingTests, ansiColorsLambda)
|
||||
|
||||
TEST_F(ValuePrintingTests, ansiColorsPrimOp)
|
||||
{
|
||||
PrimOp primOp{.name = "puppy"};
|
||||
PrimOp primOp{.name = "puppy", .impl = [](EvalState &, const PosIdx, Value **, Value &) {}};
|
||||
Value v;
|
||||
v.mkPrimOp(&primOp);
|
||||
|
||||
@@ -524,7 +540,7 @@ TEST_F(ValuePrintingTests, ansiColorsPrimOp)
|
||||
|
||||
TEST_F(ValuePrintingTests, ansiColorsPrimOpApp)
|
||||
{
|
||||
PrimOp primOp{.name = "puppy"};
|
||||
PrimOp primOp{.name = "puppy", .impl = [](EvalState &, const PosIdx, Value **, Value &) {}};
|
||||
Value vPrimOp;
|
||||
vPrimOp.mkPrimOp(&primOp);
|
||||
|
||||
|
||||
55
src/libexpr/diagnose.cc
Normal file
55
src/libexpr/diagnose.cc
Normal file
@@ -0,0 +1,55 @@
|
||||
#include "nix/expr/diagnose.hh"
|
||||
#include "nix/util/configuration.hh"
|
||||
#include "nix/util/config-impl.hh"
|
||||
#include "nix/util/abstract-setting-to-json.hh"
|
||||
|
||||
#include <nlohmann/json.hpp>
|
||||
|
||||
namespace nix {
|
||||
|
||||
template<>
|
||||
Diagnose BaseSetting<Diagnose>::parse(const std::string & str) const
|
||||
{
|
||||
if (str == "ignore")
|
||||
return Diagnose::Ignore;
|
||||
else if (str == "warn")
|
||||
return Diagnose::Warn;
|
||||
else if (str == "fatal")
|
||||
return Diagnose::Fatal;
|
||||
else
|
||||
throw UsageError("option '%s' has invalid value '%s' (expected 'ignore', 'warn', or 'fatal')", name, str);
|
||||
}
|
||||
|
||||
template<>
|
||||
struct BaseSetting<Diagnose>::trait
|
||||
{
|
||||
static constexpr bool appendable = false;
|
||||
};
|
||||
|
||||
template<>
|
||||
std::string BaseSetting<Diagnose>::to_string() const
|
||||
{
|
||||
switch (value) {
|
||||
case Diagnose::Ignore:
|
||||
return "ignore";
|
||||
case Diagnose::Warn:
|
||||
return "warn";
|
||||
case Diagnose::Fatal:
|
||||
return "fatal";
|
||||
default:
|
||||
unreachable();
|
||||
}
|
||||
}
|
||||
|
||||
NLOHMANN_JSON_SERIALIZE_ENUM(
|
||||
Diagnose,
|
||||
{
|
||||
{Diagnose::Ignore, "ignore"},
|
||||
{Diagnose::Warn, "warn"},
|
||||
{Diagnose::Fatal, "fatal"},
|
||||
});
|
||||
|
||||
/* Explicit instantiation of templates */
|
||||
template class BaseSetting<Diagnose>;
|
||||
|
||||
} // namespace nix
|
||||
@@ -11,7 +11,7 @@
|
||||
namespace nix::eval_cache {
|
||||
|
||||
CachedEvalError::CachedEvalError(ref<AttrCursor> cursor, Symbol attr)
|
||||
: EvalError(cursor->root->state, "cached failure of attribute '%s'", cursor->getAttrPathStr(attr))
|
||||
: CloneableError(cursor->root->state, "cached failure of attribute '%s'", cursor->getAttrPathStr(attr))
|
||||
, cursor(cursor)
|
||||
, attr(attr)
|
||||
{
|
||||
@@ -70,7 +70,7 @@ struct AttrDb
|
||||
{
|
||||
auto state(_state->lock());
|
||||
|
||||
auto cacheDir = std::filesystem::path(getCacheDir()) / "eval-cache-v6";
|
||||
auto cacheDir = getCacheDir() / "eval-cache-v6";
|
||||
createDirs(cacheDir);
|
||||
|
||||
auto dbPath = cacheDir / (fingerprint.to_string(HashFormat::Base16, false) + ".sqlite");
|
||||
|
||||
@@ -1,9 +1,16 @@
|
||||
#include "nix/expr/eval-error.hh"
|
||||
#include "nix/expr/eval.hh"
|
||||
#include "nix/expr/value.hh"
|
||||
#include "nix/store/store-api.hh"
|
||||
|
||||
namespace nix {
|
||||
|
||||
InvalidPathError::InvalidPathError(EvalState & state, const StorePath & path)
|
||||
: CloneableError(state, "path '%s' is not valid", path.to_string())
|
||||
, path{path}
|
||||
{
|
||||
}
|
||||
|
||||
template<class T>
|
||||
EvalErrorBuilder<T> & EvalErrorBuilder<T>::withExitStatus(unsigned int exitStatus)
|
||||
{
|
||||
@@ -114,5 +121,6 @@ template class EvalErrorBuilder<InfiniteRecursionError>;
|
||||
template class EvalErrorBuilder<StackOverflowError>;
|
||||
template class EvalErrorBuilder<InvalidPathError>;
|
||||
template class EvalErrorBuilder<IFDError>;
|
||||
template class EvalErrorBuilder<RecoverableEvalError>;
|
||||
|
||||
} // namespace nix
|
||||
|
||||
@@ -137,7 +137,7 @@ public:
|
||||
: state(state)
|
||||
, sampleInterval(period)
|
||||
, profileFd([&]() {
|
||||
AutoCloseFD fd = openNewFileForWrite(
|
||||
auto fd = openNewFileForWrite(
|
||||
profileFile,
|
||||
0660,
|
||||
{
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
#include "nix/util/users.hh"
|
||||
#include "nix/util/logging.hh"
|
||||
#include "nix/store/globals.hh"
|
||||
#include "nix/store/profiles.hh"
|
||||
#include "nix/expr/eval.hh"
|
||||
@@ -6,6 +7,26 @@
|
||||
|
||||
namespace nix {
|
||||
|
||||
void DeprecatedWarnSetting::assign(const bool & v)
|
||||
{
|
||||
value = v;
|
||||
warn("'%s' is deprecated, use '%s = %s' instead", name, targetName, v ? "warn" : "ignore");
|
||||
if (!target.overridden)
|
||||
target = v ? Diagnose::Warn : Diagnose::Ignore;
|
||||
}
|
||||
|
||||
void DeprecatedWarnSetting::appendOrSet(bool newValue, bool append)
|
||||
{
|
||||
assert(!append);
|
||||
assign(newValue);
|
||||
}
|
||||
|
||||
void DeprecatedWarnSetting::override(const bool & v)
|
||||
{
|
||||
overridden = true;
|
||||
assign(v);
|
||||
}
|
||||
|
||||
/* Very hacky way to parse $NIX_PATH, which is colon-separated, but
|
||||
can contain URLs (e.g. "nixpkgs=https://bla...:foo=https://"). */
|
||||
Strings EvalSettings::parseNixPath(const std::string & s)
|
||||
@@ -70,7 +91,7 @@ Strings EvalSettings::getDefaultNixPath()
|
||||
}
|
||||
};
|
||||
|
||||
add(std::filesystem::path{getNixDefExpr()} / "channels");
|
||||
add(getNixDefExpr() / "channels");
|
||||
auto profilesDirOpts = settings.getProfileDirsOptions();
|
||||
add(rootChannelsDir(profilesDirOpts) / "nixpkgs", "nixpkgs");
|
||||
add(rootChannelsDir(profilesDirOpts));
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
#include "nix/expr/eval.hh"
|
||||
#include "nix/expr/eval-error.hh"
|
||||
#include "nix/expr/eval-settings.hh"
|
||||
#include "nix/expr/primops.hh"
|
||||
#include "nix/expr/print-options.hh"
|
||||
@@ -31,6 +32,7 @@
|
||||
#include <algorithm>
|
||||
#include <cstddef>
|
||||
#include <cstdlib>
|
||||
#include <exception>
|
||||
#include <iostream>
|
||||
#include <sstream>
|
||||
#include <cstring>
|
||||
@@ -155,6 +157,8 @@ std::string_view showType(ValueType type, bool withArticle)
|
||||
return WA("a", "float");
|
||||
case nThunk:
|
||||
return WA("a", "thunk");
|
||||
case nFailed:
|
||||
return WA("an", "error");
|
||||
}
|
||||
unreachable();
|
||||
}
|
||||
@@ -224,10 +228,6 @@ static Symbol getName(const AttrName & name, EvalState & state, Env & env)
|
||||
static constexpr size_t BASE_ENV_SIZE = 128;
|
||||
|
||||
EvalMemory::EvalMemory()
|
||||
#if NIX_USE_BOEHMGC
|
||||
: valueAllocCache(std::allocate_shared<void *>(traceable_allocator<void *>(), nullptr))
|
||||
, env1AllocCache(std::allocate_shared<void *>(traceable_allocator<void *>(), nullptr))
|
||||
#endif
|
||||
{
|
||||
assertGCInitialized();
|
||||
}
|
||||
@@ -364,7 +364,7 @@ EvalState::EvalState(
|
||||
|
||||
EvalState::~EvalState() {}
|
||||
|
||||
void EvalState::allowPathLegacy(const Path & path)
|
||||
void EvalState::allowPathLegacy(const std::string & path)
|
||||
{
|
||||
if (auto rootFS2 = rootFS.dynamic_pointer_cast<AllowListSourceAccessor>())
|
||||
rootFS2->allowPrefix(CanonPath(path));
|
||||
@@ -422,29 +422,36 @@ bool isAllowedURI(std::string_view uri, const Strings & allowedUris)
|
||||
return false;
|
||||
}
|
||||
|
||||
void EvalState::checkURI(const std::string & uri)
|
||||
void EvalState::checkURI(const std::string & uri0)
|
||||
{
|
||||
if (!settings.restrictEval)
|
||||
return;
|
||||
|
||||
if (isAllowedURI(uri, settings.allowedUris.get()))
|
||||
if (isAllowedURI(uri0, settings.allowedUris.get()))
|
||||
return;
|
||||
|
||||
/* If the URI is a path, then check it against allowedPaths as
|
||||
well. */
|
||||
if (isAbsolute(uri)) {
|
||||
if (auto rootFS2 = rootFS.dynamic_pointer_cast<AllowListSourceAccessor>())
|
||||
rootFS2->checkAccess(CanonPath(uri));
|
||||
return;
|
||||
{
|
||||
std::filesystem::path path(uri0);
|
||||
if (path.is_absolute()) {
|
||||
if (auto rootFS2 = rootFS.dynamic_pointer_cast<AllowListSourceAccessor>())
|
||||
rootFS2->checkAccess(CanonPath(path.string()));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (hasPrefix(uri, "file://")) {
|
||||
if (auto rootFS2 = rootFS.dynamic_pointer_cast<AllowListSourceAccessor>())
|
||||
rootFS2->checkAccess(CanonPath(uri.substr(7)));
|
||||
return;
|
||||
try {
|
||||
ParsedURL uri = parseURL(uri0);
|
||||
if (uri.scheme == "file") {
|
||||
if (auto rootFS2 = rootFS.dynamic_pointer_cast<AllowListSourceAccessor>())
|
||||
rootFS2->checkAccess(CanonPath(urlPathToPath(uri.path).string()));
|
||||
return;
|
||||
}
|
||||
} catch (BadURL &) {
|
||||
}
|
||||
|
||||
throw RestrictedPathError("access to URI '%s' is forbidden in restricted mode", uri);
|
||||
throw RestrictedPathError("access to URI '%s' is forbidden in restricted mode", uri0);
|
||||
}
|
||||
|
||||
Value * EvalState::addConstant(const std::string & name, Value & v, Constant info)
|
||||
@@ -1653,7 +1660,7 @@ void EvalState::callFunction(Value & fun, std::span<Value *> args, Value & vRes,
|
||||
primOpCalls[fn->name]++;
|
||||
|
||||
try {
|
||||
fn->fun(*this, vCur.determinePos(noPos), args.data(), vCur);
|
||||
fn->impl(*this, vCur.determinePos(noPos), args.data(), vCur);
|
||||
} catch (Error & e) {
|
||||
if (fn->addTrace)
|
||||
addErrorTrace(e, pos, "while calling the '%1%' builtin", fn->name);
|
||||
@@ -1703,7 +1710,7 @@ void EvalState::callFunction(Value & fun, std::span<Value *> args, Value & vRes,
|
||||
// 2. Create a fake env (arg1, arg2, etc.) and a fake expr (arg1: arg2: etc: builtins.name arg1 arg2
|
||||
// etc)
|
||||
// so the debugger allows to inspect the wrong parameters passed to the builtin.
|
||||
fn->fun(*this, vCur.determinePos(noPos), vArgs, vCur);
|
||||
fn->impl(*this, vCur.determinePos(noPos), vArgs, vCur);
|
||||
} catch (Error & e) {
|
||||
if (fn->addTrace)
|
||||
addErrorTrace(e, pos, "while calling the '%1%' builtin", fn->name);
|
||||
@@ -1987,11 +1994,15 @@ void ExprOpUpdate::eval(EvalState & state, Env & env, Value & v)
|
||||
UpdateQueue q;
|
||||
evalForUpdate(state, env, q);
|
||||
|
||||
v.mkAttrs(&Bindings::emptyBindings);
|
||||
Value vTmp;
|
||||
vTmp.mkAttrs(&Bindings::emptyBindings);
|
||||
|
||||
for (auto & rhs : std::views::reverse(q)) {
|
||||
/* Remember that queue is sorted rightmost attrset first. */
|
||||
eval(state, /*v=*/v, /*v1=*/v, /*v2=*/rhs);
|
||||
eval(state, /*v=*/vTmp, /*v1=*/vTmp, /*v2=*/rhs);
|
||||
}
|
||||
|
||||
v = vTmp;
|
||||
}
|
||||
|
||||
void Expr::evalForUpdate(EvalState & state, Env & env, UpdateQueue & q, std::string_view errorCtx)
|
||||
@@ -2174,6 +2185,54 @@ void ExprBlackHole::eval(EvalState & state, [[maybe_unused]] Env & env, Value &
|
||||
// always force this to be separate, otherwise forceValue may inline it and take
|
||||
// a massive perf hit
|
||||
[[gnu::noinline]]
|
||||
void EvalState::handleEvalExceptionForThunk(Env * env, Expr * expr, Value & v, const PosIdx pos)
|
||||
{
|
||||
if (!env)
|
||||
tryFixupBlackHolePos(v, pos);
|
||||
|
||||
auto e = std::current_exception();
|
||||
Value * recovery = nullptr;
|
||||
try {
|
||||
std::rethrow_exception(e);
|
||||
} catch (const RecoverableEvalError & e) {
|
||||
recovery = allocValue();
|
||||
} catch (...) {
|
||||
}
|
||||
if (recovery) {
|
||||
recovery->mkThunk(env, expr);
|
||||
}
|
||||
v.mkFailed(e, recovery);
|
||||
}
|
||||
|
||||
[[gnu::noinline]]
|
||||
void EvalState::handleEvalExceptionForApp(Value & v, const Value & savedApp)
|
||||
{
|
||||
auto e = std::current_exception();
|
||||
Value * recovery = nullptr;
|
||||
try {
|
||||
std::rethrow_exception(e);
|
||||
} catch (const RecoverableEvalError & e) {
|
||||
recovery = allocValue();
|
||||
} catch (...) {
|
||||
}
|
||||
if (recovery) {
|
||||
*recovery = savedApp;
|
||||
}
|
||||
v.mkFailed(e, recovery);
|
||||
}
|
||||
|
||||
[[gnu::noinline]]
|
||||
void EvalState::handleEvalFailed(Value & v, const PosIdx pos)
|
||||
{
|
||||
assert(v.isFailed());
|
||||
if (auto recoveryValue = v.failed().recoveryValue) {
|
||||
v = *recoveryValue;
|
||||
forceValue(v, pos);
|
||||
} else {
|
||||
v.failed().rethrow();
|
||||
}
|
||||
}
|
||||
|
||||
void EvalState::tryFixupBlackHolePos(Value & v, PosIdx pos)
|
||||
{
|
||||
if (!v.isBlackhole())
|
||||
@@ -2182,7 +2241,8 @@ void EvalState::tryFixupBlackHolePos(Value & v, PosIdx pos)
|
||||
try {
|
||||
std::rethrow_exception(e);
|
||||
} catch (InfiniteRecursionError & e) {
|
||||
e.atPos(positions[pos]);
|
||||
if (!e.hasPos())
|
||||
e.atPos(positions[pos]);
|
||||
} catch (...) {
|
||||
}
|
||||
}
|
||||
@@ -2355,10 +2415,11 @@ std::string_view EvalState::forceStringNoCtx(Value & v, const PosIdx pos, std::s
|
||||
{
|
||||
auto s = forceString(v, pos, errorCtx);
|
||||
if (v.context()) {
|
||||
auto ctxElem = NixStringContextElem::parse((*v.context()->begin())->view());
|
||||
error<EvalError>(
|
||||
"the string '%1%' is not allowed to refer to a store path (such as '%2%')",
|
||||
v.string_view(),
|
||||
(*v.context()->begin())->view())
|
||||
ctxElem.display(*store))
|
||||
.withTrace(pos, errorCtx)
|
||||
.debugThrow();
|
||||
}
|
||||
@@ -2821,8 +2882,11 @@ void EvalState::assertEqValues(Value & v1, Value & v2, const PosIdx pos, std::st
|
||||
}
|
||||
return;
|
||||
|
||||
case nThunk: // Must not be left by forceValue
|
||||
assert(false);
|
||||
// Cannot be returned by forceValue().
|
||||
case nThunk:
|
||||
case nFailed:
|
||||
unreachable();
|
||||
|
||||
default: // Note that we pass compiler flags that should make `default:` unreachable.
|
||||
// Also note that this probably ran after `eqValues`, which implements
|
||||
// the same logic more efficiently (without having to unwind stacks),
|
||||
@@ -2916,8 +2980,11 @@ bool EvalState::eqValues(Value & v1, Value & v2, const PosIdx pos, std::string_v
|
||||
// !!!
|
||||
return v1.fpoint() == v2.fpoint();
|
||||
|
||||
case nThunk: // Must not be left by forceValue
|
||||
assert(false);
|
||||
// Cannot be returned by forceValue().
|
||||
case nThunk:
|
||||
case nFailed:
|
||||
unreachable();
|
||||
|
||||
default: // Note that we pass compiler flags that should make `default:` unreachable.
|
||||
error<EvalError>("eqValues: cannot compare %1% with %2%", showType(v1), showType(v2))
|
||||
.withTrace(pos, errorCtx)
|
||||
@@ -3138,6 +3205,21 @@ Expr * EvalState::parseExprFromString(std::string s, const SourcePath & basePath
|
||||
return parseExprFromString(std::move(s), basePath, staticBaseEnv);
|
||||
}
|
||||
|
||||
ExprAttrs *
|
||||
EvalState::parseReplBindings(std::string s_, const SourcePath & basePath, const std::shared_ptr<StaticEnv> & staticEnv)
|
||||
{
|
||||
return parseReplBindings(s_, s_, basePath, staticEnv);
|
||||
}
|
||||
|
||||
ExprAttrs * EvalState::parseReplBindings(
|
||||
std::string s_, std::string errorSource, const SourcePath & basePath, const std::shared_ptr<StaticEnv> & staticEnv)
|
||||
{
|
||||
auto s = make_ref<std::string>(std::move(errorSource));
|
||||
// flex requires two NUL terminators for yy_scan_buffer
|
||||
s_.append("\0\0", 2);
|
||||
return parseReplBindings(s_.data(), s_.size(), Pos::String{.source = s}, basePath, staticEnv);
|
||||
}
|
||||
|
||||
Expr * EvalState::parseStdin()
|
||||
{
|
||||
// NOTE this method (and parseExprFromString) must take care to *fully copy* their
|
||||
@@ -3279,6 +3361,30 @@ Expr * EvalState::parse(
|
||||
return result;
|
||||
}
|
||||
|
||||
ExprAttrs * EvalState::parseReplBindings(
|
||||
char * text,
|
||||
size_t length,
|
||||
Pos::Origin origin,
|
||||
const SourcePath & basePath,
|
||||
const std::shared_ptr<StaticEnv> & staticEnv)
|
||||
{
|
||||
DocCommentMap tmpDocComments;
|
||||
DocCommentMap * docComments = &tmpDocComments;
|
||||
|
||||
if (auto sourcePath = std::get_if<SourcePath>(&origin)) {
|
||||
auto [it, _] = positionToDocComment.try_emplace(*sourcePath);
|
||||
docComments = &it->second;
|
||||
}
|
||||
|
||||
auto bindings = parseReplBindingsFromBuf(
|
||||
text, length, origin, basePath, mem.exprs, symbols, settings, positions, *docComments, rootFS);
|
||||
assert(bindings);
|
||||
|
||||
bindings->bindVars(*this, staticEnv);
|
||||
|
||||
return bindings;
|
||||
}
|
||||
|
||||
DocComment EvalState::getDocCommentForPos(PosIdx pos)
|
||||
{
|
||||
auto pos2 = positions[pos];
|
||||
|
||||
@@ -101,7 +101,7 @@ StorePath PackageInfo::queryOutPath() const
|
||||
i->pos, *i->value, context, "while evaluating the output path of a derivation");
|
||||
}
|
||||
if (!outPath)
|
||||
throw UnimplementedError("CA derivations are not yet supported");
|
||||
throw Error("derivation does not have attribute 'outPath'");
|
||||
return *outPath;
|
||||
}
|
||||
|
||||
|
||||
76
src/libexpr/include/nix/expr/diagnose.hh
Normal file
76
src/libexpr/include/nix/expr/diagnose.hh
Normal file
@@ -0,0 +1,76 @@
|
||||
#pragma once
|
||||
///@file
|
||||
|
||||
#include <optional>
|
||||
|
||||
#include "nix/util/ansicolor.hh"
|
||||
#include "nix/util/configuration.hh"
|
||||
#include "nix/util/error.hh"
|
||||
#include "nix/util/logging.hh"
|
||||
|
||||
namespace nix {
|
||||
|
||||
/**
|
||||
* Diagnostic level for deprecated or non-portable language features.
|
||||
*/
|
||||
enum struct Diagnose {
|
||||
/**
|
||||
* Ignore the feature without any diagnostic.
|
||||
*/
|
||||
Ignore,
|
||||
/**
|
||||
* Warn when the feature is used, but allow it.
|
||||
*/
|
||||
Warn,
|
||||
/**
|
||||
* Treat the feature as a fatal error.
|
||||
*/
|
||||
Fatal,
|
||||
};
|
||||
|
||||
template<>
|
||||
Diagnose BaseSetting<Diagnose>::parse(const std::string & str) const;
|
||||
|
||||
template<>
|
||||
std::string BaseSetting<Diagnose>::to_string() const;
|
||||
|
||||
/**
|
||||
* Check a diagnostic setting and either do nothing, log a warning, or throw an error.
|
||||
*
|
||||
* The setting name is automatically appended to the error message.
|
||||
*
|
||||
* @param setting The diagnostic setting to check
|
||||
* @param mkError A function that takes a bool (true if fatal, false if warning) and
|
||||
* returns an optional error to throw (or warn with).
|
||||
* Only called if level is not `Ignore`.
|
||||
* If the function returns `std::nullopt`, no diagnostic is emitted.
|
||||
*
|
||||
* @throws The error returned by mkError if level is `Fatal` and mkError returns a value
|
||||
*/
|
||||
template<typename F>
|
||||
void diagnose(const Setting<Diagnose> & setting, F && mkError)
|
||||
{
|
||||
auto withError = [&](bool fatal, auto && handler) {
|
||||
auto maybeError = mkError(fatal);
|
||||
if (!maybeError)
|
||||
return;
|
||||
auto & info = maybeError->unsafeInfo();
|
||||
// Append the setting name to help users find the right setting
|
||||
info.msg = HintFmt("%s (" ANSI_BOLD "%s" ANSI_NORMAL ")", Uncolored(info.msg.str()), setting.name);
|
||||
maybeError->recalcWhat();
|
||||
handler(std::move(*maybeError));
|
||||
};
|
||||
|
||||
switch (setting.get()) {
|
||||
case Diagnose::Ignore:
|
||||
return;
|
||||
case Diagnose::Warn:
|
||||
withError(false, [](auto && error) { logWarning(error.info()); });
|
||||
return;
|
||||
case Diagnose::Fatal:
|
||||
withError(true, [](auto && error) { throw std::move(error); });
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace nix
|
||||
@@ -14,7 +14,7 @@ namespace nix::eval_cache {
|
||||
struct AttrDb;
|
||||
class AttrCursor;
|
||||
|
||||
struct CachedEvalError : EvalError
|
||||
struct CachedEvalError : CloneableError<CachedEvalError, EvalError>
|
||||
{
|
||||
const ref<AttrCursor> cursor;
|
||||
const Symbol attr;
|
||||
@@ -36,7 +36,7 @@ class EvalCache : public std::enable_shared_from_this<EvalCache>
|
||||
|
||||
std::shared_ptr<AttrDb> db;
|
||||
EvalState & state;
|
||||
typedef std::function<Value *()> RootLoader;
|
||||
typedef fun<Value *()> RootLoader;
|
||||
RootLoader rootLoader;
|
||||
RootValue value;
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
#include "nix/util/error.hh"
|
||||
#include "nix/util/pos-idx.hh"
|
||||
#include "nix/store/path.hh"
|
||||
|
||||
namespace nix {
|
||||
|
||||
@@ -18,7 +19,7 @@ class EvalErrorBuilder;
|
||||
*
|
||||
* Most subclasses should inherit from `EvalError` instead of this class.
|
||||
*/
|
||||
class EvalBaseError : public Error
|
||||
class EvalBaseError : public CloneableError<EvalBaseError, Error>
|
||||
{
|
||||
template<class T>
|
||||
friend class EvalErrorBuilder;
|
||||
@@ -26,14 +27,14 @@ public:
|
||||
EvalState & state;
|
||||
|
||||
EvalBaseError(EvalState & state, ErrorInfo && errorInfo)
|
||||
: Error(errorInfo)
|
||||
: CloneableError(errorInfo)
|
||||
, state(state)
|
||||
{
|
||||
}
|
||||
|
||||
template<typename... Args>
|
||||
explicit EvalBaseError(EvalState & state, const std::string & formatString, const Args &... formatArgs)
|
||||
: Error(formatString, formatArgs...)
|
||||
: CloneableError(formatString, formatArgs...)
|
||||
, state(state)
|
||||
{
|
||||
}
|
||||
@@ -60,25 +61,30 @@ MakeError(InfiniteRecursionError, EvalError);
|
||||
* Inherits from EvalBaseError (not EvalError) because resource exhaustion
|
||||
* should not be cached.
|
||||
*/
|
||||
struct StackOverflowError : public EvalBaseError
|
||||
struct StackOverflowError : public CloneableError<StackOverflowError, EvalBaseError>
|
||||
{
|
||||
StackOverflowError(EvalState & state)
|
||||
: EvalBaseError(state, "stack overflow; max-call-depth exceeded")
|
||||
: CloneableError(state, "stack overflow; max-call-depth exceeded")
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
MakeError(IFDError, EvalBaseError);
|
||||
|
||||
struct InvalidPathError : public EvalError
|
||||
/**
|
||||
* An evaluation error which should be retried instead of rethrown.
|
||||
*
|
||||
* A RecoverableEvalError is not an EvalError, because we shouldn't cache it in
|
||||
* the eval cache, as it should be retried anyway.
|
||||
*/
|
||||
MakeError(RecoverableEvalError, EvalBaseError);
|
||||
|
||||
struct InvalidPathError : public CloneableError<InvalidPathError, EvalError>
|
||||
{
|
||||
public:
|
||||
Path path;
|
||||
StorePath path;
|
||||
|
||||
InvalidPathError(EvalState & state, const Path & path)
|
||||
: EvalError(state, "path '%s' is not valid", path)
|
||||
{
|
||||
}
|
||||
InvalidPathError(EvalState & state, const StorePath & path);
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
@@ -33,6 +33,9 @@ using gc_allocator = std::allocator<T>;
|
||||
struct gc
|
||||
{};
|
||||
|
||||
struct gc_cleanup
|
||||
{};
|
||||
|
||||
#endif
|
||||
|
||||
namespace nix {
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
#include "nix/expr/eval.hh"
|
||||
#include "nix/expr/eval-error.hh"
|
||||
#include "nix/expr/eval-settings.hh"
|
||||
#include <exception>
|
||||
|
||||
namespace nix {
|
||||
|
||||
@@ -29,6 +30,11 @@ inline void * EvalMemory::allocBytes(size_t n)
|
||||
Value * EvalMemory::allocValue()
|
||||
{
|
||||
#if NIX_USE_BOEHMGC
|
||||
/* Allocation cache for GC'd Value objects. Boehm GC is already a global resource, so thread_local is
|
||||
a natural solution. Multiple EvalState instances on the same thread will reuse the same cache. */
|
||||
static thread_local std::shared_ptr<void *> valueAllocCache{
|
||||
std::allocate_shared<void *>(traceable_allocator<void *>(), nullptr)};
|
||||
|
||||
/* We use the boehm batch allocator to speed up allocations of Values (of which there are many).
|
||||
GC_malloc_many returns a linked list of objects of the given size, where the first word
|
||||
of each object is also the pointer to the next object in the list. This also means that we
|
||||
@@ -62,6 +68,10 @@ Env & EvalMemory::allocEnv(size_t size)
|
||||
|
||||
#if NIX_USE_BOEHMGC
|
||||
if (size == 1) {
|
||||
/* Allocation cache for size-1 Env objects. Boehm GC is already a global resource, so thread_local is
|
||||
a natural solution. Multiple EvalState instances on the same thread will reuse the same cache. */
|
||||
static thread_local std::shared_ptr<void *> env1AllocCache{
|
||||
std::allocate_shared<void *>(traceable_allocator<void *>(), nullptr)};
|
||||
/* see allocValue for explanations. */
|
||||
if (!*env1AllocCache) {
|
||||
*env1AllocCache = GC_malloc_many(sizeof(Env) + sizeof(Value *));
|
||||
@@ -91,18 +101,25 @@ void EvalState::forceValue(Value & v, const PosIdx pos)
|
||||
Expr * expr = v.thunk().expr;
|
||||
try {
|
||||
v.mkBlackhole();
|
||||
// checkInterrupt();
|
||||
if (env) [[likely]]
|
||||
expr->eval(*this, *env, v);
|
||||
else
|
||||
ExprBlackHole::throwInfiniteRecursionError(*this, v);
|
||||
} catch (...) {
|
||||
v.mkThunk(env, expr);
|
||||
tryFixupBlackHolePos(v, pos);
|
||||
handleEvalExceptionForThunk(env, expr, v, pos);
|
||||
throw;
|
||||
}
|
||||
} else if (v.isApp())
|
||||
callFunction(*v.app().left, *v.app().right, v, pos);
|
||||
} else if (v.isApp()) {
|
||||
Value savedApp = v;
|
||||
try {
|
||||
callFunction(*v.app().left, *v.app().right, v, pos);
|
||||
} catch (...) {
|
||||
handleEvalExceptionForApp(v, savedApp);
|
||||
throw;
|
||||
}
|
||||
} else if (v.isFailed()) {
|
||||
handleEvalFailed(v, pos);
|
||||
}
|
||||
}
|
||||
|
||||
[[gnu::always_inline]]
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#pragma once
|
||||
///@file
|
||||
|
||||
#include "nix/expr/diagnose.hh"
|
||||
#include "nix/expr/eval-profiler-settings.hh"
|
||||
#include "nix/util/configuration.hh"
|
||||
#include "nix/util/source-path.hh"
|
||||
@@ -10,6 +11,36 @@ namespace nix {
|
||||
class EvalState;
|
||||
struct PrimOp;
|
||||
|
||||
/**
|
||||
* A deprecated bool setting that migrates to a `Setting<Diagnose>`.
|
||||
* When set to true, it emits a deprecation warning and sets the target
|
||||
* `Setting<Diagnose>` setting to `Warn`.
|
||||
*/
|
||||
class DeprecatedWarnSetting : public BaseSetting<bool>
|
||||
{
|
||||
Setting<Diagnose> & target;
|
||||
const char * targetName;
|
||||
|
||||
public:
|
||||
DeprecatedWarnSetting(
|
||||
Config * options,
|
||||
Setting<Diagnose> & target,
|
||||
const char * targetName,
|
||||
const std::string & name,
|
||||
const std::string & description,
|
||||
const StringSet & aliases = {})
|
||||
: BaseSetting<bool>(false, true, name, description, aliases, std::nullopt)
|
||||
, target(target)
|
||||
, targetName(targetName)
|
||||
{
|
||||
options->addSetting(this);
|
||||
}
|
||||
|
||||
void assign(const bool & v) override;
|
||||
void appendOrSet(bool newValue, bool append) override;
|
||||
void override(const bool & v) override;
|
||||
};
|
||||
|
||||
struct EvalSettings : Config
|
||||
{
|
||||
/**
|
||||
@@ -36,7 +67,7 @@ struct EvalSettings : Config
|
||||
* if `<scheme>` is a key in this map, then `<arbitrary string>` is
|
||||
* passed to the hook that is the value in this map.
|
||||
*/
|
||||
using LookupPathHooks = std::map<std::string, std::function<LookupPathHook>>;
|
||||
using LookupPathHooks = std::map<std::string, fun<LookupPathHook>>;
|
||||
|
||||
EvalSettings(bool & readOnlyMode, LookupPathHooks lookupPathHooks = {});
|
||||
|
||||
@@ -237,7 +268,7 @@ struct EvalSettings : Config
|
||||
See [Using the `eval-profiler`](@docroot@/advanced-topics/eval-profiler.md).
|
||||
)"};
|
||||
|
||||
Setting<Path> evalProfileFile{
|
||||
Setting<std::filesystem::path> evalProfileFile{
|
||||
this,
|
||||
"nix.profile",
|
||||
"eval-profile-file",
|
||||
@@ -328,20 +359,94 @@ struct EvalSettings : Config
|
||||
This option can be enabled by setting `NIX_ABORT_ON_WARN=1` in the environment.
|
||||
)"};
|
||||
|
||||
Setting<bool> warnShortPathLiterals{
|
||||
Setting<Diagnose> lintShortPathLiterals{
|
||||
this,
|
||||
false,
|
||||
"warn-short-path-literals",
|
||||
Diagnose::Ignore,
|
||||
"lint-short-path-literals",
|
||||
R"(
|
||||
If set to true, the Nix evaluator will warn when encountering relative path literals
|
||||
that don't start with `./` or `../`.
|
||||
Controls handling of relative path literals that don't start with `./` or `../`.
|
||||
|
||||
For example, with this setting enabled, `foo/bar` would emit a warning
|
||||
suggesting to use `./foo/bar` instead.
|
||||
- `ignore`: Ignore without warning (default)
|
||||
- `warn`: Emit a warning suggesting to use `./` prefix
|
||||
- `fatal`: Treat as a parse error
|
||||
|
||||
For example, with this setting set to `warn` or `fatal`, `foo/bar` would
|
||||
suggest using `./foo/bar` instead.
|
||||
|
||||
This is useful for improving code readability and making path literals
|
||||
more explicit.
|
||||
)"};
|
||||
)",
|
||||
};
|
||||
|
||||
DeprecatedWarnSetting warnShortPathLiterals{
|
||||
this,
|
||||
lintShortPathLiterals,
|
||||
"lint-short-path-literals",
|
||||
"warn-short-path-literals",
|
||||
R"(
|
||||
Deprecated. Use [`lint-short-path-literals`](#conf-lint-short-path-literals)` = warn` instead.
|
||||
)",
|
||||
};
|
||||
|
||||
Setting<Diagnose> lintAbsolutePathLiterals{
|
||||
this,
|
||||
Diagnose::Ignore,
|
||||
"lint-absolute-path-literals",
|
||||
R"(
|
||||
Controls handling of absolute path literals (paths starting with `/`) and home path literals (paths starting with `~/`).
|
||||
|
||||
- `ignore`: Ignore without warning (default)
|
||||
- `warn`: Emit a warning about non-portability
|
||||
- `fatal`: Treat as a parse error
|
||||
|
||||
It is true that some files are more difficult to reference with relative paths,
|
||||
because they would require lots of `../../..` upward traversing to reach them.
|
||||
But firstly, it is probably not a good idea to reference these files ---
|
||||
such paths often make Nix expressions less portable and reproducible,
|
||||
as they depend on the file system layout of the machine evaluating the expression.
|
||||
|
||||
Secondly, with [pure evaluation mode](#conf-pure-eval), most such files are prohibited to access anyway,
|
||||
whether by absolute or relative paths.
|
||||
In that case, enabling this lint in fatal mode is less disruptive,
|
||||
because the paths pure eval allows are usually not the ones that would be ergonomically expressed with absolute paths anyway.
|
||||
)",
|
||||
};
|
||||
|
||||
Setting<Diagnose> lintUrlLiterals{
|
||||
this,
|
||||
Diagnose::Ignore,
|
||||
"lint-url-literals",
|
||||
R"(
|
||||
Controls handling of unquoted URLs as part of the Nix language syntax.
|
||||
The Nix language allows for URL literals, like so:
|
||||
|
||||
```
|
||||
$ nix repl
|
||||
nix-repl> http://foo
|
||||
"http://foo"
|
||||
```
|
||||
|
||||
Setting this to `warn` or `fatal` will cause the Nix parser to
|
||||
warn or throw an error when encountering a URL literal:
|
||||
|
||||
```
|
||||
$ nix repl --lint-url-literals fatal
|
||||
nix-repl> http://foo
|
||||
error: URL literal 'http://foo' is deprecated
|
||||
at «string»:1:1:
|
||||
|
||||
1| http://foo
|
||||
| ^
|
||||
```
|
||||
|
||||
Unquoted URLs are being deprecated and their usage is discouraged.
|
||||
|
||||
The reason is that, as opposed to path literals, URLs have no
|
||||
special properties that distinguish them from regular strings, URLs
|
||||
containing query parameters have to be quoted anyway, and unquoted URLs
|
||||
may confuse external tooling.
|
||||
)",
|
||||
};
|
||||
|
||||
Setting<unsigned> bindingsUpdateLayerRhsSizeThreshold{
|
||||
this,
|
||||
|
||||
@@ -120,7 +120,7 @@ struct PrimOp
|
||||
/**
|
||||
* Implementation of the primop.
|
||||
*/
|
||||
std::function<PrimOpFun> fun;
|
||||
fun<PrimOpFun> impl;
|
||||
|
||||
/**
|
||||
* Optional experimental for this to be gated on.
|
||||
@@ -305,18 +305,6 @@ struct StaticEvalSymbols
|
||||
|
||||
class EvalMemory
|
||||
{
|
||||
#if NIX_USE_BOEHMGC
|
||||
/**
|
||||
* Allocation cache for GC'd Value objects.
|
||||
*/
|
||||
std::shared_ptr<void *> valueAllocCache;
|
||||
|
||||
/**
|
||||
* Allocation cache for size-1 Env objects.
|
||||
*/
|
||||
std::shared_ptr<void *> env1AllocCache;
|
||||
#endif
|
||||
|
||||
public:
|
||||
struct Statistics
|
||||
{
|
||||
@@ -548,7 +536,7 @@ public:
|
||||
/**
|
||||
* Variant which accepts relative paths too.
|
||||
*/
|
||||
SourcePath rootPath(PathView path);
|
||||
SourcePath rootPath(std::string_view path);
|
||||
|
||||
/**
|
||||
* Return a `SourcePath` that refers to `path` in the store.
|
||||
@@ -565,7 +553,7 @@ public:
|
||||
* Only for restrict eval: pure eval just whitelist store paths,
|
||||
* never arbitrary paths.
|
||||
*/
|
||||
void allowPathLegacy(const Path & path);
|
||||
void allowPathLegacy(const std::string & path);
|
||||
|
||||
/**
|
||||
* Allow access to a store path. Note that this gets remapped to
|
||||
@@ -603,6 +591,18 @@ public:
|
||||
parseExprFromString(std::string s, const SourcePath & basePath, const std::shared_ptr<StaticEnv> & staticEnv);
|
||||
Expr * parseExprFromString(std::string s, const SourcePath & basePath);
|
||||
|
||||
/**
|
||||
* Parse REPL bindings from the specified string.
|
||||
* Returns ExprAttrs with bindings to add to scope.
|
||||
*/
|
||||
ExprAttrs *
|
||||
parseReplBindings(std::string s, const SourcePath & basePath, const std::shared_ptr<StaticEnv> & staticEnv);
|
||||
ExprAttrs * parseReplBindings(
|
||||
std::string s,
|
||||
std::string errorSource,
|
||||
const SourcePath & basePath,
|
||||
const std::shared_ptr<StaticEnv> & staticEnv);
|
||||
|
||||
Expr * parseStdin();
|
||||
|
||||
/**
|
||||
@@ -652,8 +652,28 @@ public:
|
||||
*/
|
||||
inline void forceValue(Value & v, const PosIdx pos);
|
||||
|
||||
private:
|
||||
|
||||
/**
|
||||
* Internal support function for forceValue
|
||||
*
|
||||
* This code is factored out so that it's not in the heavily inlined hot path.
|
||||
*/
|
||||
void handleEvalExceptionForThunk(Env * env, Expr * expr, Value & v, const PosIdx pos);
|
||||
|
||||
/**
|
||||
* Internal support function for forceValue
|
||||
*
|
||||
* This code is factored out so that it's not in the heavily inlined hot path.
|
||||
*/
|
||||
void handleEvalExceptionForApp(Value & v, const Value & savedApp);
|
||||
|
||||
void handleEvalFailed(Value & v, PosIdx pos);
|
||||
|
||||
void tryFixupBlackHolePos(Value & v, PosIdx pos);
|
||||
|
||||
public:
|
||||
|
||||
/**
|
||||
* Force a value, then recursively force list elements and
|
||||
* attributes.
|
||||
@@ -867,6 +887,13 @@ private:
|
||||
const SourcePath & basePath,
|
||||
const std::shared_ptr<StaticEnv> & staticEnv);
|
||||
|
||||
ExprAttrs * parseReplBindings(
|
||||
char * text,
|
||||
size_t length,
|
||||
Pos::Origin origin,
|
||||
const SourcePath & basePath,
|
||||
const std::shared_ptr<StaticEnv> & staticEnv);
|
||||
|
||||
/**
|
||||
* Current Nix call stack depth, used with `max-call-depth` setting to throw stack overflow hopefully before we run
|
||||
* out of system stack.
|
||||
|
||||
@@ -11,6 +11,7 @@ headers = [ config_pub_h ] + files(
|
||||
'attr-path.hh',
|
||||
'attr-set.hh',
|
||||
'counter.hh',
|
||||
'diagnose.hh',
|
||||
'eval-cache.hh',
|
||||
'eval-error.hh',
|
||||
'eval-gc.hh',
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
#include <cstring>
|
||||
#include <memory>
|
||||
#include <memory_resource>
|
||||
#include <exception>
|
||||
#include <span>
|
||||
#include <string_view>
|
||||
#include <type_traits>
|
||||
@@ -42,6 +43,7 @@ enum InternalType {
|
||||
tBool,
|
||||
tNull,
|
||||
tFloat,
|
||||
tFailed,
|
||||
tExternal,
|
||||
tPrimOp,
|
||||
tAttrs,
|
||||
@@ -68,6 +70,7 @@ enum InternalType {
|
||||
*/
|
||||
typedef enum {
|
||||
nThunk,
|
||||
nFailed,
|
||||
nInt,
|
||||
nFloat,
|
||||
nBool,
|
||||
@@ -152,7 +155,7 @@ public:
|
||||
bool location,
|
||||
XMLWriter & doc,
|
||||
NixStringContext & context,
|
||||
PathSet & drvsSeen,
|
||||
StringSet & drvsSeen,
|
||||
const PosIdx pos) const;
|
||||
|
||||
virtual ~ExternalValueBase() {};
|
||||
@@ -424,6 +427,36 @@ struct ValueBase
|
||||
size_t size;
|
||||
Value * const * elems;
|
||||
};
|
||||
|
||||
struct Failed : gc_cleanup
|
||||
{
|
||||
std::exception_ptr ex;
|
||||
/**
|
||||
* Optional value for recovering `RecoverableEvalError`
|
||||
* Must be set iff `ex` is an instance of `RecoverableEvalError`.
|
||||
*/
|
||||
Value * recoveryValue;
|
||||
|
||||
Failed(std::exception_ptr ex, Value * recoveryValue)
|
||||
: ex(ex)
|
||||
, recoveryValue(recoveryValue)
|
||||
{
|
||||
}
|
||||
|
||||
[[noreturn]] void rethrow() const
|
||||
{
|
||||
try {
|
||||
std::rethrow_exception(ex);
|
||||
} catch (BaseError & e) {
|
||||
/* Rethrow the copy of the exception - not the original one.
|
||||
Stack tracing mechanisms rely on being able to modify the exceptions
|
||||
they catch by reference. */
|
||||
e.throwClone();
|
||||
} catch (...) {
|
||||
throw;
|
||||
}
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
@@ -450,6 +483,7 @@ struct PayloadTypeToInternalType
|
||||
MACRO(PrimOp *, primOp, tPrimOp) \
|
||||
MACRO(ValueBase::PrimOpApplicationThunk, primOpApp, tPrimOpApp) \
|
||||
MACRO(ExternalValueBase *, external, tExternal) \
|
||||
MACRO(ValueBase::Failed *, failed, tFailed) \
|
||||
MACRO(NixFloat, fpoint, tFloat)
|
||||
|
||||
#define NIX_VALUE_PAYLOAD_TYPE(T, FIELD_NAME, DISCRIMINATOR) \
|
||||
@@ -754,6 +788,11 @@ protected:
|
||||
path.path = std::bit_cast<const StringData *>(payload[1]);
|
||||
}
|
||||
|
||||
void getStorage(Failed *& failed) const noexcept
|
||||
{
|
||||
failed = std::bit_cast<Failed *>(payload[1]);
|
||||
}
|
||||
|
||||
void setStorage(NixInt integer) noexcept
|
||||
{
|
||||
setSingleDWordPayload<tInt>(integer.value);
|
||||
@@ -803,6 +842,11 @@ protected:
|
||||
{
|
||||
setUntaggablePayload<pdPath>(path.accessor, path.path);
|
||||
}
|
||||
|
||||
void setStorage(Failed * failed) noexcept
|
||||
{
|
||||
setSingleDWordPayload<tFailed>(std::bit_cast<PackedPointer>(failed));
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -1054,12 +1098,12 @@ public:
|
||||
inline bool isThunk() const
|
||||
{
|
||||
return isa<tThunk>();
|
||||
};
|
||||
}
|
||||
|
||||
inline bool isApp() const
|
||||
{
|
||||
return isa<tApp>();
|
||||
};
|
||||
}
|
||||
|
||||
inline bool isBlackhole() const;
|
||||
|
||||
@@ -1067,17 +1111,22 @@ public:
|
||||
inline bool isLambda() const
|
||||
{
|
||||
return isa<tLambda>();
|
||||
};
|
||||
}
|
||||
|
||||
inline bool isPrimOp() const
|
||||
{
|
||||
return isa<tPrimOp>();
|
||||
};
|
||||
}
|
||||
|
||||
inline bool isPrimOpApp() const
|
||||
{
|
||||
return isa<tPrimOpApp>();
|
||||
};
|
||||
}
|
||||
|
||||
inline bool isFailed() const
|
||||
{
|
||||
return isa<tFailed>();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the normal type of a Value. This only returns nThunk if
|
||||
@@ -1098,6 +1147,7 @@ public:
|
||||
t[tBool] = nBool;
|
||||
t[tNull] = nNull;
|
||||
t[tFloat] = nFloat;
|
||||
t[tFailed] = nFailed;
|
||||
t[tExternal] = nExternal;
|
||||
t[tAttrs] = nAttrs;
|
||||
t[tPrimOp] = nFunction;
|
||||
@@ -1235,6 +1285,11 @@ public:
|
||||
setStorage(n);
|
||||
}
|
||||
|
||||
inline void mkFailed(std::exception_ptr e, Value * recovery) noexcept
|
||||
{
|
||||
setStorage(new Value::Failed(e, recovery));
|
||||
}
|
||||
|
||||
bool isList() const noexcept
|
||||
{
|
||||
return isa<tListSmall, tListN>();
|
||||
@@ -1349,6 +1404,13 @@ public:
|
||||
{
|
||||
return getStorage<Path>().accessor;
|
||||
}
|
||||
|
||||
Failed & failed() const noexcept
|
||||
{
|
||||
auto p = getStorage<Failed *>();
|
||||
assert(p);
|
||||
return *p;
|
||||
}
|
||||
};
|
||||
|
||||
extern ExprBlackHole eBlackHole;
|
||||
|
||||
@@ -9,14 +9,14 @@
|
||||
|
||||
namespace nix {
|
||||
|
||||
class BadNixStringContextElem : public Error
|
||||
class BadNixStringContextElem final : public CloneableError<BadNixStringContextElem, Error>
|
||||
{
|
||||
public:
|
||||
std::string_view raw;
|
||||
|
||||
template<typename... Args>
|
||||
BadNixStringContextElem(std::string_view raw_, const Args &... args)
|
||||
: Error("")
|
||||
: CloneableError("")
|
||||
{
|
||||
raw = raw_;
|
||||
auto hf = HintFmt(args...);
|
||||
@@ -83,6 +83,14 @@ struct NixStringContextElem
|
||||
static NixStringContextElem
|
||||
parse(std::string_view s, const ExperimentalFeatureSettings & xpSettings = experimentalFeatureSettings);
|
||||
std::string to_string() const;
|
||||
|
||||
/**
|
||||
* Render for use in error messages and other user-facing output.
|
||||
*
|
||||
* Uses store paths and `DerivedPath` syntax, unlike `to_string()`
|
||||
* which uses the internal encoding.
|
||||
*/
|
||||
std::string display(const StoreDirConfig & store) const;
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
%x INPATH
|
||||
%x INPATH_SLASH
|
||||
%x PATH_START
|
||||
%x REPL_BINDINGS_MODE
|
||||
|
||||
%top {
|
||||
#include "parser-tab.hh" // YYSTYPE
|
||||
@@ -113,6 +114,13 @@ URI [a-zA-Z][a-zA-Z0-9\+\-\.]*\:[a-zA-Z0-9\%\/\?\:\@\&\=\+\$\,\-\_\.\!\~
|
||||
|
||||
%%
|
||||
|
||||
/* REPL bindings mode: inject REPL_BINDINGS token at start, then switch to normal lexing */
|
||||
<REPL_BINDINGS_MODE>.|\n {
|
||||
yyless(0);
|
||||
yylloc->unstash();
|
||||
POP_STATE();
|
||||
return REPL_BINDINGS;
|
||||
}
|
||||
|
||||
if { return IF; }
|
||||
then { return THEN; }
|
||||
@@ -339,3 +347,17 @@ or { return OR_KW; }
|
||||
}
|
||||
|
||||
%%
|
||||
|
||||
#include <type_traits>
|
||||
|
||||
// Verify that the forward declaration in parser.y matches flex's definition
|
||||
static_assert(std::is_same_v<yyscan_t, void *>);
|
||||
|
||||
namespace nix {
|
||||
|
||||
void setReplBindingsMode(yyscan_t scanner)
|
||||
{
|
||||
yy_push_state(REPL_BINDINGS_MODE, scanner);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -153,6 +153,7 @@ endforeach
|
||||
sources = files(
|
||||
'attr-path.cc',
|
||||
'attr-set.cc',
|
||||
'diagnose.cc',
|
||||
'eval-cache.cc',
|
||||
'eval-error.cc',
|
||||
'eval-gc.cc',
|
||||
|
||||
@@ -30,6 +30,7 @@
|
||||
#include "nix/expr/nixexpr.hh"
|
||||
#include "nix/expr/eval.hh"
|
||||
#include "nix/expr/eval-settings.hh"
|
||||
#include "nix/expr/diagnose.hh"
|
||||
#include "nix/expr/parser-state.hh"
|
||||
|
||||
#define YY_DECL \
|
||||
@@ -54,6 +55,11 @@
|
||||
} \
|
||||
while (0)
|
||||
|
||||
// Forward declaration for flex scanner type.
|
||||
// lexer-tab.hh is large; avoid including it just for this type.
|
||||
// Asserted with static_assert in lexer.l.
|
||||
typedef void * yyscan_t;
|
||||
|
||||
namespace nix {
|
||||
|
||||
typedef boost::unordered_flat_map<PosIdx, DocComment, std::hash<PosIdx>> DocCommentMap;
|
||||
@@ -70,6 +76,28 @@ Expr * parseExprFromBuf(
|
||||
DocCommentMap & docComments,
|
||||
const ref<SourceAccessor> rootFS);
|
||||
|
||||
/**
|
||||
* Puts the lexer in REPL bindings mode before the first token. This causes
|
||||
* the parser to accept REPL bindings (attribute definitions).
|
||||
*/
|
||||
void setReplBindingsMode(yyscan_t scanner);
|
||||
|
||||
/**
|
||||
* Parse REPL bindings from a buffer.
|
||||
* Returns ExprAttrs with bindings to add to scope.
|
||||
*/
|
||||
ExprAttrs * parseReplBindingsFromBuf(
|
||||
char * text,
|
||||
size_t length,
|
||||
Pos::Origin origin,
|
||||
const SourcePath & basePath,
|
||||
Exprs & exprs,
|
||||
SymbolTable & symbols,
|
||||
const EvalSettings & settings,
|
||||
PosTable & positions,
|
||||
DocCommentMap & docComments,
|
||||
const ref<SourceAccessor> rootFS);
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -175,6 +203,7 @@ static Expr * makeCall(Exprs & exprs, PosIdx pos, Expr * fn, Expr * arg) {
|
||||
%token IND_STRING_OPEN "start of an indented string"
|
||||
%token IND_STRING_CLOSE "end of an indented string"
|
||||
%token ELLIPSIS "'...'"
|
||||
%token REPL_BINDINGS "start of REPL bindings"
|
||||
|
||||
%right IMPL
|
||||
%left OR
|
||||
@@ -196,6 +225,10 @@ start: expr {
|
||||
|
||||
// This parser does not use yynerrs; suppress the warning.
|
||||
(void) yynerrs_;
|
||||
}
|
||||
| REPL_BINDINGS binds1 {
|
||||
state->result = $2;
|
||||
(void) yynerrs_;
|
||||
};
|
||||
|
||||
expr: expr_function;
|
||||
@@ -337,12 +370,14 @@ expr_simple
|
||||
state->exprs.add<ExprString>(state->exprs.alloc, path)});
|
||||
}
|
||||
| URI {
|
||||
static bool noURLLiterals = experimentalFeatureSettings.isEnabled(Xp::NoUrlLiterals);
|
||||
if (noURLLiterals)
|
||||
throw ParseError({
|
||||
.msg = HintFmt("URL literals are disabled"),
|
||||
diagnose(state->settings.lintUrlLiterals, [&](bool fatal) -> std::optional<ParseError> {
|
||||
return ParseError({
|
||||
.msg = HintFmt("URL literals are %s. Consider using a string literal \"%s\" instead",
|
||||
fatal ? "disallowed" : "discouraged",
|
||||
std::string_view($1.p, $1.l)),
|
||||
.pos = state->positions[CUR_POS]
|
||||
});
|
||||
});
|
||||
$$ = state->exprs.add<ExprString>(state->exprs.alloc, $1);
|
||||
}
|
||||
| '(' expr ')' { $$ = $2; }
|
||||
@@ -380,35 +415,57 @@ path_start
|
||||
: PATH {
|
||||
std::string_view literal({$1.p, $1.l});
|
||||
|
||||
/* check for short path literals */
|
||||
if (state->settings.warnShortPathLiterals && literal.front() != '/' && literal.front() != '.') {
|
||||
logWarning({
|
||||
.msg = HintFmt("relative path literal '%s' should be prefixed with '.' for clarity: './%s'. (" ANSI_BOLD "warn-short-path-literals" ANSI_NORMAL " = true)", literal, literal),
|
||||
.pos = state->positions[CUR_POS]
|
||||
if (literal.front() == '/') {
|
||||
diagnose(state->settings.lintAbsolutePathLiterals, [&](bool) -> std::optional<ParseError> {
|
||||
return ParseError({
|
||||
.msg = HintFmt("absolute path literals are not portable. Consider replacing path literal '%s' by a string, relative path, or parameter", literal),
|
||||
.pos = state->positions[CUR_POS]
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
Path path(absPath(literal, state->basePath.path.abs()));
|
||||
/* add back in the trailing '/' to the first segment */
|
||||
if (literal.size() > 1 && literal.back() == '/')
|
||||
path += '/';
|
||||
$$ =
|
||||
/* Absolute paths are always interpreted relative to the
|
||||
root filesystem accessor, rather than the accessor of the
|
||||
current Nix expression. */
|
||||
literal.front() == '/'
|
||||
? state->exprs.add<ExprPath>(state->exprs.alloc, state->rootFS, path)
|
||||
: state->exprs.add<ExprPath>(state->exprs.alloc, state->basePath.accessor, path);
|
||||
auto path = canonPath(literal).string();
|
||||
/* add back in the trailing '/' to the first segment */
|
||||
if (literal.size() > 1 && literal.back() == '/')
|
||||
path += '/';
|
||||
$$ = state->exprs.add<ExprPath>(state->exprs.alloc, state->rootFS, path);
|
||||
} else {
|
||||
/* check for short path literals */
|
||||
diagnose(state->settings.lintShortPathLiterals, [&](bool) -> std::optional<ParseError> {
|
||||
if (literal.front() != '.')
|
||||
return ParseError({
|
||||
.msg = HintFmt("relative path literal '%s' should be prefixed with '.' for clarity: './%s'", literal, literal),
|
||||
.pos = state->positions[CUR_POS]
|
||||
});
|
||||
return std::nullopt;
|
||||
});
|
||||
|
||||
auto basePath = std::filesystem::path(state->basePath.path.abs());
|
||||
auto path = absPath(literal, &basePath).string();
|
||||
/* add back in the trailing '/' to the first segment */
|
||||
if (literal.size() > 1 && literal.back() == '/')
|
||||
path += '/';
|
||||
$$ = state->exprs.add<ExprPath>(state->exprs.alloc, state->basePath.accessor, path);
|
||||
}
|
||||
}
|
||||
| HPATH {
|
||||
std::string_view literal($1.p, $1.l);
|
||||
if (state->settings.pureEval) {
|
||||
throw Error(
|
||||
"the path '%s' can not be resolved in pure mode",
|
||||
std::string_view($1.p, $1.l)
|
||||
literal
|
||||
);
|
||||
}
|
||||
Path path(getHome().string() + std::string($1.p + 1, $1.l - 1));
|
||||
$$ = state->exprs.add<ExprPath>(state->exprs.alloc, ref<SourceAccessor>(state->rootFS), path);
|
||||
diagnose(state->settings.lintAbsolutePathLiterals, [&](bool) -> std::optional<ParseError> {
|
||||
return ParseError({
|
||||
.msg = HintFmt("home path literals are not portable. Consider replacing path literal '%s' by a string, relative path, or parameter", literal),
|
||||
.pos = state->positions[CUR_POS]
|
||||
});
|
||||
});
|
||||
auto path(getHome().string() + std::string($1.p + 1, $1.l - 1));
|
||||
$$ = state->exprs.add<ExprPath>(state->exprs.alloc, state->rootFS, path);
|
||||
}
|
||||
;
|
||||
|
||||
@@ -577,6 +634,51 @@ Expr * parseExprFromBuf(
|
||||
return state.result;
|
||||
}
|
||||
|
||||
ExprAttrs * parseReplBindingsFromBuf(
|
||||
char * text,
|
||||
size_t length,
|
||||
Pos::Origin origin,
|
||||
const SourcePath & basePath,
|
||||
Exprs & exprs,
|
||||
SymbolTable & symbols,
|
||||
const EvalSettings & settings,
|
||||
PosTable & positions,
|
||||
DocCommentMap & docComments,
|
||||
const ref<SourceAccessor> rootFS)
|
||||
{
|
||||
yyscan_t scanner;
|
||||
LexerState lexerState {
|
||||
.positionToDocComment = docComments,
|
||||
.positions = positions,
|
||||
.origin = positions.addOrigin(origin, length),
|
||||
};
|
||||
ParserState state {
|
||||
.lexerState = lexerState,
|
||||
.exprs = exprs,
|
||||
.symbols = symbols,
|
||||
.positions = positions,
|
||||
.basePath = basePath,
|
||||
.origin = lexerState.origin,
|
||||
.rootFS = rootFS,
|
||||
.settings = settings,
|
||||
};
|
||||
|
||||
yylex_init_extra(&lexerState, &scanner);
|
||||
Finally _destroy([&] { yylex_destroy(scanner); });
|
||||
|
||||
yy_scan_buffer(text, length, scanner);
|
||||
setReplBindingsMode(scanner);
|
||||
Parser parser(scanner, &state);
|
||||
parser.parse();
|
||||
|
||||
assert(state.result);
|
||||
// state.result is Expr *, but the REPL_BINDINGS grammar rule
|
||||
// always produces an ExprAttrs via the binds1 production.
|
||||
auto bindings = dynamic_cast<ExprAttrs *>(state.result);
|
||||
assert(bindings);
|
||||
return bindings;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
#pragma GCC diagnostic pop // end ignored "-Wswitch-enum"
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user