1
1
Fork 0
mirror of https://github.com/NixOS/nix.git synced 2025-11-17 07:52:43 +01:00

Merge branch 'master' into document-derivation-and-deriving-path

This commit is contained in:
John Ericson 2025-01-19 11:45:55 -05:00
commit 16bd5a44ef
375 changed files with 6188 additions and 2232 deletions

View file

@ -1,36 +1,54 @@
--- ---
name: Bug report name: Bug report
about: Create a report to help us improve about: Report unexpected or incorrect behaviour
title: '' title: ''
labels: bug labels: bug
assignees: '' assignees: ''
--- ---
**Describe the bug** ## Describe the bug
A clear and concise description of what the bug is. <!--
A clear and concise description of what the bug is.
If you have a problem with a specific package or NixOS, If you have a problem with a specific package or NixOS,
you probably want to file an issue at https://github.com/NixOS/nixpkgs/issues. you probably want to file an issue at https://github.com/NixOS/nixpkgs/issues.
-->
**Steps To Reproduce** ## Steps To Reproduce
1. Go to '...' <!--
2. Click on '....' Example:
3. Scroll down to '....'
4. See error
**Expected behavior** 1. Clone this repository: ...
2. Run `nix-... ...`
3. Observe unexpected behaviour
-->
A clear and concise description of what you expected to happen. ## Expected behavior
**`nix-env --version` output** <!-- A clear and concise description of what you expected to happen. -->
**Additional context** ## Metadata
Add any other context about the problem here. <!-- Please insert the output of running `nix-env --version` below this line -->
**Priorities** ## Additional context
<!-- Add any other context about the problem here. -->
## Checklist
<!-- make sure this issue is not redundant or obsolete -->
- [ ] checked [latest Nix manual] \([source])
- [ ] checked [open bug issues and pull requests] for possible duplicates
[latest Nix manual]: https://nixos.org/manual/nix/unstable/
[source]: https://github.com/NixOS/nix/tree/master/doc/manual/source
[open bug issues and pull requests]: https://github.com/NixOS/nix/labels/bug
---
Add :+1: to [issues you find important](https://github.com/NixOS/nix/issues?q=is%3Aissue+is%3Aopen+sort%3Areactions-%2B1-desc). Add :+1: to [issues you find important](https://github.com/NixOS/nix/issues?q=is%3Aissue+is%3Aopen+sort%3Areactions-%2B1-desc).

View file

@ -1,24 +1,39 @@
--- ---
name: Feature request name: Feature request
about: Suggest an idea for this project about: Suggest a new feature
title: '' title: ''
labels: feature labels: feature
assignees: '' assignees: ''
--- ---
**Is your feature request related to a problem? Please describe.** ## Is your feature request related to a problem?
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
**Describe the solution you'd like** <!-- A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] -->
A clear and concise description of what you want to happen.
**Describe alternatives you've considered** ## Proposed solution
A clear and concise description of any alternative solutions or features you've considered.
**Additional context** <!-- A clear and concise description of what you want to happen. -->
Add any other context or screenshots about the feature request here.
**Priorities** ## Alternative solutions
<!-- A clear and concise description of any alternative solutions or features you've considered. -->
## Additional context
<!-- Add any other context or screenshots about the feature request here. -->
## Checklist
<!-- make sure this issue is not redundant or obsolete -->
- [ ] checked [latest Nix manual] \([source])
- [ ] checked [open feature issues and pull requests] for possible duplicates
[latest Nix manual]: https://nixos.org/manual/nix/unstable/
[source]: https://github.com/NixOS/nix/tree/master/doc/manual/source
[open feature issues and pull requests]: https://github.com/NixOS/nix/labels/feature
---
Add :+1: to [issues you find important](https://github.com/NixOS/nix/issues?q=is%3Aissue+is%3Aopen+sort%3Areactions-%2B1-desc). Add :+1: to [issues you find important](https://github.com/NixOS/nix/issues?q=is%3Aissue+is%3Aopen+sort%3Areactions-%2B1-desc).

View file

@ -23,14 +23,25 @@ assignees: ''
<details><summary>Output</summary> <details><summary>Output</summary>
```log <!-- paste console output inside the below code block -->
<!-- paste console output here and remove this comment --> ```log
``` ```
</details> </details>
## Priorities ## Checklist
<!-- make sure this issue is not redundant or obsolete -->
- [ ] checked [latest Nix manual] \([source])
- [ ] checked [open installer issues and pull requests] for possible duplicates
[latest Nix manual]: https://nixos.org/manual/nix/unstable/
[source]: https://github.com/NixOS/nix/tree/master/doc/manual/source
[open installer issues and pull requests]: https://github.com/NixOS/nix/labels/installer
---
Add :+1: to [issues you find important](https://github.com/NixOS/nix/issues?q=is%3Aissue+is%3Aopen+sort%3Areactions-%2B1-desc). Add :+1: to [issues you find important](https://github.com/NixOS/nix/issues?q=is%3Aissue+is%3Aopen+sort%3Areactions-%2B1-desc).

View file

@ -26,6 +26,6 @@ assignees: ''
[source]: https://github.com/NixOS/nix/tree/master/doc/manual/source [source]: https://github.com/NixOS/nix/tree/master/doc/manual/source
[open documentation issues and pull requests]: https://github.com/NixOS/nix/labels/documentation [open documentation issues and pull requests]: https://github.com/NixOS/nix/labels/documentation
## Priorities ---
Add :+1: to [issues you find important](https://github.com/NixOS/nix/issues?q=is%3Aissue+is%3Aopen+sort%3Areactions-%2B1-desc). Add :+1: to [issues you find important](https://github.com/NixOS/nix/issues?q=is%3Aissue+is%3Aopen+sort%3Areactions-%2B1-desc).

View file

@ -17,10 +17,12 @@ so you understand the process and the expectations.
--> -->
# Motivation ## Motivation
<!-- Briefly explain what the change is about and why it is desirable. --> <!-- Briefly explain what the change is about and why it is desirable. -->
# Context ## Context
<!-- Provide context. Reference open issues if available. --> <!-- Provide context. Reference open issues if available. -->
<!-- Non-trivial change: Briefly outline the implementation strategy. --> <!-- Non-trivial change: Briefly outline the implementation strategy. -->
@ -29,7 +31,7 @@ so you understand the process and the expectations.
<!-- Large change: Provide instructions to reviewers how to read the diff. --> <!-- Large change: Provide instructions to reviewers how to read the diff. -->
# Priorities and Process ---
Add :+1: to [pull requests you find important](https://github.com/NixOS/nix/pulls?q=is%3Aopen+sort%3Areactions-%2B1-desc). Add :+1: to [pull requests you find important](https://github.com/NixOS/nix/pulls?q=is%3Aopen+sort%3Areactions-%2B1-desc).

View file

@ -7,14 +7,28 @@ on:
permissions: read-all permissions: read-all
jobs: jobs:
eval:
runs-on: ubuntu-24.04
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- uses: cachix/install-nix-action@v30
- run: nix --experimental-features 'nix-command flakes' flake show --all-systems --json
tests: tests:
needs: [check_secrets]
strategy: strategy:
fail-fast: false fail-fast: false
matrix: matrix:
os: [ubuntu-latest, macos-latest] include:
runs-on: ${{ matrix.os }} - scenario: on ubuntu
runs-on: ubuntu-24.04
os: linux
- scenario: on macos
runs-on: macos-14
os: darwin
name: tests ${{ matrix.scenario }}
runs-on: ${{ matrix.runs-on }}
timeout-minutes: 60 timeout-minutes: 60
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v4
@ -23,103 +37,54 @@ jobs:
- uses: cachix/install-nix-action@v30 - uses: cachix/install-nix-action@v30
with: with:
# The sandbox would otherwise be disabled by default on Darwin # The sandbox would otherwise be disabled by default on Darwin
extra_nix_config: "sandbox = true" extra_nix_config: |
- run: echo CACHIX_NAME="$(echo $GITHUB_REPOSITORY-install-tests | tr "[A-Z]/" "[a-z]-")" >> $GITHUB_ENV sandbox = true
- uses: cachix/cachix-action@v15 max-jobs = 1
if: needs.check_secrets.outputs.cachix == 'true' - uses: DeterminateSystems/magic-nix-cache-action@main
# 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'
- run: scripts/build-checks
- run: scripts/prepare-installer-for-github-actions
- name: Upload installer tarball
uses: actions/upload-artifact@v4
with: with:
name: '${{ env.CACHIX_NAME }}' name: installer-${{matrix.os}}
signingKey: '${{ secrets.CACHIX_SIGNING_KEY }}' path: out/*
authToken: '${{ secrets.CACHIX_AUTH_TOKEN }}'
- if: matrix.os == 'ubuntu-latest'
run: |
free -h
swapon --show
swap=$(swapon --show --noheadings | head -n 1 | awk '{print $1}')
echo "Found swap: $swap"
sudo swapoff $swap
# resize it (fallocate)
sudo fallocate -l 10G $swap
sudo mkswap $swap
sudo swapon $swap
free -h
(
while sleep 60; do
free -h
done
) &
- run: nix --experimental-features 'nix-command flakes' flake check -L
- run: nix --experimental-features 'nix-command flakes' flake show --all-systems --json
# Steps to test CI automation in your own fork.
# Cachix:
# 1. Sign-up for https://www.cachix.org/
# 2. Create a cache for $githubuser-nix-install-tests
# 3. Create a cachix auth token and save it in https://github.com/$githubuser/nix/settings/secrets/actions in "Repository secrets" as CACHIX_AUTH_TOKEN
# Dockerhub:
# 1. Sign-up for https://hub.docker.com/
# 2. Store your dockerhub username as DOCKERHUB_USERNAME in "Repository secrets" of your fork repository settings (https://github.com/$githubuser/nix/settings/secrets/actions)
# 3. Create an access token in https://hub.docker.com/settings/security and store it as DOCKERHUB_TOKEN in "Repository secrets" of your fork
check_secrets:
permissions:
contents: none
name: Check Cachix and Docker secrets present for installer tests
runs-on: ubuntu-latest
outputs:
cachix: ${{ steps.secret.outputs.cachix }}
docker: ${{ steps.secret.outputs.docker }}
steps:
- name: Check for secrets
id: secret
env:
_CACHIX_SECRETS: ${{ secrets.CACHIX_SIGNING_KEY }}${{ secrets.CACHIX_AUTH_TOKEN }}
_DOCKER_SECRETS: ${{ secrets.DOCKERHUB_USERNAME }}${{ secrets.DOCKERHUB_TOKEN }}
run: |
echo "::set-output name=cachix::${{ env._CACHIX_SECRETS != '' }}"
echo "::set-output name=docker::${{ env._DOCKER_SECRETS != '' }}"
installer:
needs: [tests, check_secrets]
if: github.event_name == 'push' && needs.check_secrets.outputs.cachix == 'true'
runs-on: ubuntu-latest
outputs:
installerURL: ${{ steps.prepare-installer.outputs.installerURL }}
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- run: echo CACHIX_NAME="$(echo $GITHUB_REPOSITORY-install-tests | tr "[A-Z]/" "[a-z]-")" >> $GITHUB_ENV
- uses: cachix/install-nix-action@v30
with:
install_url: https://releases.nixos.org/nix/nix-2.20.3/install
- uses: cachix/cachix-action@v15
with:
name: '${{ env.CACHIX_NAME }}'
signingKey: '${{ secrets.CACHIX_SIGNING_KEY }}'
authToken: '${{ secrets.CACHIX_AUTH_TOKEN }}'
cachixArgs: '-v'
- id: prepare-installer
run: scripts/prepare-installer-for-github-actions
installer_test: installer_test:
needs: [installer, check_secrets] needs: [tests]
if: github.event_name == 'push' && needs.check_secrets.outputs.cachix == 'true'
strategy: strategy:
fail-fast: false fail-fast: false
matrix: matrix:
os: [ubuntu-latest, macos-latest] include:
runs-on: ${{ matrix.os }} - scenario: on ubuntu
runs-on: ubuntu-24.04
os: linux
- scenario: on macos
runs-on: macos-14
os: darwin
name: installer test ${{ matrix.scenario }}
runs-on: ${{ matrix.runs-on }}
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v4
- run: echo CACHIX_NAME="$(echo $GITHUB_REPOSITORY-install-tests | tr "[A-Z]/" "[a-z]-")" >> $GITHUB_ENV - name: Download installer tarball
uses: actions/download-artifact@v4
with:
name: installer-${{matrix.os}}
path: out
- name: Serving installer
id: serving_installer
run: ./scripts/serve-installer-for-github-actions
- uses: cachix/install-nix-action@v30 - uses: cachix/install-nix-action@v30
with: with:
install_url: '${{needs.installer.outputs.installerURL}}' install_url: 'http://localhost:8126/install'
install_options: "--tarball-url-prefix https://${{ env.CACHIX_NAME }}.cachix.org/serve" install_options: "--tarball-url-prefix http://localhost:8126/"
- run: sudo apt install fish zsh - run: sudo apt install fish zsh
if: matrix.os == 'ubuntu-latest' if: matrix.os == 'linux'
- run: brew install fish - run: brew install fish
if: matrix.os == 'macos-latest' if: matrix.os == 'darwin'
- run: exec bash -c "nix-instantiate -E 'builtins.currentTime' --eval" - run: exec bash -c "nix-instantiate -E 'builtins.currentTime' --eval"
- run: exec sh -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 zsh -c "nix-instantiate -E 'builtins.currentTime' --eval"
@ -127,32 +92,50 @@ jobs:
- run: exec bash -c "nix-channel --add https://releases.nixos.org/nixos/unstable/nixos-23.05pre466020.60c1d71f2ba nixpkgs" - 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" - run: exec bash -c "nix-channel --update && nix-env -iA nixpkgs.hello && hello"
# Steps to test CI automation in your own fork.
# 1. Sign-up for https://hub.docker.com/
# 2. Store your dockerhub username as DOCKERHUB_USERNAME in "Repository secrets" of your fork repository settings (https://github.com/$githubuser/nix/settings/secrets/actions)
# 3. Create an access token in https://hub.docker.com/settings/security and store it as DOCKERHUB_TOKEN in "Repository secrets" of your fork
check_secrets:
permissions:
contents: none
name: Check Docker secrets present for installer tests
runs-on: ubuntu-24.04
outputs:
docker: ${{ steps.secret.outputs.docker }}
steps:
- name: Check for secrets
id: secret
env:
_DOCKER_SECRETS: ${{ secrets.DOCKERHUB_USERNAME }}${{ secrets.DOCKERHUB_TOKEN }}
run: |
echo "::set-output name=docker::${{ env._DOCKER_SECRETS != '' }}"
docker_push_image: docker_push_image:
needs: [check_secrets, tests] needs: [tests, vm_tests, check_secrets]
permissions: permissions:
contents: read contents: read
packages: write packages: write
if: >- if: >-
needs.check_secrets.outputs.docker == 'true' &&
github.event_name == 'push' && github.event_name == 'push' &&
github.ref_name == 'master' && github.ref_name == 'master'
needs.check_secrets.outputs.cachix == 'true' && runs-on: ubuntu-24.04
needs.check_secrets.outputs.docker == 'true'
runs-on: ubuntu-latest
steps: steps:
- name: Check for secrets
id: secret
env:
_DOCKER_SECRETS: ${{ secrets.DOCKERHUB_USERNAME }}${{ secrets.DOCKERHUB_TOKEN }}
run: |
echo "::set-output name=docker::${{ env._DOCKER_SECRETS != '' }}"
- uses: actions/checkout@v4 - uses: actions/checkout@v4
with: with:
fetch-depth: 0 fetch-depth: 0
- uses: cachix/install-nix-action@v30 - uses: cachix/install-nix-action@v30
with: with:
install_url: https://releases.nixos.org/nix/nix-2.20.3/install install_url: https://releases.nixos.org/nix/nix-2.20.3/install
- run: echo CACHIX_NAME="$(echo $GITHUB_REPOSITORY-install-tests | tr "[A-Z]/" "[a-z]-")" >> $GITHUB_ENV - uses: DeterminateSystems/magic-nix-cache-action@main
- run: echo NIX_VERSION="$(nix --experimental-features 'nix-command flakes' eval .\#nix.version | tr -d \")" >> $GITHUB_ENV - run: echo NIX_VERSION="$(nix --experimental-features 'nix-command flakes' eval .\#nix.version | tr -d \")" >> $GITHUB_ENV
- uses: cachix/cachix-action@v15
if: needs.check_secrets.outputs.cachix == 'true'
with:
name: '${{ env.CACHIX_NAME }}'
signingKey: '${{ secrets.CACHIX_SIGNING_KEY }}'
authToken: '${{ secrets.CACHIX_AUTH_TOKEN }}'
- run: nix --experimental-features 'nix-command flakes' build .#dockerImage -L - run: nix --experimental-features 'nix-command flakes' build .#dockerImage -L
- run: docker load -i ./result/image.tar.gz - run: docker load -i ./result/image.tar.gz
- run: docker tag nix:$NIX_VERSION ${{ secrets.DOCKERHUB_USERNAME }}/nix:$NIX_VERSION - run: docker tag nix:$NIX_VERSION ${{ secrets.DOCKERHUB_USERNAME }}/nix:$NIX_VERSION
@ -189,16 +172,22 @@ jobs:
docker push $IMAGE_ID:master docker push $IMAGE_ID:master
vm_tests: vm_tests:
runs-on: ubuntu-22.04 runs-on: ubuntu-24.04
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v4
- uses: DeterminateSystems/nix-installer-action@main - uses: DeterminateSystems/nix-installer-action@main
- uses: DeterminateSystems/magic-nix-cache-action@main - uses: DeterminateSystems/magic-nix-cache-action@main
- run: nix build -L .#hydraJobs.tests.githubFlakes .#hydraJobs.tests.tarballFlakes .#hydraJobs.tests.functional_user - run: |
nix build -L \
.#hydraJobs.tests.functional_user \
.#hydraJobs.tests.githubFlakes \
.#hydraJobs.tests.nix-docker \
.#hydraJobs.tests.tarballFlakes \
;
flake_regressions: flake_regressions:
needs: vm_tests needs: vm_tests
runs-on: ubuntu-22.04 runs-on: ubuntu-24.04
steps: steps:
- name: Checkout nix - name: Checkout nix
uses: actions/checkout@v4 uses: actions/checkout@v4
@ -214,4 +203,4 @@ jobs:
path: flake-regressions/tests path: flake-regressions/tests
- uses: DeterminateSystems/nix-installer-action@main - uses: DeterminateSystems/nix-installer-action@main
- uses: DeterminateSystems/magic-nix-cache-action@main - uses: DeterminateSystems/magic-nix-cache-action@main
- run: nix build --out-link ./new-nix && PATH=$(pwd)/new-nix/bin:$PATH MAX_FLAKES=25 flake-regressions/eval-all.sh - run: nix build -L --out-link ./new-nix && PATH=$(pwd)/new-nix/bin:$PATH MAX_FLAKES=25 flake-regressions/eval-all.sh

View file

@ -15,7 +15,7 @@ permissions:
jobs: jobs:
labels: labels:
runs-on: ubuntu-latest runs-on: ubuntu-24.04
if: github.repository_owner == 'NixOS' if: github.repository_owner == 'NixOS'
steps: steps:
- uses: actions/labeler@v5 - uses: actions/labeler@v5

122
.gitignore vendored
View file

@ -1,110 +1,12 @@
Makefile.config
perl/Makefile.config
# /
/aclocal.m4
/autom4te.cache
/precompiled-headers.h.gch
/config.*
/configure
/stamp-h1
/svn-revision
/libtool
/config/config.*
# Default meson build dir # Default meson build dir
/build /build
# /doc/manual/
/doc/manual/*.1
/doc/manual/*.5
/doc/manual/*.8
/doc/manual/generated/*
/doc/manual/nix.json
/doc/manual/conf-file.json
/doc/manual/language.json
/doc/manual/xp-features.json
/doc/manual/source/SUMMARY.md
/doc/manual/source/SUMMARY-rl-next.md
/doc/manual/source/store/types/*
!/doc/manual/source/store/types/index.md.in
/doc/manual/source/command-ref/new-cli
/doc/manual/source/command-ref/conf-file.md
/doc/manual/source/command-ref/experimental-features-shortlist.md
/doc/manual/source/contributing/experimental-feature-descriptions.md
/doc/manual/source/language/builtins.md
/doc/manual/source/language/builtin-constants.md
/doc/manual/source/release-notes/rl-next.md
# /scripts/
/scripts/nix-profile.sh
/scripts/nix-profile-daemon.sh
/scripts/nix-profile.fish
/scripts/nix-profile-daemon.fish
# /src/libexpr/
/src/libexpr/lexer-tab.cc
/src/libexpr/lexer-tab.hh
/src/libexpr/parser-tab.cc
/src/libexpr/parser-tab.hh
/src/libexpr/parser-tab.output
/src/libexpr/nix.tbl
/src/libexpr/tests
/src/libexpr-tests/libnixexpr-tests
# /src/libfetchers
/src/libfetchers-tests/libnixfetchers-tests
# /src/libflake
/src/libflake-tests/libnixflake-tests
# /src/libstore/
*.gen.*
/src/libstore/tests
/src/libstore-tests/libnixstore-tests
# /src/libutil/
/src/libutil/tests
/src/libutil-tests/libnixutil-tests
/src/nix/nix
/src/nix/generated-doc
# /src/nix-env/
/src/nix-env/nix-env
# /src/nix-instantiate/
/src/nix-instantiate/nix-instantiate
# /src/nix-store/
/src/nix-store/nix-store
/src/nix-prefetch-url/nix-prefetch-url
/src/nix-collect-garbage/nix-collect-garbage
# /src/nix-channel/
/src/nix-channel/nix-channel
# /src/nix-build/
/src/nix-build/nix-build
/src/nix-copy-closure/nix-copy-closure
/src/error-demo/error-demo
/src/build-remote/build-remote
# /tests/functional/ # /tests/functional/
/tests/functional/test-tmp
/tests/functional/common/subst-vars.sh /tests/functional/common/subst-vars.sh
/tests/functional/result*
/tests/functional/restricted-innocent /tests/functional/restricted-innocent
/tests/functional/shell
/tests/functional/shell.drv
/tests/functional/repl-result-out
/tests/functional/debugger-test-out /tests/functional/debugger-test-out
/tests/functional/test-libstoreconsumer/test-libstoreconsumer /tests/functional/test-libstoreconsumer/test-libstoreconsumer
/tests/functional/nix-shell
# /tests/functional/lang/ # /tests/functional/lang/
/tests/functional/lang/*.out /tests/functional/lang/*.out
@ -112,27 +14,9 @@ perl/Makefile.config
/tests/functional/lang/*.err /tests/functional/lang/*.err
/tests/functional/lang/*.ast /tests/functional/lang/*.ast
/perl/lib/Nix/Config.pm
/perl/lib/Nix/Store.cc
/misc/systemd/nix-daemon.service
/misc/systemd/nix-daemon.socket
/misc/systemd/nix-daemon.conf
/misc/upstart/nix-daemon.conf
outputs/ outputs/
*.a
*.o
*.o.tmp
*.so
*.dylib
*.dll
*.exe
*.dep
*~ *~
*.pc
*.plist
# GNU Global # GNU Global
GPATH GPATH
@ -147,8 +31,6 @@ GTAGS
compile_commands.json compile_commands.json
*.compile_commands.json *.compile_commands.json
nix-rust/target
result result
result-* result-*
@ -163,3 +45,5 @@ result-*
# Mac OS # Mac OS
.DS_Store .DS_Store
flake-regressions

View file

@ -2,13 +2,11 @@ queue_rules:
- name: default - name: default
# all required tests need to go here # all required tests need to go here
merge_conditions: merge_conditions:
- check-success=installer - check-success=tests on macos
- check-success=installer_test (macos-latest) - check-success=tests on ubuntu
- check-success=installer_test (ubuntu-latest) - check-success=installer test on macos
- check-success=tests (macos-latest) - check-success=installer test on ubuntu
- check-success=tests (ubuntu-latest)
- check-success=vm_tests - check-success=vm_tests
merge_method: rebase
batch_size: 5 batch_size: 5
pull_request_rules: pull_request_rules:
@ -90,3 +88,13 @@ pull_request_rules:
- "2.24-maintenance" - "2.24-maintenance"
labels: labels:
- merge-queue - merge-queue
- name: backport patches to 2.25
conditions:
- label=backport 2.25-maintenance
actions:
backport:
branches:
- "2.25-maintenance"
labels:
- merge-queue

View file

@ -1,6 +0,0 @@
# This is only conditional to work around
# https://github.com/mesonbuild/meson/issues/13293. It should be
# unconditional.
if not (host_machine.system() == 'windows' and cxx.get_id() == 'gcc')
deps_private += dependency('threads')
endif

View file

@ -199,6 +199,7 @@ nix3_manpages = [
'nix3-build', 'nix3-build',
'nix3-bundle', 'nix3-bundle',
'nix3-config', 'nix3-config',
'nix3-config-check',
'nix3-config-show', 'nix3-config-show',
'nix3-copy', 'nix3-copy',
'nix3-daemon', 'nix3-daemon',
@ -206,8 +207,8 @@ nix3_manpages = [
'nix3-derivation', 'nix3-derivation',
'nix3-derivation-show', 'nix3-derivation-show',
'nix3-develop', 'nix3-develop',
#'nix3-doctor',
'nix3-edit', 'nix3-edit',
'nix3-env-shell',
'nix3-eval', 'nix3-eval',
'nix3-flake-archive', 'nix3-flake-archive',
'nix3-flake-check', 'nix3-flake-check',
@ -224,6 +225,7 @@ nix3_manpages = [
'nix3-fmt', 'nix3-fmt',
'nix3-hash-file', 'nix3-hash-file',
'nix3-hash', 'nix3-hash',
'nix3-hash-convert',
'nix3-hash-path', 'nix3-hash-path',
'nix3-hash-to-base16', 'nix3-hash-to-base16',
'nix3-hash-to-base32', 'nix3-hash-to-base32',
@ -238,6 +240,7 @@ nix3_manpages = [
'nix3-nar-cat', 'nix3-nar-cat',
'nix3-nar-dump-path', 'nix3-nar-dump-path',
'nix3-nar-ls', 'nix3-nar-ls',
'nix3-nar-pack',
'nix3-nar', 'nix3-nar',
'nix3-path-info', 'nix3-path-info',
'nix3-print-dev-env', 'nix3-print-dev-env',
@ -260,7 +263,7 @@ nix3_manpages = [
'nix3-repl', 'nix3-repl',
'nix3-run', 'nix3-run',
'nix3-search', 'nix3-search',
#'nix3-shell', 'nix3-store-add',
'nix3-store-add-file', 'nix3-store-add-file',
'nix3-store-add-path', 'nix3-store-add-path',
'nix3-store-cat', 'nix3-store-cat',
@ -270,6 +273,7 @@ nix3_manpages = [
'nix3-store-diff-closures', 'nix3-store-diff-closures',
'nix3-store-dump-path', 'nix3-store-dump-path',
'nix3-store-gc', 'nix3-store-gc',
'nix3-store-info',
'nix3-store-ls', 'nix3-store-ls',
'nix3-store-make-content-addressed', 'nix3-store-make-content-addressed',
'nix3-store', 'nix3-store',

View file

@ -3,7 +3,7 @@
, meson , meson
, ninja , ninja
, lowdown , lowdown-unsandboxed
, mdbook , mdbook
, mdbook-linkcheck , mdbook-linkcheck
, jq , jq
@ -42,7 +42,7 @@ mkMesonDerivation (finalAttrs: {
passthru.externalNativeBuildInputs = [ passthru.externalNativeBuildInputs = [
meson meson
ninja ninja
(lib.getBin lowdown) (lib.getBin lowdown-unsandboxed)
mdbook mdbook
mdbook-linkcheck mdbook-linkcheck
jq jq

View file

@ -0,0 +1,22 @@
---
synopsis: "Flake lock file generation now ignores local registries"
prs: [12019]
---
When resolving indirect flake references like `nixpkgs` in `flake.nix` files, Nix will no longer use the system and user flake registries. It will only use the global flake registry and overrides given on the command line via `--override-flake`.
This avoids accidents where users have local registry overrides that map `nixpkgs` to a `path:` flake in the local file system, which then end up in committed lock files pushed to other users.
In the future, we may remove the use of the registry during lock file generation altogether. It's better to explicitly specify the URL of a flake input. For example, instead of
```nix
{
outputs = { self, nixpkgs }: { ... };
}
```
write
```nix
{
inputs.nixpkgs.url = "github:NixOS/nixpkgs/nixos-24.11";
outputs = { self, nixpkgs }: { ... };
}
```

View file

@ -0,0 +1,18 @@
---
synopsis: "`nix copy` supports `--profile` and `--out-link`"
prs: [11657]
---
The `nix copy` command now has flags `--profile` and `--out-link`, similar to `nix build`. `--profile` makes a profile point to the
top-level store path, while `--out-link` create symlinks to the top-level store paths.
For example, when updating the local NixOS system profile from a NixOS system closure on a remote machine, instead of
```
# nix copy --from ssh://server $path
# nix build --profile /nix/var/nix/profiles/system $path
```
you can now do
```
# nix copy --from ssh://server --profile /nix/var/nix/profiles/system $path
```
The advantage is that this avoids a time window where *path* is not a garbage collector root, and so could be deleted by a concurrent `nix store gc` process.

View file

@ -0,0 +1,8 @@
---
synopsis: "`nix-instantiate --eval` now supports `--raw`"
prs: [12119]
---
The `nix-instantiate --eval` command now supports a `--raw` flag, when used
the evaluation result must be a string, which is printed verbatim without
quotation marks or escaping.

View file

@ -0,0 +1,21 @@
---
synopsis: "Improved `NIX_SSHOPTS` parsing for better SSH option handling"
issues: [5181]
prs: [12020]
---
The parsing of the `NIX_SSHOPTS` environment variable has been improved to handle spaces and quotes correctly.
Previously, incorrectly split SSH options could cause failures in CLIs like `nix-copy-closure`,
especially when using complex ssh invocations such as `-o ProxyCommand="ssh -W %h:%p ..."`.
This change introduces a `shellSplitString` function to ensure
that `NIX_SSHOPTS` is parsed in a manner consistent with shell
behavior, addressing common parsing errors.
For example, the following now works as expected:
```bash
export NIX_SSHOPTS='-o ProxyCommand="ssh -W %h:%p ..."'
```
This update improves the reliability of SSH-related operations using `NIX_SSHOPTS` across Nix CLIs.

View file

@ -0,0 +1,12 @@
---
synopsis: "Support for relative path inputs"
prs: [10089]
---
Flakes can now refer to other flakes in the same repository using relative paths, e.g.
```nix
inputs.foo.url = "path:./foo";
```
uses the flake in the `foo` subdirectory of the referring flake. For more information, see the documentation on [the `path` flake input type](@docroot@/command-ref/new-cli/nix3-flake.md#path-fetcher).
This feature required a change to the lock file format. Previous Nix versions will not be able to use lock files that have locks for relative path inputs in them.

View file

@ -36,7 +36,7 @@ Instead, it looks in a few locations, and acts on all profiles it finds there:
> >
> Not stable; subject to change > Not stable; subject to change
> >
> Do not rely on this functionality; it just exists for migration purposes and is may change in the future. > Do not rely on this functionality; it just exists for migration purposes and may change in the future.
> These deprecated paths remain a private implementation detail of Nix. > These deprecated paths remain a private implementation detail of Nix.
`$NIX_STATE_DIR/profiles` and `$NIX_STATE_DIR/profiles/per-user`. `$NIX_STATE_DIR/profiles` and `$NIX_STATE_DIR/profiles/per-user`.
@ -62,6 +62,15 @@ These options are for deleting old [profiles] prior to deleting unreachable [sto
This is the equivalent of invoking [`nix-env --delete-generations <period>`](@docroot@/command-ref/nix-env/delete-generations.md#generations-time) on each found profile. This is the equivalent of invoking [`nix-env --delete-generations <period>`](@docroot@/command-ref/nix-env/delete-generations.md#generations-time) on each found profile.
See the documentation of that command for additional information about the *period* argument. See the documentation of that command for additional information about the *period* argument.
- <span id="opt-max-freed">[`--max-freed`](#opt-max-freed)</span> *bytes*
<!-- duplication from https://github.com/NixOS/nix/blob/442a2623e48357ff72c77bb11cf2cf06d94d2f90/doc/manual/source/command-ref/nix-store/gc.md?plain=1#L39-L44 -->
Keep deleting paths until at least *bytes* bytes have been deleted,
then stop. The argument *bytes* can be followed by the
multiplicative suffix `K`, `M`, `G` or `T`, denoting KiB, MiB, GiB
or TiB units.
{{#include ./opt-common.md}} {{#include ./opt-common.md}}
{{#include ./env-common.md}} {{#include ./env-common.md}}

View file

@ -84,7 +84,7 @@ When using public key authentication, you can avoid typing the passphrase with `
> Copy GNU Hello from a remote machine using a known store path, and run it: > Copy GNU Hello from a remote machine using a known store path, and run it:
> >
> ```shell-session > ```shell-session
> $ storePath="$(nix-instantiate --eval '<nixpkgs>' -I nixpkgs=channel:nixpkgs-unstable -A hello.outPath | tr -d '"')" > $ storePath="$(nix-instantiate --eval --raw '<nixpkgs>' -I nixpkgs=channel:nixpkgs-unstable -A hello.outPath)"
> $ nix-copy-closure --from alice@itchy.example.org "$storePath" > $ nix-copy-closure --from alice@itchy.example.org "$storePath"
> $ "$storePath"/bin/hello > $ "$storePath"/bin/hello
> Hello, world! > Hello, world!

View file

@ -11,6 +11,7 @@
[`--from-profile` *path*] [`--from-profile` *path*]
[`--preserve-installed` | `-P`] [`--preserve-installed` | `-P`]
[`--remove-all` | `-r`] [`--remove-all` | `-r`]
[`--priority` *priority*]
# Description # Description
@ -61,6 +62,10 @@ The arguments *args* map to store paths in a number of possible ways:
The derivations returned by those function calls are installed. The derivations returned by those function calls are installed.
This allows derivations to be specified in an unambiguous way, which is necessary if there are multiple derivations with the same name. This allows derivations to be specified in an unambiguous way, which is necessary if there are multiple derivations with the same name.
- If `--priority` *priority* is given, the priority of the derivations being installed is set to *priority*.
This can be used to override the priority of the derivations being installed.
This is useful if *args* are [store paths], which don't have any priority information.
- If *args* are [store paths] to [derivations](@docroot@/glossary.md#gloss-derivation), then those derivations are [realised], and the resulting output paths are installed. - If *args* are [store paths] to [derivations](@docroot@/glossary.md#gloss-derivation), then those derivations are [realised], and the resulting output paths are installed.
- If *args* are [store paths] not to derivations, then these are [realised] and installed. - If *args* are [store paths] not to derivations, then these are [realised] and installed.
@ -235,4 +240,3 @@ channel:
```console ```console
$ nix-env --file https://github.com/NixOS/nixpkgs/archive/nixos-14.12.tar.gz --install --attr firefox $ nix-env --file https://github.com/NixOS/nixpkgs/archive/nixos-14.12.tar.gz --install --attr firefox
``` ```

View file

@ -5,7 +5,7 @@
# Synopsis # Synopsis
`nix-instantiate` `nix-instantiate`
[`--parse` | `--eval` [`--strict`] [`--json`] [`--xml`] ] [`--parse` | `--eval` [`--strict`] [`--raw` | `--json` | `--xml`] ]
[`--read-write-mode`] [`--read-write-mode`]
[`--arg` *name* *value*] [`--arg` *name* *value*]
[{`--attr`| `-A`} *attrPath*] [{`--attr`| `-A`} *attrPath*]
@ -102,6 +102,11 @@ standard input.
> This option can cause non-termination, because lazy data > This option can cause non-termination, because lazy data
> structures can be infinitely large. > structures can be infinitely large.
- `--raw`
When used with `--eval`, the evaluation result must be a string,
which is printed verbatim, without quoting, escaping or trailing newline.
- `--json` - `--json`
When used with `--eval`, print the resulting value as an JSON When used with `--eval`, print the resulting value as an JSON

View file

@ -88,7 +88,9 @@ All options not listed here are passed to `nix-store
cleared before the interactive shell is started, so you get an cleared before the interactive shell is started, so you get an
environment that more closely corresponds to the “real” Nix build. A environment that more closely corresponds to the “real” Nix build. A
few variables, in particular `HOME`, `USER` and `DISPLAY`, are few variables, in particular `HOME`, `USER` and `DISPLAY`, are
retained. retained. Note that the shell used to run commands is obtained from
[`NIX_BUILD_SHELL`](#env-NIX_BUILD_SHELL) / `<nixpkgs>` from
`NIX_PATH`, and therefore not affected by `--pure`.
- `--packages` / `-p` *packages* - `--packages` / `-p` *packages*
@ -112,11 +114,30 @@ All options not listed here are passed to `nix-store
# Environment variables # Environment variables
- `NIX_BUILD_SHELL` - <span id="env-NIX_BUILD_SHELL">[`NIX_BUILD_SHELL`](#env-NIX_BUILD_SHELL)</span>
Shell used to start the interactive environment. Defaults to the Shell used to start the interactive environment.
`bash` found in `<nixpkgs>`, falling back to the `bash` found in Defaults to the `bash` from `bashInteractive` found in `<nixpkgs>`, falling back to the `bash` found in `PATH` if not found.
`PATH` if not found.
> **Note**
>
> The shell obtained using this method may not necessarily be the same as any shells requested in *path*.
<!-- -->
> **Example
>
> Despite `--pure`, this invocation will not result in a fully reproducible shell environment:
>
> ```nix
> #!/usr/bin/env -S nix-shell --pure
> let
> pkgs = import (fetchTarball "https://github.com/NixOS/nixpkgs/archive/854fdc68881791812eddd33b2fed94b954979a8e.tar.gz") {};
> in
> pkgs.mkShell {
> buildInputs = pkgs.bashInteractive;
> }
> ```
{{#include ./env-common.md}} {{#include ./env-common.md}}

View file

@ -21,6 +21,9 @@ This operation has the following options:
Use recursive instead of flat hashing mode, used when adding Use recursive instead of flat hashing mode, used when adding
directories to the store. directories to the store.
*paths* that refer to symlinks are not dereferenced, but added to the store
as symlinks with the same target.
{{#include ./opt-common.md}} {{#include ./opt-common.md}}
{{#include ../opt-common.md}} {{#include ../opt-common.md}}

View file

@ -11,6 +11,9 @@
The operation `--add` adds the specified paths to the Nix store. It The operation `--add` adds the specified paths to the Nix store. It
prints the resulting paths in the Nix store on standard output. prints the resulting paths in the Nix store on standard output.
*paths* that refer to symlinks are not dereferenced, but added to the store
as symlinks with the same target.
{{#include ./opt-common.md}} {{#include ./opt-common.md}}
{{#include ../opt-common.md}} {{#include ../opt-common.md}}

View file

@ -19,10 +19,11 @@ nix-build -E '(import ./.).packages.${builtins.currentSystem}.nix.doc'
or or
```console ```console
nix build .#nix^doc nix build .#nix-manual
``` ```
and open `./result-doc/share/doc/nix/manual/index.html`. and open `./result/share/doc/nix/manual/index.html`.
To build the manual incrementally, [enter the development shell](./building.md) and run: To build the manual incrementally, [enter the development shell](./building.md) and run:

View file

@ -297,7 +297,7 @@ Creating a Cachix cache for your installer tests and adding its authorisation to
- `armv7l-linux` - `armv7l-linux`
- `x86_64-darwin` - `x86_64-darwin`
- The `installer_test` job (which runs on `ubuntu-latest` and `macos-latest`) will try to install Nix with the cached installer and run a trivial Nix command. - The `installer_test` job (which runs on `ubuntu-24.04` and `macos-14`) will try to install Nix with the cached installer and run a trivial Nix command.
### One-time setup ### One-time setup

View file

@ -57,3 +57,21 @@ $ nix build ./\#hydraJobs.dockerImage.x86_64-linux
$ docker load -i ./result/image.tar.gz $ docker load -i ./result/image.tar.gz
$ docker run -ti nix:2.5pre20211105 $ docker run -ti nix:2.5pre20211105
``` ```
# Docker image with non-root Nix
If you would like to run Nix in a container under a user other than `root`,
you can build an image with a non-root single-user installation of Nix
by specifying the `uid`, `gid`, `uname`, and `gname` arguments to `docker.nix`:
```console
$ nix build --file docker.nix \
--arg uid 1000 \
--arg gid 1000 \
--argstr uname user \
--argstr gname user \
--argstr name nix-user \
--out-link nix-user.tar.gz
$ docker load -i nix-user.tar.gz
$ docker run -ti nix-user
```

View file

@ -9,6 +9,10 @@
, maxLayers ? 100 , maxLayers ? 100
, nixConf ? {} , nixConf ? {}
, flake-registry ? null , flake-registry ? null
, uid ? 0
, gid ? 0
, uname ? "root"
, gname ? "root"
}: }:
let let
defaultPkgs = with pkgs; [ defaultPkgs = with pkgs; [
@ -50,6 +54,15 @@ let
description = "Unprivileged account (don't use!)"; description = "Unprivileged account (don't use!)";
}; };
} // lib.optionalAttrs (uid != 0) {
"${uname}" = {
uid = uid;
shell = "${pkgs.bashInteractive}/bin/bash";
home = "/home/${uname}";
gid = gid;
groups = [ "${gname}" ];
description = "Nix user";
};
} // lib.listToAttrs ( } // lib.listToAttrs (
map map
( (
@ -70,6 +83,8 @@ let
root.gid = 0; root.gid = 0;
nixbld.gid = 30000; nixbld.gid = 30000;
nobody.gid = 65534; nobody.gid = 65534;
} // lib.optionalAttrs (gid != 0) {
"${gname}".gid = gid;
}; };
userToPasswd = ( userToPasswd = (
@ -150,6 +165,8 @@ let
in in
"${n} = ${vStr}") (defaultNixConf // nixConf))) + "\n"; "${n} = ${vStr}") (defaultNixConf // nixConf))) + "\n";
userHome = if uid == 0 then "/root" else "/home/${uname}";
baseSystem = baseSystem =
let let
nixpkgs = pkgs.path; nixpkgs = pkgs.path;
@ -237,26 +254,26 @@ let
mkdir -p $out/etc/nix mkdir -p $out/etc/nix
cat $nixConfContentsPath > $out/etc/nix/nix.conf cat $nixConfContentsPath > $out/etc/nix/nix.conf
mkdir -p $out/root mkdir -p $out${userHome}
mkdir -p $out/nix/var/nix/profiles/per-user/root mkdir -p $out/nix/var/nix/profiles/per-user/${uname}
ln -s ${profile} $out/nix/var/nix/profiles/default-1-link ln -s ${profile} $out/nix/var/nix/profiles/default-1-link
ln -s $out/nix/var/nix/profiles/default-1-link $out/nix/var/nix/profiles/default ln -s /nix/var/nix/profiles/default-1-link $out/nix/var/nix/profiles/default
ln -s /nix/var/nix/profiles/default $out/root/.nix-profile ln -s /nix/var/nix/profiles/default $out${userHome}/.nix-profile
ln -s ${channel} $out/nix/var/nix/profiles/per-user/root/channels-1-link ln -s ${channel} $out/nix/var/nix/profiles/per-user/${uname}/channels-1-link
ln -s $out/nix/var/nix/profiles/per-user/root/channels-1-link $out/nix/var/nix/profiles/per-user/root/channels ln -s /nix/var/nix/profiles/per-user/${uname}/channels-1-link $out/nix/var/nix/profiles/per-user/${uname}/channels
mkdir -p $out/root/.nix-defexpr mkdir -p $out${userHome}/.nix-defexpr
ln -s $out/nix/var/nix/profiles/per-user/root/channels $out/root/.nix-defexpr/channels ln -s /nix/var/nix/profiles/per-user/${uname}/channels $out${userHome}/.nix-defexpr/channels
echo "${channelURL} ${channelName}" > $out/root/.nix-channels echo "${channelURL} ${channelName}" > $out${userHome}/.nix-channels
mkdir -p $out/bin $out/usr/bin mkdir -p $out/bin $out/usr/bin
ln -s ${pkgs.coreutils}/bin/env $out/usr/bin/env ln -s ${pkgs.coreutils}/bin/env $out/usr/bin/env
ln -s ${pkgs.bashInteractive}/bin/bash $out/bin/sh ln -s ${pkgs.bashInteractive}/bin/bash $out/bin/sh
'' + (lib.optionalString (flake-registry-path != null) '' '' + (lib.optionalString (flake-registry-path != null) ''
nixCacheDir="/root/.cache/nix" nixCacheDir="${userHome}/.cache/nix"
mkdir -p $out$nixCacheDir mkdir -p $out$nixCacheDir
globalFlakeRegistryPath="$nixCacheDir/flake-registry.json" globalFlakeRegistryPath="$nixCacheDir/flake-registry.json"
ln -s ${flake-registry-path} $out$globalFlakeRegistryPath ln -s ${flake-registry-path} $out$globalFlakeRegistryPath
@ -268,7 +285,7 @@ let
in in
pkgs.dockerTools.buildLayeredImageWithNixDb { pkgs.dockerTools.buildLayeredImageWithNixDb {
inherit name tag maxLayers; inherit name tag maxLayers uid gid uname gname;
contents = [ baseSystem ]; contents = [ baseSystem ];
@ -279,25 +296,28 @@ pkgs.dockerTools.buildLayeredImageWithNixDb {
fakeRootCommands = '' fakeRootCommands = ''
chmod 1777 tmp chmod 1777 tmp
chmod 1777 var/tmp chmod 1777 var/tmp
chown -R ${toString uid}:${toString gid} .${userHome}
chown -R ${toString uid}:${toString gid} nix
''; '';
config = { config = {
Cmd = [ "/root/.nix-profile/bin/bash" ]; Cmd = [ "${userHome}/.nix-profile/bin/bash" ];
User = "${toString uid}:${toString gid}";
Env = [ Env = [
"USER=root" "USER=${uname}"
"PATH=${lib.concatStringsSep ":" [ "PATH=${lib.concatStringsSep ":" [
"/root/.nix-profile/bin" "${userHome}/.nix-profile/bin"
"/nix/var/nix/profiles/default/bin" "/nix/var/nix/profiles/default/bin"
"/nix/var/nix/profiles/default/sbin" "/nix/var/nix/profiles/default/sbin"
]}" ]}"
"MANPATH=${lib.concatStringsSep ":" [ "MANPATH=${lib.concatStringsSep ":" [
"/root/.nix-profile/share/man" "${userHome}/.nix-profile/share/man"
"/nix/var/nix/profiles/default/share/man" "/nix/var/nix/profiles/default/share/man"
]}" ]}"
"SSL_CERT_FILE=/nix/var/nix/profiles/default/etc/ssl/certs/ca-bundle.crt" "SSL_CERT_FILE=/nix/var/nix/profiles/default/etc/ssl/certs/ca-bundle.crt"
"GIT_SSL_CAINFO=/nix/var/nix/profiles/default/etc/ssl/certs/ca-bundle.crt" "GIT_SSL_CAINFO=/nix/var/nix/profiles/default/etc/ssl/certs/ca-bundle.crt"
"NIX_SSL_CERT_FILE=/nix/var/nix/profiles/default/etc/ssl/certs/ca-bundle.crt" "NIX_SSL_CERT_FILE=/nix/var/nix/profiles/default/etc/ssl/certs/ca-bundle.crt"
"NIX_PATH=/nix/var/nix/profiles/per-user/root/channels:/root/.nix-defexpr/channels" "NIX_PATH=/nix/var/nix/profiles/per-user/${uname}/channels:${userHome}/.nix-defexpr/channels"
]; ];
}; };

44
flake.lock generated
View file

@ -3,11 +3,11 @@
"flake-compat": { "flake-compat": {
"flake": false, "flake": false,
"locked": { "locked": {
"lastModified": 1696426674, "lastModified": 1733328505,
"narHash": "sha256-kvjfFW7WAETZlt09AgDn1MrtKzP7t90Vf7vypd3OL1U=", "narHash": "sha256-NeCCThCEP3eCl2l/+27kNNK7QrwZB1IJCrXfrbv5oqU=",
"owner": "edolstra", "owner": "edolstra",
"repo": "flake-compat", "repo": "flake-compat",
"rev": "0f9255e01c2351cc7d116c072cb317785dd33b33", "rev": "ff81ac966bb2cae68946d5ed5fc4994f96d0ffec",
"type": "github" "type": "github"
}, },
"original": { "original": {
@ -23,11 +23,11 @@
] ]
}, },
"locked": { "locked": {
"lastModified": 1719994518, "lastModified": 1733312601,
"narHash": "sha256-pQMhCCHyQGRzdfAkdJ4cIWiw+JNuWsTX7f0ZYSyz0VY=", "narHash": "sha256-4pDvzqnegAfRkPwO3wmwBhVi/Sye1mzps0zHWYnP88c=",
"owner": "hercules-ci", "owner": "hercules-ci",
"repo": "flake-parts", "repo": "flake-parts",
"rev": "9227223f6d922fee3c7b190b2cc238a99527bbb7", "rev": "205b12d8b7cd4802fbcb8e8ef6a0f1408781a4f9",
"type": "github" "type": "github"
}, },
"original": { "original": {
@ -48,11 +48,11 @@
] ]
}, },
"locked": { "locked": {
"lastModified": 1721042469, "lastModified": 1734279981,
"narHash": "sha256-6FPUl7HVtvRHCCBQne7Ylp4p+dpP3P/OYuzjztZ4s70=", "narHash": "sha256-NdaCraHPp8iYMWzdXAt5Nv6sA3MUzlCiGiR586TCwo0=",
"owner": "cachix", "owner": "cachix",
"repo": "git-hooks.nix", "repo": "git-hooks.nix",
"rev": "f451c19376071a90d8c58ab1a953c6e9840527fd", "rev": "aa9f40c906904ebd83da78e7f328cd8aeaeae785",
"type": "github" "type": "github"
}, },
"original": { "original": {
@ -61,35 +61,18 @@
"type": "github" "type": "github"
} }
}, },
"libgit2": {
"flake": false,
"locked": {
"lastModified": 1715853528,
"narHash": "sha256-J2rCxTecyLbbDdsyBWn9w7r3pbKRMkI9E7RvRgAqBdY=",
"owner": "libgit2",
"repo": "libgit2",
"rev": "36f7e21ad757a3dacc58cf7944329da6bc1d6e96",
"type": "github"
},
"original": {
"owner": "libgit2",
"ref": "v1.8.1",
"repo": "libgit2",
"type": "github"
}
},
"nixpkgs": { "nixpkgs": {
"locked": { "locked": {
"lastModified": 1723688146, "lastModified": 1734359947,
"narHash": "sha256-sqLwJcHYeWLOeP/XoLwAtYjr01TISlkOfz+NG82pbdg=", "narHash": "sha256-1Noao/H+N8nFB4Beoy8fgwrcOQLVm9o4zKW1ODaqK9E=",
"owner": "NixOS", "owner": "NixOS",
"repo": "nixpkgs", "repo": "nixpkgs",
"rev": "c3d4ac725177c030b1e289015989da2ad9d56af0", "rev": "48d12d5e70ee91fe8481378e540433a7303dbf6a",
"type": "github" "type": "github"
}, },
"original": { "original": {
"owner": "NixOS", "owner": "NixOS",
"ref": "nixos-24.05", "ref": "release-24.11",
"repo": "nixpkgs", "repo": "nixpkgs",
"type": "github" "type": "github"
} }
@ -131,7 +114,6 @@
"flake-compat": "flake-compat", "flake-compat": "flake-compat",
"flake-parts": "flake-parts", "flake-parts": "flake-parts",
"git-hooks-nix": "git-hooks-nix", "git-hooks-nix": "git-hooks-nix",
"libgit2": "libgit2",
"nixpkgs": "nixpkgs", "nixpkgs": "nixpkgs",
"nixpkgs-23-11": "nixpkgs-23-11", "nixpkgs-23-11": "nixpkgs-23-11",
"nixpkgs-regression": "nixpkgs-regression" "nixpkgs-regression": "nixpkgs-regression"

View file

@ -1,11 +1,11 @@
{ {
description = "The purely functional package manager"; description = "The purely functional package manager";
inputs.nixpkgs.url = "github:NixOS/nixpkgs/nixos-24.05"; inputs.nixpkgs.url = "github:NixOS/nixpkgs/release-24.11";
inputs.nixpkgs-regression.url = "github:NixOS/nixpkgs/215d4d0fd80ca5163643b03a33fde804a29cc1e2"; inputs.nixpkgs-regression.url = "github:NixOS/nixpkgs/215d4d0fd80ca5163643b03a33fde804a29cc1e2";
inputs.nixpkgs-23-11.url = "github:NixOS/nixpkgs/a62e6edd6d5e1fa0329b8653c801147986f8d446"; inputs.nixpkgs-23-11.url = "github:NixOS/nixpkgs/a62e6edd6d5e1fa0329b8653c801147986f8d446";
inputs.flake-compat = { url = "github:edolstra/flake-compat"; flake = false; }; inputs.flake-compat = { url = "github:edolstra/flake-compat"; flake = false; };
inputs.libgit2 = { url = "github:libgit2/libgit2/v1.8.1"; flake = false; };
# dev tooling # dev tooling
inputs.flake-parts.url = "github:hercules-ci/flake-parts"; inputs.flake-parts.url = "github:hercules-ci/flake-parts";
@ -18,7 +18,7 @@
inputs.git-hooks-nix.inputs.flake-compat.follows = ""; inputs.git-hooks-nix.inputs.flake-compat.follows = "";
inputs.git-hooks-nix.inputs.gitignore.follows = ""; inputs.git-hooks-nix.inputs.gitignore.follows = "";
outputs = inputs@{ self, nixpkgs, nixpkgs-regression, libgit2, ... }: outputs = inputs@{ self, nixpkgs, nixpkgs-regression, ... }:
let let
@ -36,7 +36,8 @@
"armv6l-unknown-linux-gnueabihf" "armv6l-unknown-linux-gnueabihf"
"armv7l-unknown-linux-gnueabihf" "armv7l-unknown-linux-gnueabihf"
"riscv64-unknown-linux-gnu" "riscv64-unknown-linux-gnu"
"x86_64-unknown-netbsd" # Disabled because of https://github.com/NixOS/nixpkgs/issues/344423
# "x86_64-unknown-netbsd"
"x86_64-unknown-freebsd" "x86_64-unknown-freebsd"
"x86_64-w64-mingw32" "x86_64-w64-mingw32"
]; ];
@ -106,6 +107,7 @@
in { in {
inherit stdenvs native; inherit stdenvs native;
static = native.pkgsStatic; static = native.pkgsStatic;
llvm = native.pkgsLLVM;
cross = forAllCrossSystems (crossSystem: make-pkgs crossSystem "stdenv"); cross = forAllCrossSystems (crossSystem: make-pkgs crossSystem "stdenv");
}); });
@ -124,18 +126,36 @@
# without "polluting" the top level "`pkgs`" attrset. # without "polluting" the top level "`pkgs`" attrset.
# This also has the benefit of providing us with a distinct set of packages # This also has the benefit of providing us with a distinct set of packages
# we can iterate over. # we can iterate over.
nixComponents = lib.makeScope final.nixDependencies.newScope (import ./packaging/components.nix { nixComponents =
lib.makeScopeWithSplicing'
{
inherit (final) splicePackages;
inherit (final.nixDependencies) newScope;
}
{
otherSplices = final.generateSplicesForMkScope "nixComponents";
f = import ./packaging/components.nix {
inherit (final) lib; inherit (final) lib;
inherit officialRelease; inherit officialRelease;
src = self; src = self;
}); };
};
# The dependencies are in their own scope, so that they don't have to be # The dependencies are in their own scope, so that they don't have to be
# in Nixpkgs top level `pkgs` or `nixComponents`. # in Nixpkgs top level `pkgs` or `nixComponents`.
nixDependencies = lib.makeScope final.newScope (import ./packaging/dependencies.nix { nixDependencies =
lib.makeScopeWithSplicing'
{
inherit (final) splicePackages;
inherit (final) newScope; # layered directly on pkgs, unlike nixComponents above
}
{
otherSplices = final.generateSplicesForMkScope "nixDependencies";
f = import ./packaging/dependencies.nix {
inherit inputs stdenv; inherit inputs stdenv;
pkgs = final; pkgs = final;
}); };
};
nix = final.nixComponents.nix-cli; nix = final.nixComponents.nix-cli;
@ -145,7 +165,6 @@
if prev.stdenv.hostPlatform.system == "i686-linux" if prev.stdenv.hostPlatform.system == "i686-linux"
then (prev.pre-commit.override (o: { dotnet-sdk = ""; })).overridePythonAttrs (o: { doCheck = false; }) then (prev.pre-commit.override (o: { dotnet-sdk = ""; })).overridePythonAttrs (o: { doCheck = false; })
else prev.pre-commit; else prev.pre-commit;
}; };
in { in {
@ -168,7 +187,7 @@
}; };
checks = forAllSystems (system: { checks = forAllSystems (system: {
binaryTarball = self.hydraJobs.binaryTarball.${system}; installerScriptForGHA = self.hydraJobs.installerScriptForGHA.${system};
installTests = self.hydraJobs.installTests.${system}; installTests = self.hydraJobs.installTests.${system};
nixpkgsLibTests = self.hydraJobs.tests.nixpkgsLibTests.${system}; nixpkgsLibTests = self.hydraJobs.tests.nixpkgsLibTests.${system};
rl-next = rl-next =
@ -183,11 +202,7 @@
# Some perl dependencies are broken on i686-linux. # Some perl dependencies are broken on i686-linux.
# Since the support is only best-effort there, disable the perl # Since the support is only best-effort there, disable the perl
# bindings # bindings
perlBindings = self.hydraJobs.perlBindings.${system};
# Temporarily disabled because GitHub Actions OOM issues. Once
# the old build system is gone and we are back to one build
# system, we should reenable this.
#perlBindings = self.hydraJobs.perlBindings.${system};
} }
# Add "passthru" tests # Add "passthru" tests
// flatMapAttrs ({ // flatMapAttrs ({
@ -219,6 +234,8 @@
inherit (nixpkgsFor.${system}.native) inherit (nixpkgsFor.${system}.native)
changelog-d; changelog-d;
default = self.packages.${system}.nix; default = self.packages.${system}.nix;
installerScriptForGHA = self.hydraJobs.installerScriptForGHA.${system};
binaryTarball = self.hydraJobs.binaryTarball.${system};
# TODO probably should be `nix-cli` # TODO probably should be `nix-cli`
nix = self.packages.${system}.nix-everything; nix = self.packages.${system}.nix-everything;
nix-manual = nixpkgsFor.${system}.native.nixComponents.nix-manual; nix-manual = nixpkgsFor.${system}.native.nixComponents.nix-manual;
@ -266,6 +283,7 @@
# These attributes go right into `packages.<system>`. # These attributes go right into `packages.<system>`.
"${pkgName}" = nixpkgsFor.${system}.native.nixComponents.${pkgName}; "${pkgName}" = nixpkgsFor.${system}.native.nixComponents.${pkgName};
"${pkgName}-static" = nixpkgsFor.${system}.static.nixComponents.${pkgName}; "${pkgName}-static" = nixpkgsFor.${system}.static.nixComponents.${pkgName};
"${pkgName}-llvm" = nixpkgsFor.${system}.llvm.nixComponents.${pkgName};
} }
// lib.optionalAttrs supportsCross (flatMapAttrs (lib.genAttrs crossSystems (_: { })) (crossSystem: {}: { // lib.optionalAttrs supportsCross (flatMapAttrs (lib.genAttrs crossSystems (_: { })) (crossSystem: {}: {
# These attributes go right into `packages.<system>`. # These attributes go right into `packages.<system>`.
@ -305,6 +323,9 @@
prefixAttrs "static" (forAllStdenvs (stdenvName: makeShell { prefixAttrs "static" (forAllStdenvs (stdenvName: makeShell {
pkgs = nixpkgsFor.${system}.stdenvs."${stdenvName}Packages".pkgsStatic; pkgs = nixpkgsFor.${system}.stdenvs."${stdenvName}Packages".pkgsStatic;
})) // })) //
prefixAttrs "llvm" (forAllStdenvs (stdenvName: makeShell {
pkgs = nixpkgsFor.${system}.stdenvs."${stdenvName}Packages".pkgsLLVM;
})) //
prefixAttrs "cross" (forAllCrossSystems (crossSystem: makeShell { prefixAttrs "cross" (forAllCrossSystems (crossSystem: makeShell {
pkgs = nixpkgsFor.${system}.cross.${crossSystem}; pkgs = nixpkgsFor.${system}.cross.${crossSystem};
})) }))

View file

@ -1,66 +0,0 @@
# Ensure that this bug is not present in the C++ toolchain we are using.
#
# URL for bug: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=80431
#
# The test program is from that issue, with only a slight modification
# to set an exit status instead of printing strings.
AC_DEFUN([ENSURE_NO_GCC_BUG_80431],
[
AC_MSG_CHECKING([that GCC bug 80431 is fixed])
AC_LANG_PUSH(C++)
AC_RUN_IFELSE(
[AC_LANG_PROGRAM(
[[
#include <cstdio>
static bool a = true;
static bool b = true;
struct Options { };
struct Option
{
Option(Options * options)
{
a = false;
}
~Option()
{
b = false;
}
};
struct MyOptions : Options { };
struct MyOptions2 : virtual MyOptions
{
Option foo{this};
};
]],
[[
{
MyOptions2 opts;
}
return (a << 1) | b;
]])],
[status_80431=0],
[status_80431=$?],
[status_80431=''])
AC_LANG_POP(C++)
AS_CASE([$status_80431],
[''],[
AC_MSG_RESULT(cannot check because cross compiling)
AC_MSG_NOTICE(assume we are bug free)
],
[0],[
AC_MSG_RESULT(yes)
],
[2],[
AC_MSG_RESULT(no)
AC_MSG_ERROR(Cannot build Nix with C++ compiler with this bug)
],
[
AC_MSG_RESULT(unexpected result $status_80431: not expected failure with bug, ignoring)
])
])

View file

@ -10,8 +10,31 @@
# https://flake.parts/options/git-hooks-nix#options # https://flake.parts/options/git-hooks-nix#options
pre-commit.settings = { pre-commit.settings = {
hooks = { hooks = {
# Conflicts are usually found by other checks, but not those in docs,
# and potentially other places.
check-merge-conflicts.enable = true;
# built-in check-merge-conflicts seems ineffective against those produced by mergify backports
check-merge-conflicts-2 = {
enable = true;
entry = "${pkgs.writeScript "check-merge-conflicts" ''
#!${pkgs.runtimeShell}
conflicts=false
for file in "$@"; do
if grep --with-filename --line-number -E '^>>>>>>> ' -- "$file"; then
conflicts=true
fi
done
if $conflicts; then
echo "ERROR: found merge/patch conflicts in files"
exit 1
fi
touch $out
''}";
};
clang-format = { clang-format = {
enable = true; enable = true;
# https://github.com/cachix/git-hooks.nix/pull/532
package = pkgs.llvmPackages_latest.clang-tools;
excludes = [ excludes = [
# We don't want to format test data # We don't want to format test data
# ''tests/(?!nixos/).*\.nix'' # ''tests/(?!nixos/).*\.nix''
@ -354,6 +377,7 @@
''^src/libutil/util\.cc$'' ''^src/libutil/util\.cc$''
''^src/libutil/util\.hh$'' ''^src/libutil/util\.hh$''
''^src/libutil/variant-wrapper\.hh$'' ''^src/libutil/variant-wrapper\.hh$''
''^src/libutil/widecharwidth/widechar_width\.h$'' # vendored source
''^src/libutil/windows/file-descriptor\.cc$'' ''^src/libutil/windows/file-descriptor\.cc$''
''^src/libutil/windows/file-path\.cc$'' ''^src/libutil/windows/file-path\.cc$''
''^src/libutil/windows/processes\.cc$'' ''^src/libutil/windows/processes\.cc$''
@ -496,7 +520,6 @@
''^scripts/create-darwin-volume\.sh$'' ''^scripts/create-darwin-volume\.sh$''
''^scripts/install-darwin-multi-user\.sh$'' ''^scripts/install-darwin-multi-user\.sh$''
''^scripts/install-multi-user\.sh$'' ''^scripts/install-multi-user\.sh$''
''^scripts/install-nix-from-closure\.sh$''
''^scripts/install-systemd-multi-user\.sh$'' ''^scripts/install-systemd-multi-user\.sh$''
''^src/nix/get-env\.sh$'' ''^src/nix/get-env\.sh$''
''^tests/functional/ca/build-dry\.sh$'' ''^tests/functional/ca/build-dry\.sh$''

View file

@ -34,6 +34,7 @@ endif
subproject('libutil-c') subproject('libutil-c')
subproject('libstore-c') subproject('libstore-c')
subproject('libexpr-c') subproject('libexpr-c')
subproject('libflake-c')
subproject('libmain-c') subproject('libmain-c')
# Language Bindings # Language Bindings

13
misc/launchd/meson.build Normal file
View file

@ -0,0 +1,13 @@
configure_file(
input : 'org.nixos.nix-daemon.plist.in',
output : 'org.nixos.nix-daemon.plist',
install : true,
install_dir : get_option('prefix') / 'Library/LaunchDaemons',
install_mode : 'rw-r--r--',
configuration : {
# TODO: unhardcode paths with something like:
# 'storedir' : store_dir,
# 'localstatedir' : localstatedir,
# 'bindir' : bindir,
},
)

View file

@ -2,4 +2,10 @@ subdir('bash')
subdir('fish') subdir('fish')
subdir('zsh') subdir('zsh')
subdir('systemd') if host_machine.system() == 'linux'
subdir('systemd')
endif
if host_machine.system() == 'darwin'
subdir('launchd')
endif

View file

@ -1,3 +1,10 @@
# This is only conditional to work around
# https://github.com/mesonbuild/meson/issues/13293. It should be
# unconditional.
if not (host_machine.system() == 'windows' and cxx.get_id() == 'gcc')
deps_private += dependency('threads')
endif
add_project_arguments( add_project_arguments(
'-Wdeprecated-copy', '-Wdeprecated-copy',
'-Werror=suggest-override', '-Werror=suggest-override',

View file

@ -44,6 +44,7 @@ in
nix-expr-tests = callPackage ../src/libexpr-tests/package.nix { }; nix-expr-tests = callPackage ../src/libexpr-tests/package.nix { };
nix-flake = callPackage ../src/libflake/package.nix { }; nix-flake = callPackage ../src/libflake/package.nix { };
nix-flake-c = callPackage ../src/libflake-c/package.nix { };
nix-flake-tests = callPackage ../src/libflake-tests/package.nix { }; nix-flake-tests = callPackage ../src/libflake-tests/package.nix { };
nix-main = callPackage ../src/libmain/package.nix { }; nix-main = callPackage ../src/libmain/package.nix { };

View file

@ -66,10 +66,28 @@ let
mesonLayer = finalAttrs: prevAttrs: mesonLayer = finalAttrs: prevAttrs:
{ {
# NOTE:
# As of https://github.com/NixOS/nixpkgs/blob/8baf8241cea0c7b30e0b8ae73474cb3de83c1a30/pkgs/by-name/me/meson/setup-hook.sh#L26,
# `mesonBuildType` defaults to `plain` if not specified. We want our Nix-built binaries to be optimized by default.
# More on build types here: https://mesonbuild.com/Builtin-options.html#details-for-buildtype.
mesonBuildType = "release";
# NOTE:
# Users who are debugging Nix builds are expected to set the environment variable `mesonBuildType`, per the
# guidance in https://github.com/NixOS/nix/blob/8a3fc27f1b63a08ac983ee46435a56cf49ebaf4a/doc/manual/source/development/debugging.md?plain=1#L10.
# For this reason, we don't want to refer to `finalAttrs.mesonBuildType` here, but rather use the environment variable.
preConfigure = prevAttrs.preConfigure or "" + ''
case "$mesonBuildType" in
release|minsize) appendToVar mesonFlags "-Db_lto=true" ;;
*) appendToVar mesonFlags "-Db_lto=false" ;;
esac
'';
nativeBuildInputs = [ nativeBuildInputs = [
pkgs.buildPackages.meson pkgs.buildPackages.meson
pkgs.buildPackages.ninja pkgs.buildPackages.ninja
] ++ prevAttrs.nativeBuildInputs or []; ] ++ prevAttrs.nativeBuildInputs or [];
mesonCheckFlags = prevAttrs.mesonCheckFlags or [] ++ [
"--print-errorlogs"
];
}; };
mesonBuildLayer = finalAttrs: prevAttrs: mesonBuildLayer = finalAttrs: prevAttrs:
@ -79,6 +97,12 @@ let
]; ];
separateDebugInfo = !stdenv.hostPlatform.isStatic; separateDebugInfo = !stdenv.hostPlatform.isStatic;
hardeningDisable = lib.optional stdenv.hostPlatform.isStatic "pie"; hardeningDisable = lib.optional stdenv.hostPlatform.isStatic "pie";
env = prevAttrs.env or {}
// lib.optionalAttrs
(stdenv.isLinux
&& !(stdenv.hostPlatform.isStatic && stdenv.system == "aarch64-linux")
&& !(stdenv.hostPlatform.useLLVM or false))
{ LDFLAGS = "-fuse-ld=gold"; };
}; };
mesonLibraryLayer = finalAttrs: prevAttrs: mesonLibraryLayer = finalAttrs: prevAttrs:
@ -111,14 +135,6 @@ scope: {
requiredSystemFeatures = [ ]; requiredSystemFeatures = [ ];
}; };
libseccomp = pkgs.libseccomp.overrideAttrs (_: rec {
version = "2.5.5";
src = pkgs.fetchurl {
url = "https://github.com/seccomp/libseccomp/releases/download/v${version}/libseccomp-${version}.tar.gz";
hash = "sha256-JIosik2bmFiqa69ScSw0r+/PnJ6Ut23OAsHJqiX7M3U=";
};
});
boehmgc = pkgs.boehmgc.override { boehmgc = pkgs.boehmgc.override {
enableLargeConfig = true; enableLargeConfig = true;
}; };
@ -137,8 +153,6 @@ scope: {
}); });
libgit2 = pkgs.libgit2.overrideAttrs (attrs: { libgit2 = pkgs.libgit2.overrideAttrs (attrs: {
src = inputs.libgit2;
version = inputs.libgit2.lastModifiedDate;
cmakeFlags = attrs.cmakeFlags or [] cmakeFlags = attrs.cmakeFlags or []
++ [ "-DUSE_SSH=exec" ]; ++ [ "-DUSE_SSH=exec" ];
nativeBuildInputs = attrs.nativeBuildInputs or [] nativeBuildInputs = attrs.nativeBuildInputs or []
@ -166,36 +180,6 @@ scope: {
]; ];
}); });
busybox-sandbox-shell = pkgs.busybox-sandbox-shell or (pkgs.busybox.override {
useMusl = true;
enableStatic = true;
enableMinimal = true;
extraConfig = ''
CONFIG_FEATURE_FANCY_ECHO y
CONFIG_FEATURE_SH_MATH y
CONFIG_FEATURE_SH_MATH_64 y
CONFIG_ASH y
CONFIG_ASH_OPTIMIZE_FOR_SIZE y
CONFIG_ASH_ALIAS y
CONFIG_ASH_BASH_COMPAT y
CONFIG_ASH_CMDCMD y
CONFIG_ASH_ECHO y
CONFIG_ASH_GETOPTS y
CONFIG_ASH_INTERNAL_GLOB y
CONFIG_ASH_JOB_CONTROL y
CONFIG_ASH_PRINTF y
CONFIG_ASH_TEST y
'';
});
# TODO change in Nixpkgs, Windows works fine. First commit of
# https://github.com/NixOS/nixpkgs/pull/322977 backported will fix.
toml11 = pkgs.toml11.overrideAttrs (old: {
meta.platforms = lib.platforms.all;
});
inherit resolvePath filesetToSource; inherit resolvePath filesetToSource;
mkMesonDerivation = mkMesonDerivation =

View file

@ -5,12 +5,10 @@
nix-util, nix-util,
nix-util-c, nix-util-c,
nix-util-test-support,
nix-util-tests, nix-util-tests,
nix-store, nix-store,
nix-store-c, nix-store-c,
nix-store-test-support,
nix-store-tests, nix-store-tests,
nix-fetchers, nix-fetchers,
@ -18,10 +16,10 @@
nix-expr, nix-expr,
nix-expr-c, nix-expr-c,
nix-expr-test-support,
nix-expr-tests, nix-expr-tests,
nix-flake, nix-flake,
nix-flake-c,
nix-flake-tests, nix-flake-tests,
nix-main, nix-main,
@ -38,45 +36,82 @@
nix-external-api-docs, nix-external-api-docs,
nix-perl-bindings, nix-perl-bindings,
testers,
runCommand,
}: }:
let
dev = stdenv.mkDerivation (finalAttrs: {
name = "nix-${nix-cli.version}-dev";
pname = "nix";
version = nix-cli.version;
dontUnpack = true;
dontBuild = true;
libs = map lib.getDev [
nix-cmd
nix-expr
nix-expr-c
nix-fetchers
nix-flake
nix-flake-c
nix-main
nix-main-c
nix-store
nix-store-c
nix-util
nix-util-c
nix-perl-bindings
];
installPhase = ''
mkdir -p $out/nix-support
echo $libs >> $out/nix-support/propagated-build-inputs
'';
passthru = {
tests = {
pkg-config =
testers.hasPkgConfigModules {
package = finalAttrs.finalPackage;
};
};
# If we were to fully emulate output selection here, we'd confuse the Nix CLIs,
# because they rely on `drvPath`.
dev = finalAttrs.finalPackage.out;
libs = throw "`nix.dev.libs` is not meant to be used; use `nix.libs` instead.";
};
meta = {
pkgConfigModules = [
"nix-cmd"
"nix-expr"
"nix-expr-c"
"nix-fetchers"
"nix-flake"
"nix-flake-c"
"nix-main"
"nix-main-c"
"nix-store"
"nix-store-c"
"nix-util"
"nix-util-c"
];
};
});
devdoc = buildEnv {
name = "nix-${nix-cli.version}-devdoc";
paths = [
nix-internal-api-docs
nix-external-api-docs
];
};
in
(buildEnv { (buildEnv {
name = "nix-${nix-cli.version}"; name = "nix-${nix-cli.version}";
paths = [ paths = [
nix-util
nix-util-c
nix-util-test-support
nix-util-tests
nix-store
nix-store-c
nix-store-test-support
nix-store-tests
nix-fetchers
nix-fetchers-tests
nix-expr
nix-expr-c
nix-expr-test-support
nix-expr-tests
nix-flake
nix-flake-tests
nix-main
nix-main-c
nix-cmd
nix-cli nix-cli
nix-manual.man
nix-manual
nix-internal-api-docs
nix-external-api-docs
] ++ lib.optionals (stdenv.buildPlatform.canExecute stdenv.hostPlatform) [
nix-perl-bindings
]; ];
meta.mainProgram = "nix"; meta.mainProgram = "nix";
@ -85,12 +120,25 @@
doInstallCheck = true; doInstallCheck = true;
checkInputs = [ checkInputs = [
# Actually run the unit tests too # Make sure the unit tests have passed
nix-util-tests.tests.run nix-util-tests.tests.run
nix-store-tests.tests.run nix-store-tests.tests.run
nix-expr-tests.tests.run nix-expr-tests.tests.run
nix-fetchers-tests.tests.run
nix-flake-tests.tests.run nix-flake-tests.tests.run
];
# dev bundle is ok
# (checkInputs must be empty paths??)
(runCommand "check-pkg-config" { checked = dev.tests.pkg-config; } "mkdir $out")
] ++
(if stdenv.buildPlatform.canExecute stdenv.hostPlatform
then [
# TODO: add perl.tests
nix-perl-bindings
]
else [
nix-perl-bindings
]);
installCheckInputs = [ installCheckInputs = [
nix-functional-tests nix-functional-tests
]; ];
@ -124,9 +172,31 @@
nix-expr nix-expr
nix-expr-c nix-expr-c
nix-flake nix-flake
nix-flake-c
nix-main nix-main
nix-main-c nix-main-c
; ;
}; };
tests = prevAttrs.passthru.tests or {} // {
# TODO: create a proper fixpoint and:
# pkg-config =
# testers.hasPkgConfigModules {
# package = finalPackage;
# };
};
/**
A derivation referencing the `dev` outputs of the Nix libraries.
*/
inherit dev;
inherit devdoc;
doc = nix-manual;
outputs = [ "out" "dev" "devdoc" "doc" ];
all = lib.attrValues (lib.genAttrs finalAttrs.passthru.outputs (outName: finalAttrs.finalPackage.${outName}));
};
meta = prevAttrs.meta // {
description = "The Nix package manager";
pkgConfigModules = dev.meta.pkgConfigModules;
}; };
}) })

View file

@ -18,12 +18,8 @@ let
testNixVersions = pkgs: daemon: testNixVersions = pkgs: daemon:
pkgs.nixComponents.nix-functional-tests.override { pkgs.nixComponents.nix-functional-tests.override {
pname = pname = "nix-daemon-compat-tests";
"nix-tests" version = "${pkgs.nix.version}-with-daemon-${daemon.version}";
+ lib.optionalString
(lib.versionAtLeast daemon.version "2.4pre20211005" &&
lib.versionAtLeast pkgs.nix.version "2.4pre20211005")
"-${pkgs.nix.version}-against-${daemon.version}";
test-daemon = daemon; test-daemon = daemon;
}; };
@ -127,15 +123,10 @@ in
self.hydraJobs.binaryTarballCross."x86_64-linux"."armv7l-unknown-linux-gnueabihf" self.hydraJobs.binaryTarballCross."x86_64-linux"."armv7l-unknown-linux-gnueabihf"
self.hydraJobs.binaryTarballCross."x86_64-linux"."riscv64-unknown-linux-gnu" self.hydraJobs.binaryTarballCross."x86_64-linux"."riscv64-unknown-linux-gnu"
]; ];
installerScriptForGHA = installScriptFor [
# Native installerScriptForGHA = forAllSystems (system: nixpkgsFor.${system}.native.callPackage ../scripts/installer.nix {
self.hydraJobs.binaryTarball."x86_64-linux" tarballs = [ self.hydraJobs.binaryTarball.${system} ];
self.hydraJobs.binaryTarball."aarch64-darwin" });
# Cross
self.hydraJobs.binaryTarballCross."x86_64-linux"."armv6l-unknown-linux-gnueabihf"
self.hydraJobs.binaryTarballCross."x86_64-linux"."armv7l-unknown-linux-gnueabihf"
self.hydraJobs.binaryTarballCross."x86_64-linux"."riscv64-unknown-linux-gnu"
];
# docker image with Nix inside # docker image with Nix inside
dockerImage = lib.genAttrs linux64BitSystems (system: self.packages.${system}.dockerImage); dockerImage = lib.genAttrs linux64BitSystems (system: self.packages.${system}.dockerImage);

View file

@ -23,7 +23,7 @@ in
runCommand "nix-binary-tarball-${version}" env '' runCommand "nix-binary-tarball-${version}" env ''
cp ${installerClosureInfo}/registration $TMPDIR/reginfo cp ${installerClosureInfo}/registration $TMPDIR/reginfo
cp ${./create-darwin-volume.sh} $TMPDIR/create-darwin-volume.sh cp ${./create-darwin-volume.sh} $TMPDIR/create-darwin-volume.sh
substitute ${./install-nix-from-closure.sh} $TMPDIR/install \ substitute ${./install-nix-from-tarball.sh} $TMPDIR/install \
--subst-var-by nix ${nix} \ --subst-var-by nix ${nix} \
--subst-var-by cacert ${cacert} --subst-var-by cacert ${cacert}
@ -65,7 +65,7 @@ runCommand "nix-binary-tarball-${version}" env ''
fn=$out/$dir.tar.xz fn=$out/$dir.tar.xz
mkdir -p $out/nix-support mkdir -p $out/nix-support
echo "file binary-dist $fn" >> $out/nix-support/hydra-build-products echo "file binary-dist $fn" >> $out/nix-support/hydra-build-products
tar cvfJ $fn \ tar cfJ $fn \
--owner=0 --group=0 --mode=u+rw,uga+r \ --owner=0 --group=0 --mode=u+rw,uga+r \
--mtime='1970-01-01' \ --mtime='1970-01-01' \
--absolute-names \ --absolute-names \

6
scripts/build-checks Executable file
View file

@ -0,0 +1,6 @@
#!/usr/bin/env bash
set -euo pipefail
system=$(nix eval --raw --impure --expr builtins.currentSystem)
nix eval --json ".#checks.$system" --apply builtins.attrNames | \
jq -r '.[]' | \
xargs -P0 -I '{}' sh -c "nix build -L .#checks.$system.{} || { echo 'FAILED: \033[0;31mnix build -L .#checks.$system.{}\\033[0m'; kill 0; }"

View file

@ -463,7 +463,7 @@ EOF
EDITOR="$SCRATCH/ex_cleanroom_wrapper" _sudo "to add nix to fstab" "$@" <<EOF EDITOR="$SCRATCH/ex_cleanroom_wrapper" _sudo "to add nix to fstab" "$@" <<EOF
:a :a
UUID=$uuid $escaped_mountpoint apfs rw,noauto,nobrowse,suid,owners UUID=$uuid $escaped_mountpoint apfs rw,noauto,nobrowse,nosuid,noatime,owners
. .
:x :x
EOF EOF

View file

@ -145,13 +145,28 @@ poly_user_id_get() {
dsclattr "/Users/$1" "UniqueID" dsclattr "/Users/$1" "UniqueID"
} }
dscl_create() {
# workaround a bug in dscl where it sometimes fails with eNotYetImplemented:
# https://github.com/NixOS/nix/issues/12140
while ! _sudo "$1" /usr/bin/dscl . -create "$2" "$3" "$4" 2> "$SCRATCH/dscl.err"; do
local err=$?
if [[ $err -eq 140 ]] && grep -q "-14988 (eNotYetImplemented)" "$SCRATCH/dscl.err"; then
echo "dscl failed with eNotYetImplemented, retrying..."
sleep 1
continue
fi
cat "$SCRATCH/dscl.err"
return $err
done
}
poly_user_hidden_get() { poly_user_hidden_get() {
dsclattr "/Users/$1" "IsHidden" dsclattr "/Users/$1" "IsHidden"
} }
poly_user_hidden_set() { poly_user_hidden_set() {
_sudo "in order to make $1 a hidden user" \ dscl_create "in order to make $1 a hidden user" \
/usr/bin/dscl . -create "/Users/$1" "IsHidden" "1" "/Users/$1" "IsHidden" "1"
} }
poly_user_home_get() { poly_user_home_get() {
@ -161,8 +176,8 @@ poly_user_home_get() {
poly_user_home_set() { poly_user_home_set() {
# This can trigger a permission prompt now: # This can trigger a permission prompt now:
# "Terminal" would like to administer your computer. Administration can include modifying passwords, networking, and system settings. # "Terminal" would like to administer your computer. Administration can include modifying passwords, networking, and system settings.
_sudo "in order to give $1 a safe home directory" \ dscl_create "in order to give $1 a safe home directory" \
/usr/bin/dscl . -create "/Users/$1" "NFSHomeDirectory" "$2" "/Users/$1" "NFSHomeDirectory" "$2"
} }
poly_user_note_get() { poly_user_note_get() {
@ -170,8 +185,8 @@ poly_user_note_get() {
} }
poly_user_note_set() { poly_user_note_set() {
_sudo "in order to give $username a useful note" \ dscl_create "in order to give $1 a useful note" \
/usr/bin/dscl . -create "/Users/$1" "RealName" "$2" "/Users/$1" "RealName" "$2"
} }
poly_user_shell_get() { poly_user_shell_get() {
@ -179,8 +194,8 @@ poly_user_shell_get() {
} }
poly_user_shell_set() { poly_user_shell_set() {
_sudo "in order to give $1 a safe shell" \ dscl_create "in order to give $1 a safe shell" \
/usr/bin/dscl . -create "/Users/$1" "UserShell" "$2" "/Users/$1" "UserShell" "$2"
} }
poly_user_in_group_check() { poly_user_in_group_check() {

View file

@ -56,6 +56,9 @@ readonly NIX_INSTALLED_CACERT="@cacert@"
#readonly NIX_INSTALLED_CACERT="/nix/store/7dxhzymvy330i28ii676fl1pqwcahv2f-nss-cacert-3.49.2" #readonly NIX_INSTALLED_CACERT="/nix/store/7dxhzymvy330i28ii676fl1pqwcahv2f-nss-cacert-3.49.2"
readonly EXTRACTED_NIX_PATH="$(dirname "$0")" readonly EXTRACTED_NIX_PATH="$(dirname "$0")"
# allow to override identity change command
readonly NIX_BECOME=${NIX_BECOME:-sudo}
readonly ROOT_HOME=~root readonly ROOT_HOME=~root
if [ -t 0 ] && [ -z "${NIX_INSTALLER_YES:-}" ]; then if [ -t 0 ] && [ -z "${NIX_INSTALLER_YES:-}" ]; then
@ -123,7 +126,7 @@ uninstall_directions() {
cat <<EOF cat <<EOF
$step. Restore $profile_target$PROFILE_BACKUP_SUFFIX back to $profile_target $step. Restore $profile_target$PROFILE_BACKUP_SUFFIX back to $profile_target
sudo mv $profile_target$PROFILE_BACKUP_SUFFIX $profile_target $NIX_BECOME mv $profile_target$PROFILE_BACKUP_SUFFIX $profile_target
(after this one, you may need to re-open any terminals that were (after this one, you may need to re-open any terminals that were
opened while it existed.) opened while it existed.)
@ -136,7 +139,7 @@ EOF
cat <<EOF cat <<EOF
$step. Delete the files Nix added to your system: $step. Delete the files Nix added to your system:
sudo rm -rf "/etc/nix" "$NIX_ROOT" "$ROOT_HOME/.nix-profile" "$ROOT_HOME/.nix-defexpr" "$ROOT_HOME/.nix-channels" "$ROOT_HOME/.local/state/nix" "$ROOT_HOME/.cache/nix" "$HOME/.nix-profile" "$HOME/.nix-defexpr" "$HOME/.nix-channels" "$HOME/.local/state/nix" "$HOME/.cache/nix" $NIX_BECOME rm -rf "/etc/nix" "$NIX_ROOT" "$ROOT_HOME/.nix-profile" "$ROOT_HOME/.nix-defexpr" "$ROOT_HOME/.nix-channels" "$ROOT_HOME/.local/state/nix" "$ROOT_HOME/.cache/nix" "$HOME/.nix-profile" "$HOME/.nix-defexpr" "$HOME/.nix-channels" "$HOME/.local/state/nix" "$HOME/.cache/nix"
and that is it. and that is it.
@ -343,7 +346,7 @@ __sudo() {
echo "I am executing:" echo "I am executing:"
echo "" echo ""
printf " $ sudo %s\\n" "$cmd" printf " $ $NIX_BECOME %s\\n" "$cmd"
echo "" echo ""
echo "$expl" echo "$expl"
echo "" echo ""
@ -361,7 +364,9 @@ _sudo() {
if is_root; then if is_root; then
env "$@" env "$@"
else else
sudo "$@" # env sets environment variables for sudo alternatives
# that don't support "VAR=value command" syntax
$NIX_BECOME env "$@"
fi fi
} }
@ -557,7 +562,7 @@ create_build_user_for_core() {
if [ "$actual_uid" != "$uid" ]; then if [ "$actual_uid" != "$uid" ]; then
failure <<EOF failure <<EOF
It seems the build user $username already exists, but with the UID It seems the build user $username already exists, but with the UID
with the UID '$actual_uid'. This script can't really handle that right '$actual_uid'. This script can't really handle that right
now, so I'm going to give up. now, so I'm going to give up.
If you already created the users and you know they start from If you already created the users and you know they start from
@ -690,7 +695,7 @@ place_channel_configuration() {
if [ -z "${NIX_INSTALLER_NO_CHANNEL_ADD:-}" ]; then if [ -z "${NIX_INSTALLER_NO_CHANNEL_ADD:-}" ]; then
echo "https://nixos.org/channels/nixpkgs-unstable nixpkgs" > "$SCRATCH/.nix-channels" echo "https://nixos.org/channels/nixpkgs-unstable nixpkgs" > "$SCRATCH/.nix-channels"
_sudo "to set up the default system channel (part 1)" \ _sudo "to set up the default system channel (part 1)" \
install -m 0664 "$SCRATCH/.nix-channels" "$ROOT_HOME/.nix-channels" install -m 0644 "$SCRATCH/.nix-channels" "$ROOT_HOME/.nix-channels"
fi fi
} }
@ -964,7 +969,7 @@ $NIX_EXTRA_CONF
build-users-group = $NIX_BUILD_GROUP_NAME build-users-group = $NIX_BUILD_GROUP_NAME
EOF EOF
_sudo "to place the default nix daemon configuration (part 2)" \ _sudo "to place the default nix daemon configuration (part 2)" \
install -m 0664 "$SCRATCH/nix.conf" /etc/nix/nix.conf install -m 0644 "$SCRATCH/nix.conf" /etc/nix/nix.conf
} }

View file

@ -9,6 +9,8 @@ self="$(dirname "$0")"
nix="@nix@" nix="@nix@"
cacert="@cacert@" cacert="@cacert@"
# allow to override identity change command
readonly NIX_BECOME="${NIX_BECOME:-sudo}"
if ! [ -e "$self/.reginfo" ]; then if ! [ -e "$self/.reginfo" ]; then
echo "$0: incomplete installer (.reginfo is missing)" >&2 echo "$0: incomplete installer (.reginfo is missing)" >&2
@ -48,15 +50,14 @@ case "$(uname -s)" in
INSTALL_MODE=no-daemon;; INSTALL_MODE=no-daemon;;
esac esac
# space-separated string ACTION=
ACTIONS=
# handle the command line flags # handle the command line flags
while [ $# -gt 0 ]; do while [ $# -gt 0 ]; do
case $1 in case $1 in
--daemon) --daemon)
INSTALL_MODE=daemon INSTALL_MODE=daemon
ACTIONS="${ACTIONS}install " ACTION=install
;; ;;
--no-daemon) --no-daemon)
if [ "$(uname -s)" = "Darwin" ]; then if [ "$(uname -s)" = "Darwin" ]; then
@ -64,19 +65,14 @@ while [ $# -gt 0 ]; do
exit 1 exit 1
fi fi
INSTALL_MODE=no-daemon INSTALL_MODE=no-daemon
# intentional tail space ACTION=install
ACTIONS="${ACTIONS}install "
;; ;;
# --uninstall)
# # intentional tail space
# ACTIONS="${ACTIONS}uninstall "
# ;;
--yes) --yes)
export NIX_INSTALLER_YES=1;; export NIX_INSTALLER_YES=1;;
--no-channel-add) --no-channel-add)
export NIX_INSTALLER_NO_CHANNEL_ADD=1;; export NIX_INSTALLER_NO_CHANNEL_ADD=1;;
--daemon-user-count) --daemon-user-count)
export NIX_USER_COUNT=$2 export NIX_USER_COUNT="$2"
shift;; shift;;
--no-modify-profile) --no-modify-profile)
NIX_INSTALLER_NO_MODIFY_PROFILE=1;; NIX_INSTALLER_NO_MODIFY_PROFILE=1;;
@ -128,7 +124,7 @@ done
if [ "$INSTALL_MODE" = "daemon" ]; then if [ "$INSTALL_MODE" = "daemon" ]; then
printf '\e[1;31mSwitching to the Multi-user Installer\e[0m\n' printf '\e[1;31mSwitching to the Multi-user Installer\e[0m\n'
exec "$self/install-multi-user" $ACTIONS # let ACTIONS split exec "$self/install-multi-user" $ACTION
exit 0 exit 0
fi fi
@ -140,8 +136,8 @@ echo "performing a single-user installation of Nix..." >&2
if ! [ -e "$dest" ]; then if ! [ -e "$dest" ]; then
cmd="mkdir -m 0755 $dest && chown $USER $dest" cmd="mkdir -m 0755 $dest && chown $USER $dest"
echo "directory $dest does not exist; creating it by running '$cmd' using sudo" >&2 echo "directory $dest does not exist; creating it by running '$cmd' using $NIX_BECOME" >&2
if ! sudo sh -c "$cmd"; then if ! $NIX_BECOME sh -c "$cmd"; then
echo "$0: please manually run '$cmd' as root to create $dest" >&2 echo "$0: please manually run '$cmd' as root to create $dest" >&2
exit 1 exit 1
fi fi

View file

@ -1,10 +1,11 @@
#!/usr/bin/env bash #!/usr/bin/env bash
set -e set -euo pipefail
script=$(nix-build -A outputs.hydraJobs.installerScriptForGHA --no-out-link) nix build -L ".#installerScriptForGHA" ".#binaryTarball"
installerHash=$(echo "$script" | cut -b12-43 -)
installerURL=https://$CACHIX_NAME.cachix.org/serve/$installerHash/install mkdir -p out
cp ./result/install "out/install"
echo "::set-output name=installerURL::$installerURL" name="$(basename "$(realpath ./result-1)")"
# everything before the first dash
cp -r ./result-1 "out/${name%%-*}"

View file

@ -2,6 +2,9 @@
set -eo pipefail set -eo pipefail
# stock path to avoid unexpected command versions
PATH="$(/usr/bin/getconf PATH)"
((NEW_NIX_FIRST_BUILD_UID=351)) ((NEW_NIX_FIRST_BUILD_UID=351))
((TEMP_NIX_FIRST_BUILD_UID=31000)) ((TEMP_NIX_FIRST_BUILD_UID=31000))

View file

@ -0,0 +1,22 @@
#!/usr/bin/env bash
set -euo pipefail
if [[ ! -d out ]]; then
echo "run prepare-installer-for-github-actions first"
exit 1
fi
cd out
PORT=${PORT:-8126}
nohup python -m http.server "$PORT" >/dev/null 2>&1 &
pid=$!
while ! curl -s "http://localhost:$PORT"; do
sleep 1
if ! kill -0 $pid; then
echo "Failed to start http server"
exit 1
fi
done
echo 'To install nix, run the following command:'
echo "sh <(curl http://localhost:$PORT/install) --tarball-url-prefix http://localhost:$PORT"

View file

@ -40,6 +40,7 @@ GENERATE_LATEX = NO
INPUT = \ INPUT = \
@src@/src/libutil-c \ @src@/src/libutil-c \
@src@/src/libexpr-c \ @src@/src/libexpr-c \
@src@/src/libflake-c \
@src@/src/libstore-c \ @src@/src/libstore-c \
@src@/src/external-api-docs/README.md @src@/src/external-api-docs/README.md

View file

@ -30,6 +30,7 @@ mkMesonDerivation (finalAttrs: {
# Source is not compiled, but still must be available for Doxygen # Source is not compiled, but still must be available for Doxygen
# to gather comments. # to gather comments.
(cpp ../libexpr-c) (cpp ../libexpr-c)
(cpp ../libflake-c)
(cpp ../libstore-c) (cpp ../libstore-c)
(cpp ../libutil-c) (cpp ../libutil-c)
]; ];

View file

@ -1 +0,0 @@
../../build-utils-meson

View file

@ -179,30 +179,34 @@ BuiltPathsCommand::BuiltPathsCommand(bool recursive)
void BuiltPathsCommand::run(ref<Store> store, Installables && installables) void BuiltPathsCommand::run(ref<Store> store, Installables && installables)
{ {
BuiltPaths paths; BuiltPaths rootPaths, allPaths;
if (all) { if (all) {
if (installables.size()) if (installables.size())
throw UsageError("'--all' does not expect arguments"); throw UsageError("'--all' does not expect arguments");
// XXX: Only uses opaque paths, ignores all the realisations // XXX: Only uses opaque paths, ignores all the realisations
for (auto & p : store->queryAllValidPaths()) for (auto & p : store->queryAllValidPaths())
paths.emplace_back(BuiltPath::Opaque{p}); rootPaths.emplace_back(BuiltPath::Opaque{p});
allPaths = rootPaths;
} else { } else {
paths = Installable::toBuiltPaths(getEvalStore(), store, realiseMode, operateOn, installables); rootPaths = Installable::toBuiltPaths(getEvalStore(), store, realiseMode, operateOn, installables);
allPaths = rootPaths;
if (recursive) { if (recursive) {
// XXX: This only computes the store path closure, ignoring // XXX: This only computes the store path closure, ignoring
// intermediate realisations // intermediate realisations
StorePathSet pathsRoots, pathsClosure; StorePathSet pathsRoots, pathsClosure;
for (auto & root : paths) { for (auto & root : rootPaths) {
auto rootFromThis = root.outPaths(); auto rootFromThis = root.outPaths();
pathsRoots.insert(rootFromThis.begin(), rootFromThis.end()); pathsRoots.insert(rootFromThis.begin(), rootFromThis.end());
} }
store->computeFSClosure(pathsRoots, pathsClosure); store->computeFSClosure(pathsRoots, pathsClosure);
for (auto & path : pathsClosure) for (auto & path : pathsClosure)
paths.emplace_back(BuiltPath::Opaque{path}); allPaths.emplace_back(BuiltPath::Opaque{path});
} }
} }
run(store, std::move(paths)); run(store, std::move(allPaths), std::move(rootPaths));
} }
StorePathsCommand::StorePathsCommand(bool recursive) StorePathsCommand::StorePathsCommand(bool recursive)
@ -210,10 +214,10 @@ StorePathsCommand::StorePathsCommand(bool recursive)
{ {
} }
void StorePathsCommand::run(ref<Store> store, BuiltPaths && paths) void StorePathsCommand::run(ref<Store> store, BuiltPaths && allPaths, BuiltPaths && rootPaths)
{ {
StorePathSet storePaths; StorePathSet storePaths;
for (auto & builtPath : paths) for (auto & builtPath : allPaths)
for (auto & p : builtPath.outPaths()) for (auto & p : builtPath.outPaths())
storePaths.insert(p); storePaths.insert(p);
@ -245,7 +249,7 @@ void MixProfile::updateProfile(const StorePath & storePath)
{ {
if (!profile) if (!profile)
return; return;
auto store = getStore().dynamic_pointer_cast<LocalFSStore>(); auto store = getDstStore().dynamic_pointer_cast<LocalFSStore>();
if (!store) if (!store)
throw Error("'--profile' is not supported for this Nix store"); throw Error("'--profile' is not supported for this Nix store");
auto profile2 = absPath(*profile); auto profile2 = absPath(*profile);
@ -365,4 +369,31 @@ void MixEnvironment::setEnviron()
return; return;
} }
void createOutLinks(const std::filesystem::path & outLink, const BuiltPaths & buildables, LocalFSStore & store)
{
for (const auto & [_i, buildable] : enumerate(buildables)) {
auto i = _i;
std::visit(
overloaded{
[&](const BuiltPath::Opaque & bo) {
auto symlink = outLink;
if (i)
symlink += fmt("-%d", i);
store.addPermRoot(bo.path, absPath(symlink.string()));
},
[&](const BuiltPath::Built & bfd) {
for (auto & output : bfd.outputs) {
auto symlink = outLink;
if (i)
symlink += fmt("-%d", i);
if (output.first != "out")
symlink += fmt("-%s", output.first);
store.addPermRoot(output.second, absPath(symlink.string()));
}
},
},
buildable.raw());
}
}
} }

View file

@ -18,6 +18,7 @@ extern char ** savedArgv;
class EvalState; class EvalState;
struct Pos; struct Pos;
class Store; class Store;
class LocalFSStore;
static constexpr Command::Category catHelp = -1; static constexpr Command::Category catHelp = -1;
static constexpr Command::Category catSecondary = 100; static constexpr Command::Category catSecondary = 100;
@ -46,7 +47,20 @@ struct StoreCommand : virtual Command
{ {
StoreCommand(); StoreCommand();
void run() override; void run() override;
/**
* Return the default Nix store.
*/
ref<Store> getStore(); ref<Store> getStore();
/**
* Return the destination Nix store.
*/
virtual ref<Store> getDstStore()
{
return getStore();
}
virtual ref<Store> createStore(); virtual ref<Store> createStore();
/** /**
* Main entry point, with a `Store` provided * Main entry point, with a `Store` provided
@ -69,7 +83,7 @@ struct CopyCommand : virtual StoreCommand
ref<Store> createStore() override; ref<Store> createStore() override;
ref<Store> getDstStore(); ref<Store> getDstStore() override;
}; };
/** /**
@ -239,7 +253,7 @@ public:
BuiltPathsCommand(bool recursive = false); BuiltPathsCommand(bool recursive = false);
virtual void run(ref<Store> store, BuiltPaths && paths) = 0; virtual void run(ref<Store> store, BuiltPaths && allPaths, BuiltPaths && rootPaths) = 0;
void run(ref<Store> store, Installables && installables) override; void run(ref<Store> store, Installables && installables) override;
@ -252,7 +266,7 @@ struct StorePathsCommand : public BuiltPathsCommand
virtual void run(ref<Store> store, StorePaths && storePaths) = 0; virtual void run(ref<Store> store, StorePaths && storePaths) = 0;
void run(ref<Store> store, BuiltPaths && paths) override; void run(ref<Store> store, BuiltPaths && allPaths, BuiltPaths && rootPaths) override;
}; };
/** /**
@ -354,4 +368,10 @@ std::string showVersions(const std::set<std::string> & versions);
void printClosureDiff( void printClosureDiff(
ref<Store> store, const StorePath & beforePath, const StorePath & afterPath, std::string_view indent); ref<Store> store, const StorePath & beforePath, const StorePath & afterPath, std::string_view indent);
/**
* Create symlinks prefixed by `outLink` to the store paths in
* `buildables`.
*/
void createOutLinks(const std::filesystem::path & outLink, const BuiltPaths & buildables, LocalFSStore & store);
} }

View file

@ -29,13 +29,13 @@ EvalSettings evalSettings {
{ {
{ {
"flake", "flake",
[](ref<Store> store, std::string_view rest) { [](EvalState & state, std::string_view rest) {
experimentalFeatureSettings.require(Xp::Flakes); experimentalFeatureSettings.require(Xp::Flakes);
// FIXME `parseFlakeRef` should take a `std::string_view`. // FIXME `parseFlakeRef` should take a `std::string_view`.
auto flakeRef = parseFlakeRef(fetchSettings, std::string { rest }, {}, true, false); auto flakeRef = parseFlakeRef(fetchSettings, std::string { rest }, {}, true, false);
debug("fetching flake search path element '%s''", rest); debug("fetching flake search path element '%s''", rest);
auto storePath = flakeRef.resolve(store).fetchTree(store).first; auto storePath = flakeRef.resolve(state.store).fetchTree(state.store).first;
return store->toRealPath(storePath); return state.rootPath(state.store->toRealPath(storePath));
}, },
}, },
}, },

View file

@ -450,7 +450,7 @@ ref<eval_cache::EvalCache> openEvalCache(
std::shared_ptr<flake::LockedFlake> lockedFlake) std::shared_ptr<flake::LockedFlake> lockedFlake)
{ {
auto fingerprint = evalSettings.useEvalCache && evalSettings.pureEval auto fingerprint = evalSettings.useEvalCache && evalSettings.pureEval
? lockedFlake->getFingerprint(state.store) ? lockedFlake->getFingerprint(state.store, state.fetchSettings)
: std::nullopt; : std::nullopt;
auto rootLoader = [&state, lockedFlake]() auto rootLoader = [&state, lockedFlake]()
{ {
@ -858,7 +858,7 @@ std::vector<FlakeRef> RawInstallablesCommand::getFlakeRefsForCompletion()
applyDefaultInstallables(rawInstallables); applyDefaultInstallables(rawInstallables);
std::vector<FlakeRef> res; std::vector<FlakeRef> res;
res.reserve(rawInstallables.size()); res.reserve(rawInstallables.size());
for (auto i : rawInstallables) for (const auto & i : rawInstallables)
res.push_back(parseFlakeRefWithFragment( res.push_back(parseFlakeRefWithFragment(
fetchSettings, fetchSettings,
expandTilde(i), expandTilde(i),
@ -918,4 +918,12 @@ void BuiltPathsCommand::applyDefaultInstallables(std::vector<std::string> & rawI
rawInstallables.push_back("."); rawInstallables.push_back(".");
} }
BuiltPaths toBuiltPaths(const std::vector<BuiltPathWithResult> & builtPathsWithResult)
{
BuiltPaths res;
for (auto & i : builtPathsWithResult)
res.push_back(i.path);
return res;
}
} }

View file

@ -86,6 +86,8 @@ struct BuiltPathWithResult
std::optional<BuildResult> result; std::optional<BuildResult> result;
}; };
BuiltPaths toBuiltPaths(const std::vector<BuiltPathWithResult> & builtPathsWithResult);
/** /**
* Shorthand, for less typing and helping us keep the choice of * Shorthand, for less typing and helping us keep the choice of
* collection in sync. * collection in sync.

View file

@ -16,13 +16,25 @@ static std::string doRenderMarkdownToTerminal(std::string_view markdown)
{ {
int windowWidth = getWindowSize().second; int windowWidth = getWindowSize().second;
struct lowdown_opts opts #if HAVE_LOWDOWN_1_4
{ struct lowdown_opts_term opts_term {
.type = LOWDOWN_TERM,
.maxdepth = 20,
.cols = (size_t) std::max(windowWidth - 5, 60), .cols = (size_t) std::max(windowWidth - 5, 60),
.hmargin = 0, .hmargin = 0,
.vmargin = 0, .vmargin = 0,
};
#endif
struct lowdown_opts opts
{
.type = LOWDOWN_TERM,
#if HAVE_LOWDOWN_1_4
.term = opts_term,
#endif
.maxdepth = 20,
#if !HAVE_LOWDOWN_1_4
.cols = (size_t) std::max(windowWidth - 5, 60),
.hmargin = 0,
.vmargin = 0,
#endif
.feat = LOWDOWN_COMMONMARK | LOWDOWN_FENCED | LOWDOWN_DEFLIST | LOWDOWN_TABLES, .feat = LOWDOWN_COMMONMARK | LOWDOWN_FENCED | LOWDOWN_DEFLIST | LOWDOWN_TABLES,
.oflags = LOWDOWN_TERM_NOLINK, .oflags = LOWDOWN_TERM_NOLINK,
}; };

View file

@ -4,8 +4,6 @@ project('nix-cmd', 'cpp',
'cpp_std=c++2a', 'cpp_std=c++2a',
# TODO(Qyriad): increase the warning level # TODO(Qyriad): increase the warning level
'warning_level=1', 'warning_level=1',
'debug=true',
'optimization=2',
'errorlogs=true', # Please print logs for tests that fail 'errorlogs=true', # Please print logs for tests that fail
], ],
meson_version : '>= 1.1', meson_version : '>= 1.1',
@ -14,7 +12,7 @@ project('nix-cmd', 'cpp',
cxx = meson.get_compiler('cpp') cxx = meson.get_compiler('cpp')
subdir('build-utils-meson/deps-lists') subdir('nix-meson-build-support/deps-lists')
configdata = configuration_data() configdata = configuration_data()
@ -28,9 +26,7 @@ deps_public_maybe_subproject = [
dependency('nix-flake'), dependency('nix-flake'),
dependency('nix-main'), dependency('nix-main'),
] ]
subdir('build-utils-meson/subprojects') subdir('nix-meson-build-support/subprojects')
subdir('build-utils-meson/threads')
nlohmann_json = dependency('nlohmann_json', version : '>= 3.9') nlohmann_json = dependency('nlohmann_json', version : '>= 3.9')
deps_public += nlohmann_json deps_public += nlohmann_json
@ -38,6 +34,8 @@ deps_public += nlohmann_json
lowdown = dependency('lowdown', version : '>= 0.9.0', required : get_option('markdown')) lowdown = dependency('lowdown', version : '>= 0.9.0', required : get_option('markdown'))
deps_private += lowdown deps_private += lowdown
configdata.set('HAVE_LOWDOWN', lowdown.found().to_int()) configdata.set('HAVE_LOWDOWN', lowdown.found().to_int())
# The API changed slightly around terminal initialization.
configdata.set('HAVE_LOWDOWN_1_4', lowdown.version().version_compare('>= 1.4.0').to_int())
readline_flavor = get_option('readline-flavor') readline_flavor = get_option('readline-flavor')
if readline_flavor == 'editline' if readline_flavor == 'editline'
@ -72,7 +70,7 @@ add_project_arguments(
language : 'cpp', language : 'cpp',
) )
subdir('build-utils-meson/diagnostics') subdir('nix-meson-build-support/common')
sources = files( sources = files(
'built-path.cc', 'built-path.cc',
@ -127,4 +125,4 @@ install_headers(headers, subdir : 'nix', preserve_path : true)
libraries_private = [] libraries_private = []
subdir('build-utils-meson/export') subdir('nix-meson-build-support/export')

View file

@ -0,0 +1 @@
../../nix-meson-build-support

View file

@ -39,8 +39,8 @@ mkMesonLibrary (finalAttrs: {
workDir = ./.; workDir = ./.;
fileset = fileset.unions [ fileset = fileset.unions [
../../build-utils-meson ../../nix-meson-build-support
./build-utils-meson ./nix-meson-build-support
../../.version ../../.version
./.version ./.version
./meson.build ./meson.build
@ -76,10 +76,6 @@ mkMesonLibrary (finalAttrs: {
(lib.mesonOption "readline-flavor" readlineFlavor) (lib.mesonOption "readline-flavor" readlineFlavor)
]; ];
env = lib.optionalAttrs (stdenv.isLinux && !(stdenv.hostPlatform.isStatic && stdenv.system == "aarch64-linux")) {
LDFLAGS = "-fuse-ld=gold";
};
meta = { meta = {
platforms = lib.platforms.unix ++ lib.platforms.windows; platforms = lib.platforms.unix ++ lib.platforms.windows;
}; };

View file

@ -1 +0,0 @@
../../build-utils-meson

View file

@ -4,8 +4,6 @@ project('nix-expr-c', 'cpp',
'cpp_std=c++2a', 'cpp_std=c++2a',
# TODO(Qyriad): increase the warning level # TODO(Qyriad): increase the warning level
'warning_level=1', 'warning_level=1',
'debug=true',
'optimization=2',
'errorlogs=true', # Please print logs for tests that fail 'errorlogs=true', # Please print logs for tests that fail
], ],
meson_version : '>= 1.1', meson_version : '>= 1.1',
@ -14,7 +12,7 @@ project('nix-expr-c', 'cpp',
cxx = meson.get_compiler('cpp') cxx = meson.get_compiler('cpp')
subdir('build-utils-meson/deps-lists') subdir('nix-meson-build-support/deps-lists')
configdata = configuration_data() configdata = configuration_data()
@ -27,9 +25,7 @@ deps_public_maybe_subproject = [
dependency('nix-util-c'), dependency('nix-util-c'),
dependency('nix-store-c'), dependency('nix-store-c'),
] ]
subdir('build-utils-meson/subprojects') subdir('nix-meson-build-support/subprojects')
subdir('build-utils-meson/threads')
# TODO rename, because it will conflict with downstream projects # TODO rename, because it will conflict with downstream projects
configdata.set_quoted('PACKAGE_VERSION', meson.project_version()) configdata.set_quoted('PACKAGE_VERSION', meson.project_version())
@ -55,7 +51,7 @@ add_project_arguments(
language : 'cpp', language : 'cpp',
) )
subdir('build-utils-meson/diagnostics') subdir('nix-meson-build-support/common')
sources = files( sources = files(
'nix_api_expr.cc', 'nix_api_expr.cc',
@ -74,8 +70,8 @@ headers = [config_h] + files(
# TODO move this header to libexpr, maybe don't use it in tests? # TODO move this header to libexpr, maybe don't use it in tests?
headers += files('nix_api_expr_internal.h') headers += files('nix_api_expr_internal.h')
subdir('build-utils-meson/export-all-symbols') subdir('nix-meson-build-support/export-all-symbols')
subdir('build-utils-meson/windows-version') subdir('nix-meson-build-support/windows-version')
this_library = library( this_library = library(
'nixexprc', 'nixexprc',
@ -91,4 +87,4 @@ install_headers(headers, subdir : 'nix', preserve_path : true)
libraries_private = [] libraries_private = []
subdir('build-utils-meson/export') subdir('nix-meson-build-support/export')

View file

@ -0,0 +1 @@
../../nix-meson-build-support

View file

@ -6,6 +6,7 @@
#include "eval-gc.hh" #include "eval-gc.hh"
#include "globals.hh" #include "globals.hh"
#include "eval-settings.hh" #include "eval-settings.hh"
#include "ref.hh"
#include "nix_api_expr.h" #include "nix_api_expr.h"
#include "nix_api_expr_internal.h" #include "nix_api_expr_internal.h"
@ -18,6 +19,29 @@
# include <mutex> # include <mutex>
#endif #endif
/**
* @brief Allocate and initialize using self-reference
*
* This allows a brace initializer to reference the object being constructed.
*
* @warning Use with care, as the pointer points to an object that is not fully constructed yet.
*
* @tparam T Type to allocate
* @tparam F A function type for `init`, taking a T* and returning the initializer for T
* @param init Function that takes a T* and returns the initializer for T
* @return Pointer to allocated and initialized object
*/
template <typename T, typename F>
static T * unsafe_new_with_self(F && init)
{
// Allocate
void * p = ::operator new(
sizeof(T),
static_cast<std::align_val_t>(alignof(T)));
// Initialize with placement new
return new (p) T(init(static_cast<T *>(p)));
}
nix_err nix_libexpr_init(nix_c_context * context) nix_err nix_libexpr_init(nix_c_context * context)
{ {
if (context) if (context)
@ -67,7 +91,7 @@ nix_err nix_value_call_multi(nix_c_context * context, EvalState * state, nix_val
if (context) if (context)
context->last_err_code = NIX_OK; context->last_err_code = NIX_OK;
try { try {
state->state.callFunction(fn->value, nargs, (nix::Value * *)args, value->value, nix::noPos); state->state.callFunction(fn->value, {(nix::Value * *) args, nargs}, value->value, nix::noPos);
state->state.forceValue(value->value, nix::noPos); state->state.forceValue(value->value, nix::noPos);
} }
NIXC_CATCH_ERRS NIXC_CATCH_ERRS
@ -93,7 +117,42 @@ nix_err nix_value_force_deep(nix_c_context * context, EvalState * state, nix_val
NIXC_CATCH_ERRS NIXC_CATCH_ERRS
} }
EvalState * nix_state_create(nix_c_context * context, const char ** lookupPath_c, Store * store) nix_eval_state_builder * nix_eval_state_builder_new(nix_c_context * context, Store * store)
{
if (context)
context->last_err_code = NIX_OK;
try {
return unsafe_new_with_self<nix_eval_state_builder>([&](auto * self) {
return nix_eval_state_builder{
.store = nix::ref<nix::Store>(store->ptr),
.settings = nix::EvalSettings{/* &bool */ self->readOnlyMode},
.fetchSettings = nix::fetchers::Settings{},
.readOnlyMode = true,
};
});
}
NIXC_CATCH_ERRS_NULL
}
void nix_eval_state_builder_free(nix_eval_state_builder * builder)
{
delete builder;
}
nix_err nix_eval_state_builder_load(nix_c_context * context, nix_eval_state_builder * builder)
{
if (context)
context->last_err_code = NIX_OK;
try {
// TODO: load in one go?
builder->settings.readOnlyMode = nix::settings.readOnlyMode;
loadConfFile(builder->settings);
loadConfFile(builder->fetchSettings);
}
NIXC_CATCH_ERRS
}
nix_err nix_eval_state_builder_set_lookup_path(nix_c_context * context, nix_eval_state_builder * builder, const char ** lookupPath_c)
{ {
if (context) if (context)
context->last_err_code = NIX_OK; context->last_err_code = NIX_OK;
@ -102,28 +161,47 @@ EvalState * nix_state_create(nix_c_context * context, const char ** lookupPath_c
if (lookupPath_c != nullptr) if (lookupPath_c != nullptr)
for (size_t i = 0; lookupPath_c[i] != nullptr; i++) for (size_t i = 0; lookupPath_c[i] != nullptr; i++)
lookupPath.push_back(lookupPath_c[i]); lookupPath.push_back(lookupPath_c[i]);
builder->lookupPath = nix::LookupPath::parse(lookupPath);
}
NIXC_CATCH_ERRS
}
void * p = ::operator new( EvalState * nix_eval_state_build(nix_c_context * context, nix_eval_state_builder * builder)
sizeof(EvalState), {
static_cast<std::align_val_t>(alignof(EvalState))); if (context)
auto * p2 = static_cast<EvalState *>(p); context->last_err_code = NIX_OK;
new (p) EvalState { try {
.fetchSettings = nix::fetchers::Settings{}, return unsafe_new_with_self<EvalState>([&](auto * self) {
.settings = nix::EvalSettings{ return EvalState{
nix::settings.readOnlyMode, .fetchSettings = std::move(builder->fetchSettings),
}, .settings = std::move(builder->settings),
.state = nix::EvalState( .state = nix::EvalState(
nix::LookupPath::parse(lookupPath), builder->lookupPath,
store->ptr, builder->store,
p2->fetchSettings, self->fetchSettings,
p2->settings), self->settings),
}; };
loadConfFile(p2->settings); });
return p2;
} }
NIXC_CATCH_ERRS_NULL NIXC_CATCH_ERRS_NULL
} }
EvalState * nix_state_create(nix_c_context * context, const char ** lookupPath_c, Store * store)
{
auto builder = nix_eval_state_builder_new(context, store);
if (builder == nullptr)
return nullptr;
if (nix_eval_state_builder_load(context, builder) != NIX_OK)
return nullptr;
if (nix_eval_state_builder_set_lookup_path(context, builder, lookupPath_c)
!= NIX_OK)
return nullptr;
return nix_eval_state_build(context, builder);
}
void nix_state_free(EvalState * state) void nix_state_free(EvalState * state)
{ {
delete state; delete state;

View file

@ -30,6 +30,11 @@ extern "C" {
// cffi start // cffi start
// Type definitions // Type definitions
/**
* @brief Builder for EvalState
*/
typedef struct nix_eval_state_builder nix_eval_state_builder;
/** /**
* @brief Represents a state of the Nix language evaluator. * @brief Represents a state of the Nix language evaluator.
* *
@ -174,12 +179,70 @@ nix_err nix_value_force(nix_c_context * context, EvalState * state, nix_value *
nix_err nix_value_force_deep(nix_c_context * context, EvalState * state, nix_value * value); nix_err nix_value_force_deep(nix_c_context * context, EvalState * state, nix_value * value);
/** /**
* @brief Create a new Nix language evaluator state. * @brief Create a new nix_eval_state_builder
*
* The settings are initialized to their default value.
* Values can be sourced elsewhere with nix_eval_state_builder_load.
*
* @param[out] context Optional, stores error information
* @param[in] store The Nix store to use.
* @return A new nix_eval_state_builder or NULL on failure.
*/
nix_eval_state_builder * nix_eval_state_builder_new(nix_c_context * context, Store * store);
/**
* @brief Read settings from the ambient environment
*
* Settings are sourced from environment variables and configuration files,
* as documented in the Nix manual.
*
* @param[out] context Optional, stores error information
* @param[out] builder The builder to modify.
* @return NIX_OK if successful, an error code otherwise.
*/
nix_err nix_eval_state_builder_load(nix_c_context * context, nix_eval_state_builder * builder);
/**
* @brief Set the lookup path for `<...>` expressions
*
* @param[in] context Optional, stores error information
* @param[in] builder The builder to modify.
* @param[in] lookupPath Null-terminated array of strings corresponding to entries in NIX_PATH.
*/
nix_err nix_eval_state_builder_set_lookup_path(
nix_c_context * context, nix_eval_state_builder * builder, const char ** lookupPath);
/**
* @brief Create a new Nix language evaluator state
*
* Remember to nix_eval_state_builder_free after building the state.
*
* @param[out] context Optional, stores error information
* @param[in] builder The builder to use and free
* @return A new Nix state or NULL on failure.
* @see nix_eval_state_builder_new, nix_eval_state_builder_free
*/
EvalState * nix_eval_state_build(nix_c_context * context, nix_eval_state_builder * builder);
/**
* @brief Free a nix_eval_state_builder
*
* Does not fail.
*
* @param[in] builder The builder to free.
*/
void nix_eval_state_builder_free(nix_eval_state_builder * builder);
/**
* @brief Create a new Nix language evaluator state
*
* For more control, use nix_eval_state_builder
* *
* @param[out] context Optional, stores error information * @param[out] context Optional, stores error information
* @param[in] lookupPath Null-terminated array of strings corresponding to entries in NIX_PATH. * @param[in] lookupPath Null-terminated array of strings corresponding to entries in NIX_PATH.
* @param[in] store The Nix store to use. * @param[in] store The Nix store to use.
* @return A new Nix state or NULL on failure. * @return A new Nix state or NULL on failure.
* @see nix_state_builder_new
*/ */
EvalState * nix_state_create(nix_c_context * context, const char ** lookupPath, Store * store); EvalState * nix_state_create(nix_c_context * context, const char ** lookupPath, Store * store);

View file

@ -6,6 +6,17 @@
#include "eval-settings.hh" #include "eval-settings.hh"
#include "attr-set.hh" #include "attr-set.hh"
#include "nix_api_value.h" #include "nix_api_value.h"
#include "search-path.hh"
struct nix_eval_state_builder
{
nix::ref<nix::Store> store;
nix::EvalSettings settings;
nix::fetchers::Settings fetchSettings;
nix::LookupPath lookupPath;
// TODO: make an EvalSettings setting own this instead?
bool readOnlyMode;
};
struct EvalState struct EvalState
{ {

View file

@ -213,7 +213,7 @@ nix_get_string(nix_c_context * context, const nix_value * value, nix_get_string_
/** @brief Get path as string /** @brief Get path as string
* @param[out] context Optional, stores error information * @param[out] context Optional, stores error information
* @param[in] value Nix value to inspect * @param[in] value Nix value to inspect
* @return string * @return string, if the type is NIX_TYPE_PATH
* @return NULL in case of error. * @return NULL in case of error.
*/ */
const char * nix_get_path_string(nix_c_context * context, const nix_value * value); const char * nix_get_path_string(nix_c_context * context, const nix_value * value);

View file

@ -1,5 +1,4 @@
{ lib { lib
, stdenv
, mkMesonLibrary , mkMesonLibrary
, nix-store-c , nix-store-c
@ -20,8 +19,8 @@ mkMesonLibrary (finalAttrs: {
workDir = ./.; workDir = ./.;
fileset = fileset.unions [ fileset = fileset.unions [
../../build-utils-meson ../../nix-meson-build-support
./build-utils-meson ./nix-meson-build-support
../../.version ../../.version
./.version ./.version
./meson.build ./meson.build
@ -47,10 +46,6 @@ mkMesonLibrary (finalAttrs: {
mesonFlags = [ mesonFlags = [
]; ];
env = lib.optionalAttrs (stdenv.isLinux && !(stdenv.hostPlatform.isStatic && stdenv.system == "aarch64-linux")) {
LDFLAGS = "-fuse-ld=gold";
};
meta = { meta = {
platforms = lib.platforms.unix ++ lib.platforms.windows; platforms = lib.platforms.unix ++ lib.platforms.windows;
}; };

View file

@ -1 +0,0 @@
../../build-utils-meson

View file

@ -4,8 +4,6 @@ project('nix-expr-test-support', 'cpp',
'cpp_std=c++2a', 'cpp_std=c++2a',
# TODO(Qyriad): increase the warning level # TODO(Qyriad): increase the warning level
'warning_level=1', 'warning_level=1',
'debug=true',
'optimization=2',
'errorlogs=true', # Please print logs for tests that fail 'errorlogs=true', # Please print logs for tests that fail
], ],
meson_version : '>= 1.1', meson_version : '>= 1.1',
@ -14,7 +12,7 @@ project('nix-expr-test-support', 'cpp',
cxx = meson.get_compiler('cpp') cxx = meson.get_compiler('cpp')
subdir('build-utils-meson/deps-lists') subdir('nix-meson-build-support/deps-lists')
deps_private_maybe_subproject = [ deps_private_maybe_subproject = [
] ]
@ -24,10 +22,9 @@ deps_public_maybe_subproject = [
dependency('nix-store'), dependency('nix-store'),
dependency('nix-store-test-support'), dependency('nix-store-test-support'),
dependency('nix-expr'), dependency('nix-expr'),
dependency('nix-expr-c'),
] ]
subdir('build-utils-meson/subprojects') subdir('nix-meson-build-support/subprojects')
subdir('build-utils-meson/threads')
rapidcheck = dependency('rapidcheck') rapidcheck = dependency('rapidcheck')
deps_public += rapidcheck deps_public += rapidcheck
@ -41,7 +38,7 @@ add_project_arguments(
language : 'cpp', language : 'cpp',
) )
subdir('build-utils-meson/diagnostics') subdir('nix-meson-build-support/common')
sources = files( sources = files(
'tests/value/context.cc', 'tests/value/context.cc',
@ -55,8 +52,8 @@ headers = files(
'tests/value/context.hh', 'tests/value/context.hh',
) )
subdir('build-utils-meson/export-all-symbols') subdir('nix-meson-build-support/export-all-symbols')
subdir('build-utils-meson/windows-version') subdir('nix-meson-build-support/windows-version')
this_library = library( this_library = library(
'nix-expr-test-support', 'nix-expr-test-support',
@ -74,4 +71,4 @@ install_headers(headers, subdir : 'nix', preserve_path : true)
libraries_private = [] libraries_private = []
subdir('build-utils-meson/export') subdir('nix-meson-build-support/export')

View file

@ -0,0 +1 @@
../../nix-meson-build-support

View file

@ -1,9 +1,9 @@
{ lib { lib
, stdenv
, mkMesonLibrary , mkMesonLibrary
, nix-store-test-support , nix-store-test-support
, nix-expr , nix-expr
, nix-expr-c
, rapidcheck , rapidcheck
@ -22,8 +22,8 @@ mkMesonLibrary (finalAttrs: {
workDir = ./.; workDir = ./.;
fileset = fileset.unions [ fileset = fileset.unions [
../../build-utils-meson ../../nix-meson-build-support
./build-utils-meson ./nix-meson-build-support
../../.version ../../.version
./.version ./.version
./meson.build ./meson.build
@ -35,6 +35,7 @@ mkMesonLibrary (finalAttrs: {
propagatedBuildInputs = [ propagatedBuildInputs = [
nix-store-test-support nix-store-test-support
nix-expr nix-expr
nix-expr-c
rapidcheck rapidcheck
]; ];
@ -49,10 +50,6 @@ mkMesonLibrary (finalAttrs: {
mesonFlags = [ mesonFlags = [
]; ];
env = lib.optionalAttrs (stdenv.isLinux && !(stdenv.hostPlatform.isStatic && stdenv.system == "aarch64-linux")) {
LDFLAGS = "-fuse-ld=gold";
};
meta = { meta = {
platforms = lib.platforms.unix ++ lib.platforms.windows; platforms = lib.platforms.unix ++ lib.platforms.windows;
}; };

View file

@ -40,6 +40,12 @@ namespace nix {
return v; return v;
} }
Value * maybeThunk(std::string input, bool forceValue = true) {
Expr * e = state.parseExprFromString(input, state.rootPath(CanonPath::root));
assert(e);
return e->maybeThunk(state, state.baseEnv);
}
Symbol createSymbol(const char * value) { Symbol createSymbol(const char * value) {
return state.symbols.create(value); return state.symbols.create(value);
} }

View file

@ -1 +0,0 @@
../../build-utils-meson

View file

@ -691,15 +691,15 @@ namespace nix {
ASSERT_TRACE2("elemAt \"foo\" (-1)", ASSERT_TRACE2("elemAt \"foo\" (-1)",
TypeError, TypeError,
HintFmt("expected a list but found %s: %s", "a string", Uncolored(ANSI_MAGENTA "\"foo\"" ANSI_NORMAL)), HintFmt("expected a list but found %s: %s", "a string", Uncolored(ANSI_MAGENTA "\"foo\"" ANSI_NORMAL)),
HintFmt("while evaluating the first argument passed to builtins.elemAt")); HintFmt("while evaluating the first argument passed to 'builtins.elemAt'"));
ASSERT_TRACE1("elemAt [] (-1)", ASSERT_TRACE1("elemAt [] (-1)",
Error, Error,
HintFmt("list index %d is out of bounds", -1)); HintFmt("'builtins.elemAt' called with index %d on a list of size %d", -1, 0));
ASSERT_TRACE1("elemAt [\"foo\"] 3", ASSERT_TRACE1("elemAt [\"foo\"] 3",
Error, Error,
HintFmt("list index %d is out of bounds", 3)); HintFmt("'builtins.elemAt' called with index %d on a list of size %d", 3, 1));
} }
@ -708,11 +708,11 @@ namespace nix {
ASSERT_TRACE2("head 1", ASSERT_TRACE2("head 1",
TypeError, TypeError,
HintFmt("expected a list but found %s: %s", "an integer", Uncolored(ANSI_CYAN "1" ANSI_NORMAL)), HintFmt("expected a list but found %s: %s", "an integer", Uncolored(ANSI_CYAN "1" ANSI_NORMAL)),
HintFmt("while evaluating the first argument passed to builtins.elemAt")); HintFmt("while evaluating the first argument passed to 'builtins.head'"));
ASSERT_TRACE1("head []", ASSERT_TRACE1("head []",
Error, Error,
HintFmt("list index %d is out of bounds", 0)); HintFmt("'builtins.head' called on an empty list"));
} }
@ -721,11 +721,11 @@ namespace nix {
ASSERT_TRACE2("tail 1", ASSERT_TRACE2("tail 1",
TypeError, TypeError,
HintFmt("expected a list but found %s: %s", "an integer", Uncolored(ANSI_CYAN "1" ANSI_NORMAL)), HintFmt("expected a list but found %s: %s", "an integer", Uncolored(ANSI_CYAN "1" ANSI_NORMAL)),
HintFmt("while evaluating the first argument passed to builtins.tail")); HintFmt("while evaluating the first argument passed to 'builtins.tail'"));
ASSERT_TRACE1("tail []", ASSERT_TRACE1("tail []",
Error, Error,
HintFmt("'tail' called on an empty list")); HintFmt("'builtins.tail' called on an empty list"));
} }

View file

@ -138,4 +138,27 @@ TEST(nix_isAllowedURI, non_scheme_colon) {
ASSERT_FALSE(isAllowedURI("https://foo/bar:baz", allowed)); ASSERT_FALSE(isAllowedURI("https://foo/bar:baz", allowed));
} }
class EvalStateTest : public LibExprTest {};
TEST_F(EvalStateTest, getBuiltins_ok) {
auto evaled = maybeThunk("builtins");
auto & builtins = state.getBuiltins();
ASSERT_TRUE(builtins.type() == nAttrs);
ASSERT_EQ(evaled, &builtins);
}
TEST_F(EvalStateTest, getBuiltin_ok) {
auto & builtin = state.getBuiltin("toString");
ASSERT_TRUE(builtin.type() == nFunction);
// FIXME
// auto evaled = maybeThunk("builtins.toString");
// ASSERT_EQ(evaled, &builtin);
auto & builtin2 = state.getBuiltin("true");
ASSERT_EQ(state.forceBool(builtin2, noPos, "in unit test"), true);
}
TEST_F(EvalStateTest, getBuiltin_fail) {
ASSERT_THROW(state.getBuiltin("nonexistent"), EvalError);
}
} // namespace nix } // namespace nix

View file

@ -4,8 +4,6 @@ project('nix-expr-tests', 'cpp',
'cpp_std=c++2a', 'cpp_std=c++2a',
# TODO(Qyriad): increase the warning level # TODO(Qyriad): increase the warning level
'warning_level=1', 'warning_level=1',
'debug=true',
'optimization=2',
'errorlogs=true', # Please print logs for tests that fail 'errorlogs=true', # Please print logs for tests that fail
], ],
meson_version : '>= 1.1', meson_version : '>= 1.1',
@ -14,7 +12,7 @@ project('nix-expr-tests', 'cpp',
cxx = meson.get_compiler('cpp') cxx = meson.get_compiler('cpp')
subdir('build-utils-meson/deps-lists') subdir('nix-meson-build-support/deps-lists')
deps_private_maybe_subproject = [ deps_private_maybe_subproject = [
dependency('nix-expr'), dependency('nix-expr'),
@ -23,12 +21,10 @@ deps_private_maybe_subproject = [
] ]
deps_public_maybe_subproject = [ deps_public_maybe_subproject = [
] ]
subdir('build-utils-meson/subprojects') subdir('nix-meson-build-support/subprojects')
subdir('build-utils-meson/threads') subdir('nix-meson-build-support/export-all-symbols')
subdir('nix-meson-build-support/windows-version')
subdir('build-utils-meson/export-all-symbols')
subdir('build-utils-meson/windows-version')
rapidcheck = dependency('rapidcheck') rapidcheck = dependency('rapidcheck')
deps_private += rapidcheck deps_private += rapidcheck
@ -51,7 +47,7 @@ add_project_arguments(
language : 'cpp', language : 'cpp',
) )
subdir('build-utils-meson/diagnostics') subdir('nix-meson-build-support/common')
sources = files( sources = files(
'derived-path.cc', 'derived-path.cc',

View file

@ -0,0 +1 @@
../../nix-meson-build-support

View file

@ -7,12 +7,49 @@
#include "tests/nix_api_expr.hh" #include "tests/nix_api_expr.hh"
#include "tests/string_callback.hh" #include "tests/string_callback.hh"
#include "file-system.hh"
#include <gmock/gmock.h> #include <gmock/gmock.h>
#include <gtest/gtest.h> #include <gtest/gtest.h>
namespace nixC { namespace nixC {
TEST_F(nix_api_store_test, nix_eval_state_lookup_path)
{
auto tmpDir = nix::createTempDir();
auto delTmpDir = std::make_unique<nix::AutoDelete>(tmpDir, true);
auto nixpkgs = tmpDir + "/pkgs";
auto nixos = tmpDir + "/cfg";
std::filesystem::create_directories(nixpkgs);
std::filesystem::create_directories(nixos);
std::string nixpkgsEntry = "nixpkgs=" + nixpkgs;
std::string nixosEntry = "nixos-config=" + nixos;
const char * lookupPath[] = {nixpkgsEntry.c_str(), nixosEntry.c_str(), nullptr};
auto builder = nix_eval_state_builder_new(ctx, store);
assert_ctx_ok();
ASSERT_EQ(NIX_OK, nix_eval_state_builder_set_lookup_path(ctx, builder, lookupPath));
assert_ctx_ok();
auto state = nix_eval_state_build(ctx, builder);
assert_ctx_ok();
nix_eval_state_builder_free(builder);
Value * value = nix_alloc_value(ctx, state);
nix_expr_eval_from_string(ctx, state, "builtins.seq <nixos-config> <nixpkgs>", ".", value);
assert_ctx_ok();
ASSERT_EQ(nix_get_type(ctx, value), NIX_TYPE_PATH);
assert_ctx_ok();
auto pathStr = nix_get_path_string(ctx, value);
assert_ctx_ok();
ASSERT_EQ(0, strcmp(pathStr, nixpkgs.c_str()));
}
TEST_F(nix_api_expr_test, nix_expr_eval_from_string) TEST_F(nix_api_expr_test, nix_expr_eval_from_string)
{ {
nix_expr_eval_from_string(nullptr, state, "builtins.nixVersion", ".", value); nix_expr_eval_from_string(nullptr, state, "builtins.nixVersion", ".", value);

View file

@ -27,8 +27,8 @@ mkMesonExecutable (finalAttrs: {
workDir = ./.; workDir = ./.;
fileset = fileset.unions [ fileset = fileset.unions [
../../build-utils-meson ../../nix-meson-build-support
./build-utils-meson ./nix-meson-build-support
../../.version ../../.version
./.version ./.version
./meson.build ./meson.build
@ -56,10 +56,6 @@ mkMesonExecutable (finalAttrs: {
mesonFlags = [ mesonFlags = [
]; ];
env = lib.optionalAttrs (stdenv.isLinux && !(stdenv.hostPlatform.isStatic && stdenv.system == "aarch64-linux")) {
LDFLAGS = "-fuse-ld=gold";
};
passthru = { passthru = {
tests = { tests = {
run = runCommand "${finalAttrs.pname}-run" { run = runCommand "${finalAttrs.pname}-run" {

View file

@ -177,6 +177,57 @@ namespace nix {
) )
); );
// The following macros ultimately define 48 tests (16 variations on three
// templates). Each template tests an expression that can be written in 2^4
// different ways, by making four choices about whether to write a particular
// attribute path segment as `x.y = ...;` (collapsed) or `x = { y = ...; };`
// (expanded).
//
// The nestedAttrsetMergeXXXX tests check that the expression
// `{ a.b.c = 1; a.b.d = 2; }` has the same value regardless of how it is
// expanded. (That exact expression is exercised in test
// nestedAttrsetMerge0000, because it is fully collapsed. The test
// nestedAttrsetMerge1001 would instead examine
// `{ a = { b.c = 1; }; a.b = { d = 2; }; }`.)
//
// The nestedAttrsetMergeDupXXXX tests check that the expression
// `{ a.b.c = 1; a.b.c = 2; }` throws a duplicate attribute error, again
// regardless of how it is expanded.
//
// The nestedAttrsetMergeLetXXXX tests check that the expression
// `let a.b.c = 1; a.b.d = 2; in a` has the same value regardless of how it is
// expanded.
#define X_EXPAND_IF0(k, v) k "." v
#define X_EXPAND_IF1(k, v) k " = { " v " };"
#define X4(w, x, y, z) \
TEST_F(TrivialExpressionTest, nestedAttrsetMerge##w##x##y##z) { \
auto v = eval("{ a.b = { c = 1; d = 2; }; } == { " \
X_EXPAND_IF##w("a", X_EXPAND_IF##x("b", "c = 1;")) " " \
X_EXPAND_IF##y("a", X_EXPAND_IF##z("b", "d = 2;")) " }"); \
ASSERT_THAT(v, IsTrue()); \
}; \
TEST_F(TrivialExpressionTest, nestedAttrsetMergeDup##w##x##y##z) { \
ASSERT_THROW(eval("{ " \
X_EXPAND_IF##w("a", X_EXPAND_IF##x("b", "c = 1;")) " " \
X_EXPAND_IF##y("a", X_EXPAND_IF##z("b", "c = 2;")) " }"), Error); \
}; \
TEST_F(TrivialExpressionTest, nestedAttrsetMergeLet##w##x##y##z) { \
auto v = eval("{ b = { c = 1; d = 2; }; } == (let " \
X_EXPAND_IF##w("a", X_EXPAND_IF##x("b", "c = 1;")) " " \
X_EXPAND_IF##y("a", X_EXPAND_IF##z("b", "d = 2;")) " in a)"); \
ASSERT_THAT(v, IsTrue()); \
};
#define X3(...) X4(__VA_ARGS__, 0) X4(__VA_ARGS__, 1)
#define X2(...) X3(__VA_ARGS__, 0) X3(__VA_ARGS__, 1)
#define X1(...) X2(__VA_ARGS__, 0) X2(__VA_ARGS__, 1)
X1(0) X1(1)
#undef X_EXPAND_IF0
#undef X_EXPAND_IF1
#undef X1
#undef X2
#undef X3
#undef X4
TEST_F(TrivialExpressionTest, functor) { TEST_F(TrivialExpressionTest, functor) {
auto v = eval("{ __functor = self: arg: self.v + arg; v = 10; } 5"); auto v = eval("{ __functor = self: arg: self.v + arg; v = 10; } 5");
ASSERT_THAT(v, IsIntEq(15)); ASSERT_THAT(v, IsIntEq(15));

View file

@ -129,7 +129,6 @@ std::pair<SourcePath, uint32_t> findPackageFilename(EvalState & state, Value & v
try { try {
auto colon = fn.rfind(':'); auto colon = fn.rfind(':');
if (colon == std::string::npos) fail(); if (colon == std::string::npos) fail();
std::string filename(fn, 0, colon);
auto lineno = std::stoi(std::string(fn, colon + 1, std::string::npos)); auto lineno = std::stoi(std::string(fn, colon + 1, std::string::npos));
return {SourcePath{path.accessor, CanonPath(fn.substr(0, colon))}, lineno}; return {SourcePath{path.accessor, CanonPath(fn.substr(0, colon))}, lineno};
} catch (std::invalid_argument & e) { } catch (std::invalid_argument & e) {

View file

@ -1 +0,0 @@
../../build-utils-meson

View file

@ -41,10 +41,17 @@ let
(key: node: (key: node:
let let
parentNode = allNodes.${getInputByPath lockFile.root node.parent};
sourceInfo = sourceInfo =
if overrides ? ${key} if overrides ? ${key}
then then
overrides.${key}.sourceInfo overrides.${key}.sourceInfo
else if node.locked.type == "path" && builtins.substring 0 1 node.locked.path != "/"
then
parentNode.sourceInfo // {
outPath = parentNode.outPath + ("/" + node.locked.path);
}
else else
# FIXME: remove obsolete node.info. # FIXME: remove obsolete node.info.
# Note: lock file entries are always final. # Note: lock file entries are always final.

View file

@ -87,11 +87,15 @@ void EvalState::forceValue(Value & v, const PosIdx pos)
{ {
if (v.isThunk()) { if (v.isThunk()) {
Env * env = v.payload.thunk.env; Env * env = v.payload.thunk.env;
assert(env || v.isBlackhole());
Expr * expr = v.payload.thunk.expr; Expr * expr = v.payload.thunk.expr;
try { try {
v.mkBlackhole(); v.mkBlackhole();
//checkInterrupt(); //checkInterrupt();
if (env) [[likely]]
expr->eval(*this, *env, v); expr->eval(*this, *env, v);
else
ExprBlackHole::throwInfiniteRecursionError(*this, v);
} catch (...) { } catch (...) {
v.mkThunk(env, expr); v.mkThunk(env, expr);
tryFixupBlackHolePos(v, pos); tryFixupBlackHolePos(v, pos);

View file

@ -3,10 +3,11 @@
#include "config.hh" #include "config.hh"
#include "ref.hh" #include "ref.hh"
#include "source-path.hh"
namespace nix { namespace nix {
class Store; class EvalState;
struct EvalSettings : Config struct EvalSettings : Config
{ {
@ -18,11 +19,8 @@ struct EvalSettings : Config
* *
* The return value is (a) whether the entry was valid, and, if so, * The return value is (a) whether the entry was valid, and, if so,
* what does it map to. * what does it map to.
*
* @todo Return (`std::optional` of) `SourceAccssor` or something
* more structured instead of mere `std::string`?
*/ */
using LookupPathHook = std::optional<std::string>(ref<Store> store, std::string_view); using LookupPathHook = std::optional<SourcePath>(EvalState & state, std::string_view);
/** /**
* Map from "scheme" to a `LookupPathHook`. * Map from "scheme" to a `LookupPathHook`.

View file

@ -347,6 +347,16 @@ void EvalState::allowPath(const StorePath & storePath)
rootFS2->allowPrefix(CanonPath(store->toRealPath(storePath))); rootFS2->allowPrefix(CanonPath(store->toRealPath(storePath)));
} }
void EvalState::allowClosure(const StorePath & storePath)
{
if (!rootFS.dynamic_pointer_cast<AllowListSourceAccessor>()) return;
StorePathSet closure;
store->computeFSClosure(storePath, closure);
for (auto & p : closure)
allowPath(p);
}
void EvalState::allowAndSetStorePathString(const StorePath & storePath, Value & v) void EvalState::allowAndSetStorePathString(const StorePath & storePath, Value & v)
{ {
allowPath(storePath); allowPath(storePath);
@ -396,7 +406,7 @@ void EvalState::checkURI(const std::string & uri)
/* If the URI is a path, then check it against allowedPaths as /* If the URI is a path, then check it against allowedPaths as
well. */ well. */
if (hasPrefix(uri, "/")) { if (isAbsolute(uri)) {
if (auto rootFS2 = rootFS.dynamic_pointer_cast<AllowListSourceAccessor>()) if (auto rootFS2 = rootFS.dynamic_pointer_cast<AllowListSourceAccessor>())
rootFS2->checkAccess(CanonPath(uri)); rootFS2->checkAccess(CanonPath(uri));
return; return;
@ -448,7 +458,7 @@ void EvalState::addConstant(const std::string & name, Value * v, Constant info)
/* Install value the base environment. */ /* Install value the base environment. */
staticBaseEnv->vars.emplace_back(symbols.create(name), baseEnvDispl); staticBaseEnv->vars.emplace_back(symbols.create(name), baseEnvDispl);
baseEnv.values[baseEnvDispl++] = v; baseEnv.values[baseEnvDispl++] = v;
baseEnv.values[0]->payload.attrs->push_back(Attr(symbols.create(name2), v)); getBuiltins().payload.attrs->push_back(Attr(symbols.create(name2), v));
} }
} }
@ -516,16 +526,26 @@ Value * EvalState::addPrimOp(PrimOp && primOp)
else { else {
staticBaseEnv->vars.emplace_back(envName, baseEnvDispl); staticBaseEnv->vars.emplace_back(envName, baseEnvDispl);
baseEnv.values[baseEnvDispl++] = v; baseEnv.values[baseEnvDispl++] = v;
baseEnv.values[0]->payload.attrs->push_back(Attr(symbols.create(primOp.name), v)); getBuiltins().payload.attrs->push_back(Attr(symbols.create(primOp.name), v));
} }
return v; return v;
} }
Value & EvalState::getBuiltins()
{
return *baseEnv.values[0];
}
Value & EvalState::getBuiltin(const std::string & name) Value & EvalState::getBuiltin(const std::string & name)
{ {
return *baseEnv.values[0]->attrs()->find(symbols.create(name))->value; auto it = getBuiltins().attrs()->get(symbols.create(name));
if (it)
return *it->value;
else
error<EvalError>("builtin '%1%' not found", name).debugThrow();
} }
@ -588,14 +608,14 @@ std::optional<EvalState::Doc> EvalState::getDoc(Value & v)
if (isFunctor(v)) { if (isFunctor(v)) {
try { try {
Value & functor = *v.attrs()->find(sFunctor)->value; Value & functor = *v.attrs()->find(sFunctor)->value;
Value * vp = &v; Value * vp[] = {&v};
Value partiallyApplied; Value partiallyApplied;
// The first paramater is not user-provided, and may be // The first paramater is not user-provided, and may be
// handled by code that is opaque to the user, like lib.const = x: y: y; // handled by code that is opaque to the user, like lib.const = x: y: y;
// So preferably we show docs that are relevant to the // So preferably we show docs that are relevant to the
// "partially applied" function returned by e.g. `const`. // "partially applied" function returned by e.g. `const`.
// We apply the first argument: // We apply the first argument:
callFunction(functor, 1, &vp, partiallyApplied, noPos); callFunction(functor, vp, partiallyApplied, noPos);
auto _level = addCallDepth(noPos); auto _level = addCallDepth(noPos);
return getDoc(partiallyApplied); return getDoc(partiallyApplied);
} }
@ -1460,7 +1480,7 @@ void ExprLambda::eval(EvalState & state, Env & env, Value & v)
v.mkLambda(&env, this); v.mkLambda(&env, this);
} }
void EvalState::callFunction(Value & fun, size_t nrArgs, Value * * args, Value & vRes, const PosIdx pos) void EvalState::callFunction(Value & fun, std::span<Value *> args, Value & vRes, const PosIdx pos)
{ {
auto _level = addCallDepth(pos); auto _level = addCallDepth(pos);
@ -1475,16 +1495,16 @@ void EvalState::callFunction(Value & fun, size_t nrArgs, Value * * args, Value &
auto makeAppChain = [&]() auto makeAppChain = [&]()
{ {
vRes = vCur; vRes = vCur;
for (size_t i = 0; i < nrArgs; ++i) { for (auto arg : args) {
auto fun2 = allocValue(); auto fun2 = allocValue();
*fun2 = vRes; *fun2 = vRes;
vRes.mkPrimOpApp(fun2, args[i]); vRes.mkPrimOpApp(fun2, arg);
} }
}; };
const Attr * functor; const Attr * functor;
while (nrArgs > 0) { while (args.size() > 0) {
if (vCur.isLambda()) { if (vCur.isLambda()) {
@ -1587,15 +1607,14 @@ void EvalState::callFunction(Value & fun, size_t nrArgs, Value * * args, Value &
throw; throw;
} }
nrArgs--; args = args.subspan(1);
args += 1;
} }
else if (vCur.isPrimOp()) { else if (vCur.isPrimOp()) {
size_t argsLeft = vCur.primOp()->arity; size_t argsLeft = vCur.primOp()->arity;
if (nrArgs < argsLeft) { if (args.size() < argsLeft) {
/* We don't have enough arguments, so create a tPrimOpApp chain. */ /* We don't have enough arguments, so create a tPrimOpApp chain. */
makeAppChain(); makeAppChain();
return; return;
@ -1607,15 +1626,14 @@ void EvalState::callFunction(Value & fun, size_t nrArgs, Value * * args, Value &
if (countCalls) primOpCalls[fn->name]++; if (countCalls) primOpCalls[fn->name]++;
try { try {
fn->fun(*this, vCur.determinePos(noPos), args, vCur); fn->fun(*this, vCur.determinePos(noPos), args.data(), vCur);
} catch (Error & e) { } catch (Error & e) {
if (fn->addTrace) if (fn->addTrace)
addErrorTrace(e, pos, "while calling the '%1%' builtin", fn->name); addErrorTrace(e, pos, "while calling the '%1%' builtin", fn->name);
throw; throw;
} }
nrArgs -= argsLeft; args = args.subspan(argsLeft);
args += argsLeft;
} }
} }
@ -1631,7 +1649,7 @@ void EvalState::callFunction(Value & fun, size_t nrArgs, Value * * args, Value &
auto arity = primOp->primOp()->arity; auto arity = primOp->primOp()->arity;
auto argsLeft = arity - argsDone; auto argsLeft = arity - argsDone;
if (nrArgs < argsLeft) { if (args.size() < argsLeft) {
/* We still don't have enough arguments, so extend the tPrimOpApp chain. */ /* We still don't have enough arguments, so extend the tPrimOpApp chain. */
makeAppChain(); makeAppChain();
return; return;
@ -1663,8 +1681,7 @@ void EvalState::callFunction(Value & fun, size_t nrArgs, Value * * args, Value &
throw; throw;
} }
nrArgs -= argsLeft; args = args.subspan(argsLeft);
args += argsLeft;
} }
} }
@ -1675,13 +1692,12 @@ void EvalState::callFunction(Value & fun, size_t nrArgs, Value * * args, Value &
Value * args2[] = {allocValue(), args[0]}; Value * args2[] = {allocValue(), args[0]};
*args2[0] = vCur; *args2[0] = vCur;
try { try {
callFunction(*functor->value, 2, args2, vCur, functor->pos); callFunction(*functor->value, args2, vCur, functor->pos);
} catch (Error & e) { } catch (Error & e) {
e.addTrace(positions[pos], "while calling a functor (an attribute set with a '__functor' attribute)"); e.addTrace(positions[pos], "while calling a functor (an attribute set with a '__functor' attribute)");
throw; throw;
} }
nrArgs--; args = args.subspan(1);
args++;
} }
else else
@ -1724,7 +1740,7 @@ void ExprCall::eval(EvalState & state, Env & env, Value & v)
for (size_t i = 0; i < args.size(); ++i) for (size_t i = 0; i < args.size(); ++i)
vArgs[i] = args[i]->maybeThunk(state, env); vArgs[i] = args[i]->maybeThunk(state, env);
state.callFunction(vFun, args.size(), vArgs.data(), v, pos); state.callFunction(vFun, vArgs, v, pos);
} }
@ -2046,9 +2062,12 @@ void ExprPos::eval(EvalState & state, Env & env, Value & v)
state.mkPos(v, pos); state.mkPos(v, pos);
} }
void ExprBlackHole::eval(EvalState & state, [[maybe_unused]] Env & env, Value & v)
void ExprBlackHole::eval(EvalState & state, Env & env, Value & v)
{ {
throwInfiniteRecursionError(state, v);
}
[[gnu::noinline]] [[noreturn]] void ExprBlackHole::throwInfiniteRecursionError(EvalState & state, Value &v) {
state.error<InfiniteRecursionError>("infinite recursion encountered") state.error<InfiniteRecursionError>("infinite recursion encountered")
.atPos(v.determinePos(noPos)) .atPos(v.determinePos(noPos))
.debugThrow(); .debugThrow();
@ -3029,8 +3048,8 @@ SourcePath EvalState::findFile(const LookupPath & lookupPath, const std::string_
if (!rOpt) continue; if (!rOpt) continue;
auto r = *rOpt; auto r = *rOpt;
Path res = suffix == "" ? r : concatStrings(r, "/", suffix); auto res = (r / CanonPath(suffix)).resolveSymlinks();
if (pathExists(res)) return rootPath(CanonPath(canonPath(res))); if (res.pathExists()) return res;
} }
if (hasPrefix(path, "nix/")) if (hasPrefix(path, "nix/"))
@ -3045,13 +3064,13 @@ SourcePath EvalState::findFile(const LookupPath & lookupPath, const std::string_
} }
std::optional<std::string> EvalState::resolveLookupPathPath(const LookupPath::Path & value0, bool initAccessControl) std::optional<SourcePath> EvalState::resolveLookupPathPath(const LookupPath::Path & value0, bool initAccessControl)
{ {
auto & value = value0.s; auto & value = value0.s;
auto i = lookupPathResolved.find(value); auto i = lookupPathResolved.find(value);
if (i != lookupPathResolved.end()) return i->second; if (i != lookupPathResolved.end()) return i->second;
auto finish = [&](std::string res) { auto finish = [&](SourcePath res) {
debug("resolved search path element '%s' to '%s'", value, res); debug("resolved search path element '%s' to '%s'", value, res);
lookupPathResolved.emplace(value, res); lookupPathResolved.emplace(value, res);
return res; return res;
@ -3064,7 +3083,7 @@ std::optional<std::string> EvalState::resolveLookupPathPath(const LookupPath::Pa
fetchSettings, fetchSettings,
EvalSettings::resolvePseudoUrl(value)); EvalSettings::resolvePseudoUrl(value));
auto storePath = fetchToStore(*store, SourcePath(accessor), FetchMode::Copy); auto storePath = fetchToStore(*store, SourcePath(accessor), FetchMode::Copy);
return finish(store->toRealPath(storePath)); return finish(rootPath(store->toRealPath(storePath)));
} catch (Error & e) { } catch (Error & e) {
logWarning({ logWarning({
.msg = HintFmt("Nix search path entry '%1%' cannot be downloaded, ignoring", value) .msg = HintFmt("Nix search path entry '%1%' cannot be downloaded, ignoring", value)
@ -3076,29 +3095,26 @@ std::optional<std::string> EvalState::resolveLookupPathPath(const LookupPath::Pa
auto scheme = value.substr(0, colPos); auto scheme = value.substr(0, colPos);
auto rest = value.substr(colPos + 1); auto rest = value.substr(colPos + 1);
if (auto * hook = get(settings.lookupPathHooks, scheme)) { if (auto * hook = get(settings.lookupPathHooks, scheme)) {
auto res = (*hook)(store, rest); auto res = (*hook)(*this, rest);
if (res) if (res)
return finish(std::move(*res)); return finish(std::move(*res));
} }
} }
{ {
auto path = absPath(value); auto path = rootPath(value);
/* Allow access to paths in the search path. */ /* Allow access to paths in the search path. */
if (initAccessControl) { if (initAccessControl) {
allowPath(path); allowPath(path.path.abs());
if (store->isInStore(path)) { if (store->isInStore(path.path.abs())) {
try { try {
StorePathSet closure; allowClosure(store->toStorePath(path.path.abs()).first);
store->computeFSClosure(store->toStorePath(path).first, closure);
for (auto & p : closure)
allowPath(p);
} catch (InvalidPath &) { } } catch (InvalidPath &) { }
} }
} }
if (pathExists(path)) if (path.pathExists())
return finish(std::move(path)); return finish(std::move(path));
else { else {
logWarning({ logWarning({
@ -3109,7 +3125,6 @@ std::optional<std::string> EvalState::resolveLookupPathPath(const LookupPath::Pa
debug("failed to resolve search path element '%s'", value); debug("failed to resolve search path element '%s'", value);
return std::nullopt; return std::nullopt;
} }
@ -3170,5 +3185,18 @@ std::ostream & operator << (std::ostream & str, const ExternalValueBase & v) {
return v.print(str); return v.print(str);
} }
void forceNoNullByte(std::string_view s, std::function<Pos()> pos)
{
if (s.find('\0') != s.npos) {
using namespace std::string_view_literals;
auto str = replaceStrings(std::string(s), "\0"sv, ""sv);
Error error("input string '%s' cannot be represented as Nix string because it contains null bytes", str);
if (pos) {
error.atPos(pos());
}
throw error;
}
}
} }

View file

@ -347,7 +347,7 @@ private:
LookupPath lookupPath; LookupPath lookupPath;
std::map<std::string, std::optional<std::string>> lookupPathResolved; std::map<std::string, std::optional<SourcePath>> lookupPathResolved;
/** /**
* Cache used by prim_match(). * Cache used by prim_match().
@ -400,6 +400,11 @@ public:
*/ */
void allowPath(const StorePath & storePath); void allowPath(const StorePath & storePath);
/**
* Allow access to the closure of a store path.
*/
void allowClosure(const StorePath & storePath);
/** /**
* Allow access to a store path and return it as a string. * Allow access to a store path and return it as a string.
*/ */
@ -452,9 +457,9 @@ public:
* *
* If the specified search path element is a URI, download it. * If the specified search path element is a URI, download it.
* *
* If it is not found, return `std::nullopt` * If it is not found, return `std::nullopt`.
*/ */
std::optional<std::string> resolveLookupPathPath( std::optional<SourcePath> resolveLookupPathPath(
const LookupPath::Path & elem, const LookupPath::Path & elem,
bool initAccessControl = false); bool initAccessControl = false);
@ -623,8 +628,19 @@ private:
public: public:
/**
* Retrieve a specific builtin, equivalent to evaluating `builtins.${name}`.
* @param name The attribute name of the builtin to retrieve.
* @throws EvalError if the builtin does not exist.
*/
Value & getBuiltin(const std::string & name); Value & getBuiltin(const std::string & name);
/**
* Retrieve the `builtins` attrset, equivalent to evaluating the reference `builtins`.
* Always returns an attribute set value.
*/
Value & getBuiltins();
struct Doc struct Doc
{ {
Pos pos; Pos pos;
@ -690,13 +706,12 @@ public:
bool isFunctor(Value & fun); bool isFunctor(Value & fun);
// FIXME: use std::span void callFunction(Value & fun, std::span<Value *> args, Value & vRes, const PosIdx pos);
void callFunction(Value & fun, size_t nrArgs, Value * * args, Value & vRes, const PosIdx pos);
void callFunction(Value & fun, Value & arg, Value & vRes, const PosIdx pos) void callFunction(Value & fun, Value & arg, Value & vRes, const PosIdx pos)
{ {
Value * args[] = {&arg}; Value * args[] = {&arg};
callFunction(fun, 1, args, vRes, pos); callFunction(fun, args, vRes, pos);
} }
/** /**
@ -809,7 +824,6 @@ public:
bool callPathFilter( bool callPathFilter(
Value * filterFun, Value * filterFun,
const SourcePath & path, const SourcePath & path,
std::string_view pathArg,
PosIdx pos); PosIdx pos);
DocComment getDocCommentForPos(PosIdx pos); DocComment getDocCommentForPos(PosIdx pos);

View file

@ -50,6 +50,7 @@ class JSONSax : nlohmann::json_sax<json> {
public: public:
void key(string_t & name, EvalState & state) void key(string_t & name, EvalState & state)
{ {
forceNoNullByte(name);
attrs.insert_or_assign(state.symbols.create(name), &value(state)); attrs.insert_or_assign(state.symbols.create(name), &value(state));
} }
}; };
@ -122,6 +123,7 @@ public:
bool string(string_t & val) override bool string(string_t & val) override
{ {
forceNoNullByte(val);
rs->value(state).mkString(val); rs->value(state).mkString(val);
rs->add(); rs->add();
return true; return true;

View file

@ -1,5 +1,13 @@
#pragma once #pragma once
#include <cstddef>
// inluding the generated headers twice leads to errors
#ifndef BISON_HEADER
# include "lexer-tab.hh"
# include "parser-tab.hh"
#endif
namespace nix::lexer::internal { namespace nix::lexer::internal {
void initLoc(YYLTYPE * loc); void initLoc(YYLTYPE * loc);

View file

@ -41,16 +41,18 @@ namespace nix {
// we make use of the fact that the parser receives a private copy of the input // we make use of the fact that the parser receives a private copy of the input
// string and can munge around in it. // string and can munge around in it.
static StringToken unescapeStr(SymbolTable & symbols, char * s, size_t length) // getting the position is expensive and thus it is implemented lazily.
static StringToken unescapeStr(char * const s, size_t length, std::function<Pos()> && pos)
{ {
char * result = s; bool noNullByte = true;
char * t = s; char * t = s;
char c;
// the input string is terminated with *two* NULs, so we can safely take // the input string is terminated with *two* NULs, so we can safely take
// *one* character after the one being checked against. // *one* character after the one being checked against.
while ((c = *s++)) { for (size_t i = 0; i < length; t++) {
char c = s[i++];
noNullByte &= c != '\0';
if (c == '\\') { if (c == '\\') {
c = *s++; c = s[i++];
if (c == 'n') *t = '\n'; if (c == 'n') *t = '\n';
else if (c == 'r') *t = '\r'; else if (c == 'r') *t = '\r';
else if (c == 't') *t = '\t'; else if (c == 't') *t = '\t';
@ -59,12 +61,14 @@ static StringToken unescapeStr(SymbolTable & symbols, char * s, size_t length)
else if (c == '\r') { else if (c == '\r') {
/* Normalise CR and CR/LF into LF. */ /* Normalise CR and CR/LF into LF. */
*t = '\n'; *t = '\n';
if (*s == '\n') s++; /* cr/lf */ if (s[i] == '\n') i++; /* cr/lf */
} }
else *t = c; else *t = c;
t++;
} }
return {result, size_t(t - result)}; if (!noNullByte) {
forceNoNullByte({s, size_t(t - s)}, std::move(pos));
}
return {s, size_t(t - s)};
} }
static void requireExperimentalFeature(const ExperimentalFeature & feature, const Pos & pos) static void requireExperimentalFeature(const ExperimentalFeature & feature, const Pos & pos)
@ -175,7 +179,7 @@ or { return OR_KW; }
/* It is impossible to match strings ending with '$' with one /* It is impossible to match strings ending with '$' with one
regex because trailing contexts are only valid at the end regex because trailing contexts are only valid at the end
of a rule. (A sane but undocumented limitation.) */ of a rule. (A sane but undocumented limitation.) */
yylval->str = unescapeStr(state->symbols, yytext, yyleng); yylval->str = unescapeStr(yytext, yyleng, [&]() { return state->positions[CUR_POS]; });
return STR; return STR;
} }
<STRING>\$\{ { PUSH_STATE(DEFAULT); return DOLLAR_CURLY; } <STRING>\$\{ { PUSH_STATE(DEFAULT); return DOLLAR_CURLY; }
@ -191,6 +195,7 @@ or { return OR_KW; }
\'\'(\ *\n)? { PUSH_STATE(IND_STRING); return IND_STRING_OPEN; } \'\'(\ *\n)? { PUSH_STATE(IND_STRING); return IND_STRING_OPEN; }
<IND_STRING>([^\$\']|\$[^\{\']|\'[^\'\$])+ { <IND_STRING>([^\$\']|\$[^\{\']|\'[^\'\$])+ {
yylval->str = {yytext, (size_t) yyleng, true}; yylval->str = {yytext, (size_t) yyleng, true};
forceNoNullByte(yylval->str, [&]() { return state->positions[CUR_POS]; });
return IND_STR; return IND_STR;
} }
<IND_STRING>\'\'\$ | <IND_STRING>\'\'\$ |
@ -203,7 +208,7 @@ or { return OR_KW; }
return IND_STR; return IND_STR;
} }
<IND_STRING>\'\'\\{ANY} { <IND_STRING>\'\'\\{ANY} {
yylval->str = unescapeStr(state->symbols, yytext + 2, yyleng - 2); yylval->str = unescapeStr(yytext + 2, yyleng - 2, [&]() { return state->positions[CUR_POS]; });
return IND_STR; return IND_STR;
} }
<IND_STRING>\$\{ { PUSH_STATE(DEFAULT); return DOLLAR_CURLY; } <IND_STRING>\$\{ { PUSH_STATE(DEFAULT); return DOLLAR_CURLY; }

View file

@ -4,8 +4,6 @@ project('nix-expr', 'cpp',
'cpp_std=c++2a', 'cpp_std=c++2a',
# TODO(Qyriad): increase the warning level # TODO(Qyriad): increase the warning level
'warning_level=1', 'warning_level=1',
'debug=true',
'optimization=2',
'errorlogs=true', # Please print logs for tests that fail 'errorlogs=true', # Please print logs for tests that fail
], ],
meson_version : '>= 1.1', meson_version : '>= 1.1',
@ -14,7 +12,7 @@ project('nix-expr', 'cpp',
cxx = meson.get_compiler('cpp') cxx = meson.get_compiler('cpp')
subdir('build-utils-meson/deps-lists') subdir('nix-meson-build-support/deps-lists')
configdata = configuration_data() configdata = configuration_data()
@ -25,9 +23,7 @@ deps_public_maybe_subproject = [
dependency('nix-store'), dependency('nix-store'),
dependency('nix-fetchers'), dependency('nix-fetchers'),
] ]
subdir('build-utils-meson/subprojects') subdir('nix-meson-build-support/subprojects')
subdir('build-utils-meson/threads')
boost = dependency( boost = dependency(
'boost', 'boost',
@ -79,7 +75,7 @@ add_project_arguments(
language : 'cpp', language : 'cpp',
) )
subdir('build-utils-meson/diagnostics') subdir('nix-meson-build-support/common')
parser_tab = custom_target( parser_tab = custom_target(
input : 'parser.y', input : 'parser.y',
@ -123,7 +119,7 @@ lexer_tab = custom_target(
install_dir : get_option('includedir') / 'nix', install_dir : get_option('includedir') / 'nix',
) )
subdir('build-utils-meson/generate-header') subdir('nix-meson-build-support/generate-header')
generated_headers = [] generated_headers = []
foreach header : [ foreach header : [
@ -207,4 +203,4 @@ install_headers(headers, subdir : 'nix', preserve_path : true)
libraries_private = [] libraries_private = []
subdir('build-utils-meson/export') subdir('nix-meson-build-support/export')

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