Compare commits
251 Commits
progress-b
...
2.26.0
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
28752fe288 | ||
|
|
7af6329005 | ||
|
|
21f2e29176 | ||
|
|
617bf84518 | ||
|
|
d8dbb71c92 | ||
|
|
069ca2a21a | ||
|
|
b09b4dc995 | ||
|
|
b8c296f0de | ||
|
|
9e324df383 | ||
|
|
27c9f7ef78 | ||
|
|
9a71c3c60d | ||
|
|
a82c63f5d8 | ||
|
|
f358ab2b0c | ||
|
|
f0c209fb14 | ||
|
|
432e3a1cbf | ||
|
|
e3a285f229 | ||
|
|
6b987206ce | ||
|
|
6f0bdd9ae7 | ||
|
|
0fe92067fb | ||
|
|
ac31767c57 | ||
|
|
0be69d4573 | ||
|
|
f12ef308f6 | ||
|
|
ae7bc5f2c7 | ||
|
|
fcc501b730 | ||
|
|
8e05ddfd84 | ||
|
|
4adb579c42 | ||
|
|
dccabc8f84 | ||
|
|
809f157a49 | ||
|
|
2cb494f561 | ||
|
|
0c85477f8e | ||
|
|
89e5047e12 | ||
|
|
15073e86a8 | ||
|
|
a78f55ef97 | ||
|
|
263a818bee | ||
|
|
991be65272 | ||
|
|
2669e4ac4f | ||
|
|
d8636843b1 | ||
|
|
c656725a15 | ||
|
|
cc838e8181 | ||
|
|
a8c69cc907 | ||
|
|
c59aa3ec87 | ||
|
|
bcb92a5fa0 | ||
|
|
4f0e352ef2 | ||
|
|
f5548c17ed | ||
|
|
9003343b53 | ||
|
|
43a170a554 | ||
|
|
3197c19a31 | ||
|
|
12aff40ad7 | ||
|
|
f0271090b2 | ||
|
|
5807b5cda0 | ||
|
|
17b6557c03 | ||
|
|
6ea339ce8a | ||
|
|
3716ded8df | ||
|
|
f36cbee7fd | ||
|
|
d388a691f3 | ||
|
|
12e14956e2 | ||
|
|
2ca0c62a8d | ||
|
|
043df13f72 | ||
|
|
db46d40b12 | ||
|
|
8b1fb92a0c | ||
|
|
5d03ef9caf | ||
|
|
521667eb89 | ||
|
|
550fe889ee | ||
|
|
0c101679b4 | ||
|
|
e02026adae | ||
|
|
c45dfeeef3 | ||
|
|
9c239d4353 | ||
|
|
7ad02b62e0 | ||
|
|
cab347b4eb | ||
|
|
3d078cd508 | ||
|
|
61467b72af | ||
|
|
6a874c2865 | ||
|
|
ff9d886f3c | ||
|
|
ff8e2fe84e | ||
|
|
6cc5b48a29 | ||
|
|
8aafc05885 | ||
|
|
0be55f869b | ||
|
|
2cb0ddfe4e | ||
|
|
cd0127f957 | ||
|
|
a78f998cc7 | ||
|
|
fd053fdcad | ||
|
|
140aee33a3 | ||
|
|
01f5cf2c02 | ||
|
|
4fac767b52 | ||
|
|
29a1a21ce4 | ||
|
|
afac093b34 | ||
|
|
a44ae8b5a9 | ||
|
|
84f116e3cf | ||
|
|
d155e349fc | ||
|
|
47cf93ba80 | ||
|
|
e161393299 | ||
|
|
22adffec34 | ||
|
|
d9a50c0af2 | ||
|
|
37ac18d1d9 | ||
|
|
9d088fa502 | ||
|
|
96bd9bad2f | ||
|
|
fccfdbea57 | ||
|
|
2d9b213cc2 | ||
|
|
9b9e416836 | ||
|
|
3ad0f45e79 | ||
|
|
1a38e62a09 | ||
|
|
5f7b535b81 | ||
|
|
83ff523865 | ||
|
|
3d877ecae4 | ||
|
|
28caa35a97 | ||
|
|
ed4f2c3204 | ||
|
|
5230d3ecc4 | ||
|
|
8bec777c9d | ||
|
|
3a5fccc418 | ||
|
|
2f3bc6c015 | ||
|
|
d329b2632a | ||
|
|
4077aa43a8 | ||
|
|
f705ce7f9a | ||
|
|
ef2739b7c9 | ||
|
|
0792152627 | ||
|
|
e8c7dd9971 | ||
|
|
75cda2da7f | ||
|
|
a0901e5588 | ||
|
|
383ab87da3 | ||
|
|
4c74d679b6 | ||
|
|
5a5a86949a | ||
|
|
628c11d237 | ||
|
|
ddbbf53767 | ||
|
|
c0b64f3377 | ||
|
|
91e91f62fa | ||
|
|
36563c69a4 | ||
|
|
69853c067c | ||
|
|
438a20427f | ||
|
|
6827768807 | ||
|
|
daab4d18ac | ||
|
|
1eba904b79 | ||
|
|
98d75de1ea | ||
|
|
a44e9dd1ea | ||
|
|
442a2623e4 | ||
|
|
359a0840e2 | ||
|
|
01c96f9fd5 | ||
|
|
deb3533eab | ||
|
|
5ebc8d4960 | ||
|
|
4f3960ea26 | ||
|
|
4a2310a3a0 | ||
|
|
18f077035a | ||
|
|
5e21bdc623 | ||
|
|
7a8a28629c | ||
|
|
3859b3b9e1 | ||
|
|
1a402e0c53 | ||
|
|
61c3559116 | ||
|
|
a7f556350c | ||
|
|
968aabfb68 | ||
|
|
edbfe863ce | ||
|
|
5c968be162 | ||
|
|
ce1e9ba85a | ||
|
|
b5f10655ed | ||
|
|
1e2cace5f1 | ||
|
|
8a3fc27f1b | ||
|
|
b3eab028df | ||
|
|
92e30955b9 | ||
|
|
cf69c99f3e | ||
|
|
fe5f02c2c2 | ||
|
|
bff9296ab9 | ||
|
|
4a91e627a7 | ||
|
|
9223d64ac6 | ||
|
|
f72752c0dc | ||
|
|
6126007859 | ||
|
|
b978fa8450 | ||
|
|
f0c1262d23 | ||
|
|
535724fd79 | ||
|
|
a8e1b4757e | ||
|
|
8534c4222c | ||
|
|
3f3feae33e | ||
|
|
8117f16541 | ||
|
|
5cd5391dd8 | ||
|
|
6848154b2e | ||
|
|
3392a96901 | ||
|
|
eda331e53f | ||
|
|
b9bbdbeb0b | ||
|
|
b338140931 | ||
|
|
00f08deb48 | ||
|
|
757ea70644 | ||
|
|
33852ead6b | ||
|
|
b9f60faab5 | ||
|
|
7ba933e989 | ||
|
|
f469bc2ae4 | ||
|
|
d044a05197 | ||
|
|
331bf3e261 | ||
|
|
da7e3be8fc | ||
|
|
51463d2280 | ||
|
|
20ee83fffd | ||
|
|
2512619cb6 | ||
|
|
cf0ba0d20e | ||
|
|
b5ad051b6c | ||
|
|
81b6b79a56 | ||
|
|
1a8bd84f55 | ||
|
|
9131905185 | ||
|
|
cd002ae6dd | ||
|
|
a24d1c30bd | ||
|
|
08361f031d | ||
|
|
18770c7e18 | ||
|
|
f8eb2f6445 | ||
|
|
bdf579d018 | ||
|
|
b167e2c415 | ||
|
|
d2e1d4916a | ||
|
|
d1f20e3510 | ||
|
|
d8117c8c0d | ||
|
|
2f32cf6d90 | ||
|
|
2baab6412f | ||
|
|
7ddf7300b5 | ||
|
|
3663480d1b | ||
|
|
ccaa4c259a | ||
|
|
df3eb4f3f6 | ||
|
|
6162e1220e | ||
|
|
da7f7ba810 | ||
|
|
6a23803066 | ||
|
|
2a98168942 | ||
|
|
472912f7ca | ||
|
|
80ee736b02 | ||
|
|
7d1c41f74c | ||
|
|
bf24092bca | ||
|
|
77e391b39d | ||
|
|
366611391e | ||
|
|
44bc4c6365 | ||
|
|
4f831e2be5 | ||
|
|
9389b27fe4 | ||
|
|
9f2b9256b6 | ||
|
|
31b3a34e5b | ||
|
|
691b1ea237 | ||
|
|
f1187cb696 | ||
|
|
9cf1b0cb57 | ||
|
|
be04e68b34 | ||
|
|
da2c2547a9 | ||
|
|
6d97d57dc7 | ||
|
|
b5cdf2e268 | ||
|
|
41a464c68d | ||
|
|
9b40618d2e | ||
|
|
35dd19d785 | ||
|
|
8490fba42d | ||
|
|
8b9e0f86e4 | ||
|
|
ad296eae2a | ||
|
|
d1894f3456 | ||
|
|
e83481f5a8 | ||
|
|
04975f7c32 | ||
|
|
985b2f9df3 | ||
|
|
00b99b8bc0 | ||
|
|
0b00bf7c09 | ||
|
|
f2063255a4 | ||
|
|
91e7d493ce | ||
|
|
71d4bb8c2e | ||
|
|
09d71974ef | ||
|
|
21fc07c1a4 | ||
|
|
3180671cab | ||
|
|
49f592dd99 | ||
|
|
b2be6fed86 |
183
.github/workflows/ci.yml
vendored
183
.github/workflows/ci.yml
vendored
@@ -7,14 +7,28 @@ on:
|
||||
permissions: read-all
|
||||
|
||||
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:
|
||||
needs: [check_secrets]
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
os: [ubuntu-latest, macos-latest]
|
||||
runs-on: ${{ matrix.os }}
|
||||
include:
|
||||
- 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
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
@@ -26,102 +40,51 @@ jobs:
|
||||
extra_nix_config: |
|
||||
sandbox = true
|
||||
max-jobs = 1
|
||||
- run: echo CACHIX_NAME="$(echo $GITHUB_REPOSITORY-install-tests | tr "[A-Z]/" "[a-z]-")" >> $GITHUB_ENV
|
||||
- uses: cachix/cachix-action@v15
|
||||
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:
|
||||
name: '${{ env.CACHIX_NAME }}'
|
||||
signingKey: '${{ secrets.CACHIX_SIGNING_KEY }}'
|
||||
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
|
||||
name: installer-${{matrix.os}}
|
||||
path: out/*
|
||||
|
||||
installer_test:
|
||||
needs: [installer, check_secrets]
|
||||
if: github.event_name == 'push' && needs.check_secrets.outputs.cachix == 'true'
|
||||
needs: [tests]
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
os: [ubuntu-latest, macos-latest]
|
||||
runs-on: ${{ matrix.os }}
|
||||
include:
|
||||
- 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:
|
||||
- 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
|
||||
with:
|
||||
install_url: '${{needs.installer.outputs.installerURL}}'
|
||||
install_options: "--tarball-url-prefix https://${{ env.CACHIX_NAME }}.cachix.org/serve"
|
||||
install_url: 'http://localhost:8126/install'
|
||||
install_options: "--tarball-url-prefix http://localhost:8126/"
|
||||
- run: sudo apt install fish zsh
|
||||
if: matrix.os == 'ubuntu-latest'
|
||||
if: matrix.os == 'linux'
|
||||
- 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 sh -c "nix-instantiate -E 'builtins.currentTime' --eval"
|
||||
- run: exec zsh -c "nix-instantiate -E 'builtins.currentTime' --eval"
|
||||
@@ -129,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 --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:
|
||||
needs: [check_secrets, tests, vm_tests]
|
||||
needs: [tests, vm_tests, check_secrets]
|
||||
permissions:
|
||||
contents: read
|
||||
packages: write
|
||||
if: >-
|
||||
needs.check_secrets.outputs.docker == 'true' &&
|
||||
github.event_name == 'push' &&
|
||||
github.ref_name == 'master' &&
|
||||
needs.check_secrets.outputs.cachix == 'true' &&
|
||||
needs.check_secrets.outputs.docker == 'true'
|
||||
runs-on: ubuntu-latest
|
||||
github.ref_name == 'master'
|
||||
runs-on: ubuntu-24.04
|
||||
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
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- uses: cachix/install-nix-action@v30
|
||||
with:
|
||||
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
|
||||
- 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: docker load -i ./result/image.tar.gz
|
||||
- run: docker tag nix:$NIX_VERSION ${{ secrets.DOCKERHUB_USERNAME }}/nix:$NIX_VERSION
|
||||
@@ -191,7 +172,7 @@ jobs:
|
||||
docker push $IMAGE_ID:master
|
||||
|
||||
vm_tests:
|
||||
runs-on: ubuntu-22.04
|
||||
runs-on: ubuntu-24.04
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: DeterminateSystems/nix-installer-action@main
|
||||
@@ -206,7 +187,7 @@ jobs:
|
||||
|
||||
flake_regressions:
|
||||
needs: vm_tests
|
||||
runs-on: ubuntu-22.04
|
||||
runs-on: ubuntu-24.04
|
||||
steps:
|
||||
- name: Checkout nix
|
||||
uses: actions/checkout@v4
|
||||
|
||||
2
.github/workflows/labels.yml
vendored
2
.github/workflows/labels.yml
vendored
@@ -15,7 +15,7 @@ permissions:
|
||||
|
||||
jobs:
|
||||
labels:
|
||||
runs-on: ubuntu-latest
|
||||
runs-on: ubuntu-24.04
|
||||
if: github.repository_owner == 'NixOS'
|
||||
steps:
|
||||
- uses: actions/labeler@v5
|
||||
|
||||
122
.gitignore
vendored
122
.gitignore
vendored
@@ -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
|
||||
/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/test-tmp
|
||||
/tests/functional/common/subst-vars.sh
|
||||
/tests/functional/result*
|
||||
/tests/functional/restricted-innocent
|
||||
/tests/functional/shell
|
||||
/tests/functional/shell.drv
|
||||
/tests/functional/repl-result-out
|
||||
/tests/functional/debugger-test-out
|
||||
/tests/functional/test-libstoreconsumer/test-libstoreconsumer
|
||||
/tests/functional/nix-shell
|
||||
|
||||
# /tests/functional/lang/
|
||||
/tests/functional/lang/*.out
|
||||
@@ -112,27 +14,9 @@ perl/Makefile.config
|
||||
/tests/functional/lang/*.err
|
||||
/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/
|
||||
|
||||
*.a
|
||||
*.o
|
||||
*.o.tmp
|
||||
*.so
|
||||
*.dylib
|
||||
*.dll
|
||||
*.exe
|
||||
*.dep
|
||||
*~
|
||||
*.pc
|
||||
*.plist
|
||||
|
||||
# GNU Global
|
||||
GPATH
|
||||
@@ -147,8 +31,6 @@ GTAGS
|
||||
compile_commands.json
|
||||
*.compile_commands.json
|
||||
|
||||
nix-rust/target
|
||||
|
||||
result
|
||||
result-*
|
||||
|
||||
@@ -163,3 +45,5 @@ result-*
|
||||
|
||||
# Mac OS
|
||||
.DS_Store
|
||||
|
||||
flake-regressions
|
||||
|
||||
15
.mergify.yml
15
.mergify.yml
@@ -2,10 +2,11 @@ queue_rules:
|
||||
- name: default
|
||||
# all required tests need to go here
|
||||
merge_conditions:
|
||||
- check-success=tests (macos-latest)
|
||||
- check-success=tests (ubuntu-latest)
|
||||
- check-success=tests on macos
|
||||
- check-success=tests on ubuntu
|
||||
- check-success=installer test on macos
|
||||
- check-success=installer test on ubuntu
|
||||
- check-success=vm_tests
|
||||
merge_method: rebase
|
||||
batch_size: 5
|
||||
|
||||
pull_request_rules:
|
||||
@@ -26,6 +27,7 @@ pull_request_rules:
|
||||
branches:
|
||||
- 2.18-maintenance
|
||||
labels:
|
||||
- automatic backport
|
||||
- merge-queue
|
||||
|
||||
- name: backport patches to 2.19
|
||||
@@ -36,6 +38,7 @@ pull_request_rules:
|
||||
branches:
|
||||
- 2.19-maintenance
|
||||
labels:
|
||||
- automatic backport
|
||||
- merge-queue
|
||||
|
||||
- name: backport patches to 2.20
|
||||
@@ -46,6 +49,7 @@ pull_request_rules:
|
||||
branches:
|
||||
- 2.20-maintenance
|
||||
labels:
|
||||
- automatic backport
|
||||
- merge-queue
|
||||
|
||||
- name: backport patches to 2.21
|
||||
@@ -56,6 +60,7 @@ pull_request_rules:
|
||||
branches:
|
||||
- 2.21-maintenance
|
||||
labels:
|
||||
- automatic backport
|
||||
- merge-queue
|
||||
|
||||
- name: backport patches to 2.22
|
||||
@@ -66,6 +71,7 @@ pull_request_rules:
|
||||
branches:
|
||||
- 2.22-maintenance
|
||||
labels:
|
||||
- automatic backport
|
||||
- merge-queue
|
||||
|
||||
- name: backport patches to 2.23
|
||||
@@ -76,6 +82,7 @@ pull_request_rules:
|
||||
branches:
|
||||
- 2.23-maintenance
|
||||
labels:
|
||||
- automatic backport
|
||||
- merge-queue
|
||||
|
||||
- name: backport patches to 2.24
|
||||
@@ -86,6 +93,7 @@ pull_request_rules:
|
||||
branches:
|
||||
- "2.24-maintenance"
|
||||
labels:
|
||||
- automatic backport
|
||||
- merge-queue
|
||||
|
||||
- name: backport patches to 2.25
|
||||
@@ -96,4 +104,5 @@ pull_request_rules:
|
||||
branches:
|
||||
- "2.25-maintenance"
|
||||
labels:
|
||||
- automatic backport
|
||||
- merge-queue
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
|
||||
, meson
|
||||
, ninja
|
||||
, lowdown
|
||||
, lowdown-unsandboxed
|
||||
, mdbook
|
||||
, mdbook-linkcheck
|
||||
, jq
|
||||
@@ -42,7 +42,7 @@ mkMesonDerivation (finalAttrs: {
|
||||
passthru.externalNativeBuildInputs = [
|
||||
meson
|
||||
ninja
|
||||
(lib.getBin lowdown)
|
||||
(lib.getBin lowdown-unsandboxed)
|
||||
mdbook
|
||||
mdbook-linkcheck
|
||||
jq
|
||||
|
||||
@@ -1,18 +0,0 @@
|
||||
---
|
||||
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.
|
||||
@@ -130,6 +130,7 @@
|
||||
- [Contributing](development/contributing.md)
|
||||
- [Releases](release-notes/index.md)
|
||||
{{#include ./SUMMARY-rl-next.md}}
|
||||
- [Release 2.26 (2025-01-22)](release-notes/rl-2.26.md)
|
||||
- [Release 2.25 (2024-11-07)](release-notes/rl-2.25.md)
|
||||
- [Release 2.24 (2024-07-31)](release-notes/rl-2.24.md)
|
||||
- [Release 2.23 (2024-06-03)](release-notes/rl-2.23.md)
|
||||
|
||||
@@ -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.
|
||||
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 ./env-common.md}}
|
||||
|
||||
@@ -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:
|
||||
>
|
||||
> ```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"
|
||||
> $ "$storePath"/bin/hello
|
||||
> Hello, world!
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
[`--from-profile` *path*]
|
||||
[`--preserve-installed` | `-P`]
|
||||
[`--remove-all` | `-r`]
|
||||
[`--priority` *priority*]
|
||||
|
||||
# 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.
|
||||
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 derivations](@docroot@/glossary.md#gloss-store-derivation), then these are [realised], and the resulting output paths are installed.
|
||||
|
||||
- If *args* are [store paths] that are not store derivations, then these are [realised] and installed.
|
||||
@@ -235,4 +240,3 @@ channel:
|
||||
```console
|
||||
$ nix-env --file https://github.com/NixOS/nixpkgs/archive/nixos-14.12.tar.gz --install --attr firefox
|
||||
```
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
# Synopsis
|
||||
|
||||
`nix-instantiate`
|
||||
[`--parse` | `--eval` [`--strict`] [`--json`] [`--xml`] ]
|
||||
[`--parse` | `--eval` [`--strict`] [`--raw` | `--json` | `--xml`] ]
|
||||
[`--read-write-mode`]
|
||||
[`--arg` *name* *value*]
|
||||
[{`--attr`| `-A`} *attrPath*]
|
||||
@@ -102,6 +102,11 @@ standard input.
|
||||
> This option can cause non-termination, because lazy data
|
||||
> 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`
|
||||
|
||||
When used with `--eval`, print the resulting value as an JSON
|
||||
|
||||
@@ -21,6 +21,9 @@ This operation has the following options:
|
||||
Use recursive instead of flat hashing mode, used when adding
|
||||
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}}
|
||||
|
||||
@@ -11,6 +11,9 @@
|
||||
The operation `--add` adds the specified paths to the Nix store. It
|
||||
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}}
|
||||
|
||||
@@ -19,10 +19,11 @@ nix-build -E '(import ./.).packages.${builtins.currentSystem}.nix.doc'
|
||||
or
|
||||
|
||||
```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:
|
||||
|
||||
|
||||
@@ -297,7 +297,7 @@ Creating a Cachix cache for your installer tests and adding its authorisation to
|
||||
- `armv7l-linux`
|
||||
- `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
|
||||
|
||||
|
||||
@@ -160,6 +160,6 @@ which you may remove.
|
||||
To remove a [single-user installation](./installing-binary.md#single-user-installation) of Nix, run:
|
||||
|
||||
```console
|
||||
$ rm -rf /nix ~/.nix-channels ~/.nix-defexpr ~/.nix-profile
|
||||
rm -rf /nix ~/.nix-channels ~/.nix-defexpr ~/.nix-profile
|
||||
```
|
||||
You might also want to manually remove references to Nix from your `~/.profile`.
|
||||
|
||||
128
doc/manual/source/release-notes/rl-2.26.md
Normal file
128
doc/manual/source/release-notes/rl-2.26.md
Normal file
@@ -0,0 +1,128 @@
|
||||
# Release 2.26.0 (2025-01-22)
|
||||
|
||||
- Support for relative path inputs [#10089](https://github.com/NixOS/nix/pull/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.
|
||||
|
||||
- Flake lock file generation now ignores local registries [#12019](https://github.com/NixOS/nix/pull/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 }: { ... };
|
||||
}
|
||||
```
|
||||
|
||||
- `nix copy` supports `--profile` and `--out-link` [#11657](https://github.com/NixOS/nix/pull/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.
|
||||
|
||||
- `nix-instantiate --eval` now supports `--raw` [#12119](https://github.com/NixOS/nix/pull/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.
|
||||
|
||||
- Improved `NIX_SSHOPTS` parsing for better SSH option handling [#5181](https://github.com/NixOS/nix/issues/5181) [#12020](https://github.com/NixOS/nix/pull/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 commands 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.
|
||||
|
||||
- Nix is now built using Meson
|
||||
|
||||
As proposed in [RFC 132](https://github.com/NixOS/rfcs/pull/132), Nix's build system now uses Meson/Ninja. The old Make-based build system has been removed.
|
||||
|
||||
- Evaluation caching now works for dirty Git workdirs [#11992](https://github.com/NixOS/nix/pull/11992)
|
||||
|
||||
# Contributors
|
||||
|
||||
This release was made possible by the following 45 contributors:
|
||||
|
||||
- Anatoli Babenia [**(@abitrolly)**](https://github.com/abitrolly)
|
||||
- Domagoj Mišković [**(@allrealmsoflife)**](https://github.com/allrealmsoflife)
|
||||
- Yaroslav Bolyukin [**(@CertainLach)**](https://github.com/CertainLach)
|
||||
- bryango [**(@bryango)**](https://github.com/bryango)
|
||||
- tomberek [**(@tomberek)**](https://github.com/tomberek)
|
||||
- Matej Urbas [**(@mupdt)**](https://github.com/mupdt)
|
||||
- elikoga [**(@elikoga)**](https://github.com/elikoga)
|
||||
- wh0 [**(@wh0)**](https://github.com/wh0)
|
||||
- Félix [**(@picnoir)**](https://github.com/picnoir)
|
||||
- Valentin Gagarin [**(@fricklerhandwerk)**](https://github.com/fricklerhandwerk)
|
||||
- Gavin John [**(@Pandapip1)**](https://github.com/Pandapip1)
|
||||
- Travis A. Everett [**(@abathur)**](https://github.com/abathur)
|
||||
- Vladimir Panteleev [**(@CyberShadow)**](https://github.com/CyberShadow)
|
||||
- Ilja [**(@suruaku)**](https://github.com/suruaku)
|
||||
- Jason Yundt [**(@Jayman2000)**](https://github.com/Jayman2000)
|
||||
- Mike Kusold [**(@kusold)**](https://github.com/kusold)
|
||||
- Andy Hamon [**(@andrewhamon)**](https://github.com/andrewhamon)
|
||||
- Brian McKenna [**(@puffnfresh)**](https://github.com/puffnfresh)
|
||||
- Greg Curtis [**(@gcurtis)**](https://github.com/gcurtis)
|
||||
- Andrew Poelstra [**(@apoelstra)**](https://github.com/apoelstra)
|
||||
- Linus Heckemann [**(@lheckemann)**](https://github.com/lheckemann)
|
||||
- Tristan Ross [**(@RossComputerGuy)**](https://github.com/RossComputerGuy)
|
||||
- Dominique Martinet [**(@martinetd)**](https://github.com/martinetd)
|
||||
- h0nIg [**(@h0nIg)**](https://github.com/h0nIg)
|
||||
- Eelco Dolstra [**(@edolstra)**](https://github.com/edolstra)
|
||||
- Shahar "Dawn" Or [**(@mightyiam)**](https://github.com/mightyiam)
|
||||
- NAHO [**(@trueNAHO)**](https://github.com/trueNAHO)
|
||||
- Ryan Hendrickson [**(@rhendric)**](https://github.com/rhendric)
|
||||
- the-sun-will-rise-tomorrow [**(@the-sun-will-rise-tomorrow)**](https://github.com/the-sun-will-rise-tomorrow)
|
||||
- Connor Baker [**(@ConnorBaker)**](https://github.com/ConnorBaker)
|
||||
- Cole Helbling [**(@cole-h)**](https://github.com/cole-h)
|
||||
- Jack Wilsdon [**(@jackwilsdon)**](https://github.com/jackwilsdon)
|
||||
- rekcäH nitraM [**(@dwt)**](https://github.com/dwt)
|
||||
- Martin Fischer [**(@not-my-profile)**](https://github.com/not-my-profile)
|
||||
- John Ericson [**(@Ericson2314)**](https://github.com/Ericson2314)
|
||||
- Graham Christensen [**(@grahamc)**](https://github.com/grahamc)
|
||||
- Sergei Zimmerman [**(@xokdvium)**](https://github.com/xokdvium)
|
||||
- Siddarth Kumar [**(@siddarthkay)**](https://github.com/siddarthkay)
|
||||
- Sergei Trofimovich [**(@trofi)**](https://github.com/trofi)
|
||||
- Robert Hensing [**(@roberth)**](https://github.com/roberth)
|
||||
- Mutsuha Asada [**(@momeemt)**](https://github.com/momeemt)
|
||||
- Parker Jones [**(@knotapun)**](https://github.com/knotapun)
|
||||
- Jörg Thalheim [**(@Mic92)**](https://github.com/Mic92)
|
||||
- dbdr [**(@dbdr)**](https://github.com/dbdr)
|
||||
- myclevorname [**(@myclevorname)**](https://github.com/myclevorname)
|
||||
- Philipp Otterbein
|
||||
44
flake.lock
generated
44
flake.lock
generated
@@ -3,11 +3,11 @@
|
||||
"flake-compat": {
|
||||
"flake": false,
|
||||
"locked": {
|
||||
"lastModified": 1696426674,
|
||||
"narHash": "sha256-kvjfFW7WAETZlt09AgDn1MrtKzP7t90Vf7vypd3OL1U=",
|
||||
"lastModified": 1733328505,
|
||||
"narHash": "sha256-NeCCThCEP3eCl2l/+27kNNK7QrwZB1IJCrXfrbv5oqU=",
|
||||
"owner": "edolstra",
|
||||
"repo": "flake-compat",
|
||||
"rev": "0f9255e01c2351cc7d116c072cb317785dd33b33",
|
||||
"rev": "ff81ac966bb2cae68946d5ed5fc4994f96d0ffec",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
@@ -23,11 +23,11 @@
|
||||
]
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1719994518,
|
||||
"narHash": "sha256-pQMhCCHyQGRzdfAkdJ4cIWiw+JNuWsTX7f0ZYSyz0VY=",
|
||||
"lastModified": 1733312601,
|
||||
"narHash": "sha256-4pDvzqnegAfRkPwO3wmwBhVi/Sye1mzps0zHWYnP88c=",
|
||||
"owner": "hercules-ci",
|
||||
"repo": "flake-parts",
|
||||
"rev": "9227223f6d922fee3c7b190b2cc238a99527bbb7",
|
||||
"rev": "205b12d8b7cd4802fbcb8e8ef6a0f1408781a4f9",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
@@ -48,11 +48,11 @@
|
||||
]
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1721042469,
|
||||
"narHash": "sha256-6FPUl7HVtvRHCCBQne7Ylp4p+dpP3P/OYuzjztZ4s70=",
|
||||
"lastModified": 1734279981,
|
||||
"narHash": "sha256-NdaCraHPp8iYMWzdXAt5Nv6sA3MUzlCiGiR586TCwo0=",
|
||||
"owner": "cachix",
|
||||
"repo": "git-hooks.nix",
|
||||
"rev": "f451c19376071a90d8c58ab1a953c6e9840527fd",
|
||||
"rev": "aa9f40c906904ebd83da78e7f328cd8aeaeae785",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
@@ -61,35 +61,18 @@
|
||||
"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": {
|
||||
"locked": {
|
||||
"lastModified": 1723688146,
|
||||
"narHash": "sha256-sqLwJcHYeWLOeP/XoLwAtYjr01TISlkOfz+NG82pbdg=",
|
||||
"lastModified": 1734359947,
|
||||
"narHash": "sha256-1Noao/H+N8nFB4Beoy8fgwrcOQLVm9o4zKW1ODaqK9E=",
|
||||
"owner": "NixOS",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "c3d4ac725177c030b1e289015989da2ad9d56af0",
|
||||
"rev": "48d12d5e70ee91fe8481378e540433a7303dbf6a",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "NixOS",
|
||||
"ref": "nixos-24.05",
|
||||
"ref": "release-24.11",
|
||||
"repo": "nixpkgs",
|
||||
"type": "github"
|
||||
}
|
||||
@@ -131,7 +114,6 @@
|
||||
"flake-compat": "flake-compat",
|
||||
"flake-parts": "flake-parts",
|
||||
"git-hooks-nix": "git-hooks-nix",
|
||||
"libgit2": "libgit2",
|
||||
"nixpkgs": "nixpkgs",
|
||||
"nixpkgs-23-11": "nixpkgs-23-11",
|
||||
"nixpkgs-regression": "nixpkgs-regression"
|
||||
|
||||
27
flake.nix
27
flake.nix
@@ -1,11 +1,11 @@
|
||||
{
|
||||
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-23-11.url = "github:NixOS/nixpkgs/a62e6edd6d5e1fa0329b8653c801147986f8d446";
|
||||
inputs.flake-compat = { url = "github:edolstra/flake-compat"; flake = false; };
|
||||
inputs.libgit2 = { url = "github:libgit2/libgit2/v1.8.1"; flake = false; };
|
||||
|
||||
# dev tooling
|
||||
inputs.flake-parts.url = "github:hercules-ci/flake-parts";
|
||||
@@ -18,13 +18,13 @@
|
||||
inputs.git-hooks-nix.inputs.flake-compat.follows = "";
|
||||
inputs.git-hooks-nix.inputs.gitignore.follows = "";
|
||||
|
||||
outputs = inputs@{ self, nixpkgs, nixpkgs-regression, libgit2, ... }:
|
||||
outputs = inputs@{ self, nixpkgs, nixpkgs-regression, ... }:
|
||||
|
||||
|
||||
let
|
||||
inherit (nixpkgs) lib;
|
||||
|
||||
officialRelease = false;
|
||||
officialRelease = true;
|
||||
|
||||
linux32BitSystems = [ "i686-linux" ];
|
||||
linux64BitSystems = [ "x86_64-linux" "aarch64-linux" ];
|
||||
@@ -36,7 +36,8 @@
|
||||
"armv6l-unknown-linux-gnueabihf"
|
||||
"armv7l-unknown-linux-gnueabihf"
|
||||
"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-w64-mingw32"
|
||||
];
|
||||
@@ -106,6 +107,7 @@
|
||||
in {
|
||||
inherit stdenvs native;
|
||||
static = native.pkgsStatic;
|
||||
llvm = native.pkgsLLVM;
|
||||
cross = forAllCrossSystems (crossSystem: make-pkgs crossSystem "stdenv");
|
||||
});
|
||||
|
||||
@@ -163,7 +165,6 @@
|
||||
if prev.stdenv.hostPlatform.system == "i686-linux"
|
||||
then (prev.pre-commit.override (o: { dotnet-sdk = ""; })).overridePythonAttrs (o: { doCheck = false; })
|
||||
else prev.pre-commit;
|
||||
|
||||
};
|
||||
|
||||
in {
|
||||
@@ -186,7 +187,7 @@
|
||||
};
|
||||
|
||||
checks = forAllSystems (system: {
|
||||
binaryTarball = self.hydraJobs.binaryTarball.${system};
|
||||
installerScriptForGHA = self.hydraJobs.installerScriptForGHA.${system};
|
||||
installTests = self.hydraJobs.installTests.${system};
|
||||
nixpkgsLibTests = self.hydraJobs.tests.nixpkgsLibTests.${system};
|
||||
rl-next =
|
||||
@@ -201,11 +202,7 @@
|
||||
# Some perl dependencies are broken on i686-linux.
|
||||
# Since the support is only best-effort there, disable the perl
|
||||
# bindings
|
||||
|
||||
# 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};
|
||||
perlBindings = self.hydraJobs.perlBindings.${system};
|
||||
}
|
||||
# Add "passthru" tests
|
||||
// flatMapAttrs ({
|
||||
@@ -237,6 +234,8 @@
|
||||
inherit (nixpkgsFor.${system}.native)
|
||||
changelog-d;
|
||||
default = self.packages.${system}.nix;
|
||||
installerScriptForGHA = self.hydraJobs.installerScriptForGHA.${system};
|
||||
binaryTarball = self.hydraJobs.binaryTarball.${system};
|
||||
# TODO probably should be `nix-cli`
|
||||
nix = self.packages.${system}.nix-everything;
|
||||
nix-manual = nixpkgsFor.${system}.native.nixComponents.nix-manual;
|
||||
@@ -284,6 +283,7 @@
|
||||
# These attributes go right into `packages.<system>`.
|
||||
"${pkgName}" = nixpkgsFor.${system}.native.nixComponents.${pkgName};
|
||||
"${pkgName}-static" = nixpkgsFor.${system}.static.nixComponents.${pkgName};
|
||||
"${pkgName}-llvm" = nixpkgsFor.${system}.llvm.nixComponents.${pkgName};
|
||||
}
|
||||
// lib.optionalAttrs supportsCross (flatMapAttrs (lib.genAttrs crossSystems (_: { })) (crossSystem: {}: {
|
||||
# These attributes go right into `packages.<system>`.
|
||||
@@ -323,6 +323,9 @@
|
||||
prefixAttrs "static" (forAllStdenvs (stdenvName: makeShell {
|
||||
pkgs = nixpkgsFor.${system}.stdenvs."${stdenvName}Packages".pkgsStatic;
|
||||
})) //
|
||||
prefixAttrs "llvm" (forAllStdenvs (stdenvName: makeShell {
|
||||
pkgs = nixpkgsFor.${system}.stdenvs."${stdenvName}Packages".pkgsLLVM;
|
||||
})) //
|
||||
prefixAttrs "cross" (forAllCrossSystems (crossSystem: makeShell {
|
||||
pkgs = nixpkgsFor.${system}.cross.${crossSystem};
|
||||
}))
|
||||
|
||||
@@ -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)
|
||||
])
|
||||
])
|
||||
@@ -98,5 +98,39 @@
|
||||
"aks.kenji@protonmail.com": "a-kenji",
|
||||
"54070204+0x5a4@users.noreply.github.com": "0x5a4",
|
||||
"brian@bmcgee.ie": "brianmcgee",
|
||||
"squalus@squalus.net": "squalus"
|
||||
"squalus@squalus.net": "squalus",
|
||||
"kusold@users.noreply.github.com": "kusold",
|
||||
"37929162+mergify[bot]@users.noreply.github.com": "mergify[bot]",
|
||||
"ilja@mailbox.org": "suruaku",
|
||||
"and.ham95@gmail.com": "andrewhamon",
|
||||
"andy.hamon@discordapp.com": "andrewhamon",
|
||||
"siddarthkay@gmail.com": "siddarthkay",
|
||||
"apoelstra@wpsoftware.net": "apoelstra",
|
||||
"asmadeus@codewreck.org": "martinetd",
|
||||
"tristan.ross@midstall.com": "RossComputerGuy",
|
||||
"bryanlais@gmail.com": "bryango",
|
||||
"157494086+allrealmsoflife@users.noreply.github.com": "allrealmsoflife",
|
||||
"ConnorBaker01@gmail.com": "ConnorBaker",
|
||||
"me@momee.mt": "momeemt",
|
||||
"martin@push-f.com": "not-my-profile",
|
||||
"90870942+trueNAHO@users.noreply.github.com": "trueNAHO",
|
||||
"49885263+knotapun@users.noreply.github.com": "knotapun",
|
||||
"iam@lach.pw": "CertainLach",
|
||||
"elikowa@gmail.com": "elikoga",
|
||||
"greg.curtis@jetpack.io": "gcurtis",
|
||||
"git@sphalerite.org": "lheckemann",
|
||||
"mightyiampresence@gmail.com": "mightyiam",
|
||||
"spamfaenger@gmx.de": "dwt",
|
||||
"graham@grahamc.com": "grahamc",
|
||||
"wh0@users.noreply.github.com": "wh0",
|
||||
"25388474+mupdt@users.noreply.github.com": "mupdt",
|
||||
"anatoli@rainforce.org": "abitrolly",
|
||||
"h0nIg@users.noreply.github.com": "h0nIg",
|
||||
"CyberShadow@users.noreply.github.com": "CyberShadow",
|
||||
"gavinnjohn@gmail.com": "Pandapip1",
|
||||
"picnoir@alternativebit.fr": "picnoir",
|
||||
"140354451+myclevorname@users.noreply.github.com": "myclevorname",
|
||||
"bonniot@gmail.com": "dbdr",
|
||||
"jack@wilsdon.me": "jackwilsdon",
|
||||
"143541718+WxNzEMof@users.noreply.github.com": "the-sun-will-rise-tomorrow"
|
||||
}
|
||||
@@ -86,5 +86,37 @@
|
||||
"Aleksanaa": "Aleksana",
|
||||
"YorikSar": "Yuriy Taraday",
|
||||
"kjeremy": "Jeremy Kolb",
|
||||
"artemist": "Artemis Tosini"
|
||||
"artemist": "Artemis Tosini",
|
||||
"the-sun-will-rise-tomorrow": null,
|
||||
"gcurtis": "Greg Curtis",
|
||||
"ConnorBaker": "Connor Baker",
|
||||
"abitrolly": "Anatoli Babenia",
|
||||
"allrealmsoflife": "Domagoj Mi\u0161kovi\u0107",
|
||||
"andrewhamon": "Andy Hamon",
|
||||
"picnoir": "F\u00e9lix",
|
||||
"dbdr": null,
|
||||
"suruaku": "Ilja",
|
||||
"jackwilsdon": "Jack Wilsdon",
|
||||
"mergify[bot]": null,
|
||||
"kusold": "Mike Kusold",
|
||||
"lheckemann": "Linus Heckemann",
|
||||
"h0nIg": null,
|
||||
"grahamc": "Graham Christensen",
|
||||
"not-my-profile": "Martin Fischer",
|
||||
"CyberShadow": "Vladimir Panteleev",
|
||||
"Pandapip1": "Gavin John",
|
||||
"RossComputerGuy": "Tristan Ross",
|
||||
"elikoga": null,
|
||||
"martinetd": "Dominique Martinet",
|
||||
"knotapun": "Parker Jones",
|
||||
"mightyiam": "Shahar \"Dawn\" Or",
|
||||
"siddarthkay": "Siddarth Kumar",
|
||||
"apoelstra": "Andrew Poelstra",
|
||||
"myclevorname": null,
|
||||
"CertainLach": "Yaroslav Bolyukin",
|
||||
"trueNAHO": "NAHO",
|
||||
"wh0": null,
|
||||
"mupdt": "Matej Urbas",
|
||||
"momeemt": "Mutsuha Asada",
|
||||
"dwt": "\u202erekc\u00e4H nitraM\u202e"
|
||||
}
|
||||
@@ -10,8 +10,31 @@
|
||||
# https://flake.parts/options/git-hooks-nix#options
|
||||
pre-commit.settings = {
|
||||
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 = {
|
||||
enable = true;
|
||||
# https://github.com/cachix/git-hooks.nix/pull/532
|
||||
package = pkgs.llvmPackages_latest.clang-tools;
|
||||
excludes = [
|
||||
# We don't want to format test data
|
||||
# ''tests/(?!nixos/).*\.nix''
|
||||
@@ -354,6 +377,7 @@
|
||||
''^src/libutil/util\.cc$''
|
||||
''^src/libutil/util\.hh$''
|
||||
''^src/libutil/variant-wrapper\.hh$''
|
||||
''^src/libutil/widecharwidth/widechar_width\.h$'' # vendored source
|
||||
''^src/libutil/windows/file-descriptor\.cc$''
|
||||
''^src/libutil/windows/file-path\.cc$''
|
||||
''^src/libutil/windows/processes\.cc$''
|
||||
|
||||
@@ -16,7 +16,3 @@ add_project_arguments(
|
||||
'-Wno-deprecated-declarations',
|
||||
language : 'cpp',
|
||||
)
|
||||
|
||||
if get_option('buildtype') not in ['debug']
|
||||
add_project_arguments('-O3', language : 'cpp')
|
||||
endif
|
||||
|
||||
@@ -66,6 +66,27 @@ let
|
||||
|
||||
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 "" + lib.optionalString (
|
||||
!stdenv.hostPlatform.isWindows
|
||||
# build failure
|
||||
&& !stdenv.hostPlatform.isStatic
|
||||
# LTO breaks exception handling on x86-64-darwin.
|
||||
&& stdenv.system != "x86_64-darwin"
|
||||
) ''
|
||||
case "$mesonBuildType" in
|
||||
release|minsize) appendToVar mesonFlags "-Db_lto=true" ;;
|
||||
*) appendToVar mesonFlags "-Db_lto=false" ;;
|
||||
esac
|
||||
'';
|
||||
nativeBuildInputs = [
|
||||
pkgs.buildPackages.meson
|
||||
pkgs.buildPackages.ninja
|
||||
@@ -82,6 +103,12 @@ let
|
||||
];
|
||||
separateDebugInfo = !stdenv.hostPlatform.isStatic;
|
||||
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:
|
||||
@@ -114,14 +141,6 @@ scope: {
|
||||
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 {
|
||||
enableLargeConfig = true;
|
||||
};
|
||||
@@ -140,8 +159,6 @@ scope: {
|
||||
});
|
||||
|
||||
libgit2 = pkgs.libgit2.overrideAttrs (attrs: {
|
||||
src = inputs.libgit2;
|
||||
version = inputs.libgit2.lastModifiedDate;
|
||||
cmakeFlags = attrs.cmakeFlags or []
|
||||
++ [ "-DUSE_SSH=exec" ];
|
||||
nativeBuildInputs = attrs.nativeBuildInputs or []
|
||||
@@ -169,36 +186,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;
|
||||
|
||||
mkMesonDerivation =
|
||||
|
||||
@@ -42,27 +42,35 @@
|
||||
}:
|
||||
|
||||
let
|
||||
libs = {
|
||||
inherit
|
||||
nix-util
|
||||
nix-util-c
|
||||
nix-store
|
||||
nix-store-c
|
||||
nix-fetchers
|
||||
nix-expr
|
||||
nix-expr-c
|
||||
nix-flake
|
||||
nix-flake-c
|
||||
nix-main
|
||||
nix-main-c
|
||||
nix-cmd
|
||||
;
|
||||
} // lib.optionalAttrs (!stdenv.hostPlatform.isStatic && stdenv.buildPlatform.canExecute stdenv.hostPlatform) {
|
||||
# Currently fails in static build
|
||||
inherit
|
||||
nix-perl-bindings
|
||||
;
|
||||
};
|
||||
|
||||
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
|
||||
];
|
||||
libs = map lib.getDev (lib.attrValues libs);
|
||||
installPhase = ''
|
||||
mkdir -p $out/nix-support
|
||||
echo $libs >> $out/nix-support/propagated-build-inputs
|
||||
@@ -127,20 +135,16 @@ in
|
||||
nix-fetchers-tests.tests.run
|
||||
nix-flake-tests.tests.run
|
||||
|
||||
# Make sure the functional tests have passed
|
||||
nix-functional-tests
|
||||
|
||||
# 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 = [
|
||||
nix-functional-tests
|
||||
] ++ lib.optionals (!stdenv.hostPlatform.isStatic && stdenv.buildPlatform.canExecute stdenv.hostPlatform) [
|
||||
# Perl currently fails in static build
|
||||
# TODO: Split out tests into a separate derivation?
|
||||
nix-perl-bindings
|
||||
];
|
||||
passthru = prevAttrs.passthru // {
|
||||
inherit (nix-cli) version;
|
||||
@@ -162,21 +166,7 @@ in
|
||||
disallowedReferences = nix.all;
|
||||
```
|
||||
*/
|
||||
libs = {
|
||||
inherit
|
||||
nix-util
|
||||
nix-util-c
|
||||
nix-store
|
||||
nix-store-c
|
||||
nix-fetchers
|
||||
nix-expr
|
||||
nix-expr-c
|
||||
nix-flake
|
||||
nix-flake-c
|
||||
nix-main
|
||||
nix-main-c
|
||||
;
|
||||
};
|
||||
inherit libs;
|
||||
|
||||
tests = prevAttrs.passthru.tests or {} // {
|
||||
# TODO: create a proper fixpoint and:
|
||||
|
||||
@@ -123,15 +123,10 @@ in
|
||||
self.hydraJobs.binaryTarballCross."x86_64-linux"."armv7l-unknown-linux-gnueabihf"
|
||||
self.hydraJobs.binaryTarballCross."x86_64-linux"."riscv64-unknown-linux-gnu"
|
||||
];
|
||||
installerScriptForGHA = installScriptFor [
|
||||
# Native
|
||||
self.hydraJobs.binaryTarball."x86_64-linux"
|
||||
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"
|
||||
];
|
||||
|
||||
installerScriptForGHA = forAllSystems (system: nixpkgsFor.${system}.native.callPackage ../scripts/installer.nix {
|
||||
tarballs = [ self.hydraJobs.binaryTarball.${system} ];
|
||||
});
|
||||
|
||||
# docker image with Nix inside
|
||||
dockerImage = lib.genAttrs linux64BitSystems (system: self.packages.${system}.dockerImage);
|
||||
|
||||
@@ -65,7 +65,7 @@ runCommand "nix-binary-tarball-${version}" env ''
|
||||
fn=$out/$dir.tar.xz
|
||||
mkdir -p $out/nix-support
|
||||
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 \
|
||||
--mtime='1970-01-01' \
|
||||
--absolute-names \
|
||||
|
||||
6
scripts/build-checks
Executable file
6
scripts/build-checks
Executable 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; }"
|
||||
@@ -145,13 +145,28 @@ poly_user_id_get() {
|
||||
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() {
|
||||
dsclattr "/Users/$1" "IsHidden"
|
||||
}
|
||||
|
||||
poly_user_hidden_set() {
|
||||
_sudo "in order to make $1 a hidden user" \
|
||||
/usr/bin/dscl . -create "/Users/$1" "IsHidden" "1"
|
||||
dscl_create "in order to make $1 a hidden user" \
|
||||
"/Users/$1" "IsHidden" "1"
|
||||
}
|
||||
|
||||
poly_user_home_get() {
|
||||
@@ -161,8 +176,8 @@ poly_user_home_get() {
|
||||
poly_user_home_set() {
|
||||
# This can trigger a permission prompt now:
|
||||
# "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" \
|
||||
/usr/bin/dscl . -create "/Users/$1" "NFSHomeDirectory" "$2"
|
||||
dscl_create "in order to give $1 a safe home directory" \
|
||||
"/Users/$1" "NFSHomeDirectory" "$2"
|
||||
}
|
||||
|
||||
poly_user_note_get() {
|
||||
@@ -170,8 +185,8 @@ poly_user_note_get() {
|
||||
}
|
||||
|
||||
poly_user_note_set() {
|
||||
_sudo "in order to give $username a useful note" \
|
||||
/usr/bin/dscl . -create "/Users/$1" "RealName" "$2"
|
||||
dscl_create "in order to give $1 a useful note" \
|
||||
"/Users/$1" "RealName" "$2"
|
||||
}
|
||||
|
||||
poly_user_shell_get() {
|
||||
@@ -179,8 +194,8 @@ poly_user_shell_get() {
|
||||
}
|
||||
|
||||
poly_user_shell_set() {
|
||||
_sudo "in order to give $1 a safe shell" \
|
||||
/usr/bin/dscl . -create "/Users/$1" "UserShell" "$2"
|
||||
dscl_create "in order to give $1 a safe shell" \
|
||||
"/Users/$1" "UserShell" "$2"
|
||||
}
|
||||
|
||||
poly_user_in_group_check() {
|
||||
|
||||
@@ -56,6 +56,9 @@ readonly NIX_INSTALLED_CACERT="@cacert@"
|
||||
#readonly NIX_INSTALLED_CACERT="/nix/store/7dxhzymvy330i28ii676fl1pqwcahv2f-nss-cacert-3.49.2"
|
||||
readonly EXTRACTED_NIX_PATH="$(dirname "$0")"
|
||||
|
||||
# allow to override identity change command
|
||||
readonly NIX_BECOME=${NIX_BECOME:-sudo}
|
||||
|
||||
readonly ROOT_HOME=~root
|
||||
|
||||
if [ -t 0 ] && [ -z "${NIX_INSTALLER_YES:-}" ]; then
|
||||
@@ -123,7 +126,7 @@ uninstall_directions() {
|
||||
cat <<EOF
|
||||
$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
|
||||
opened while it existed.)
|
||||
@@ -136,7 +139,7 @@ EOF
|
||||
cat <<EOF
|
||||
$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.
|
||||
|
||||
@@ -343,7 +346,7 @@ __sudo() {
|
||||
|
||||
echo "I am executing:"
|
||||
echo ""
|
||||
printf " $ sudo %s\\n" "$cmd"
|
||||
printf " $ $NIX_BECOME %s\\n" "$cmd"
|
||||
echo ""
|
||||
echo "$expl"
|
||||
echo ""
|
||||
@@ -361,7 +364,9 @@ _sudo() {
|
||||
if is_root; then
|
||||
env "$@"
|
||||
else
|
||||
sudo "$@"
|
||||
# env sets environment variables for sudo alternatives
|
||||
# that don't support "VAR=value command" syntax
|
||||
$NIX_BECOME env "$@"
|
||||
fi
|
||||
}
|
||||
|
||||
@@ -557,7 +562,7 @@ create_build_user_for_core() {
|
||||
if [ "$actual_uid" != "$uid" ]; then
|
||||
failure <<EOF
|
||||
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.
|
||||
|
||||
If you already created the users and you know they start from
|
||||
|
||||
@@ -9,6 +9,8 @@ self="$(dirname "$0")"
|
||||
nix="@nix@"
|
||||
cacert="@cacert@"
|
||||
|
||||
# allow to override identity change command
|
||||
readonly NIX_BECOME="${NIX_BECOME:-sudo}"
|
||||
|
||||
if ! [ -e "$self/.reginfo" ]; then
|
||||
echo "$0: incomplete installer (.reginfo is missing)" >&2
|
||||
@@ -63,7 +65,6 @@ while [ $# -gt 0 ]; do
|
||||
exit 1
|
||||
fi
|
||||
INSTALL_MODE=no-daemon
|
||||
# intentional tail space
|
||||
ACTION=install
|
||||
;;
|
||||
--yes)
|
||||
@@ -135,8 +136,8 @@ echo "performing a single-user installation of Nix..." >&2
|
||||
|
||||
if ! [ -e "$dest" ]; then
|
||||
cmd="mkdir -m 0755 $dest && chown $USER $dest"
|
||||
echo "directory $dest does not exist; creating it by running '$cmd' using sudo" >&2
|
||||
if ! sudo sh -c "$cmd"; then
|
||||
echo "directory $dest does not exist; creating it by running '$cmd' using $NIX_BECOME" >&2
|
||||
if ! $NIX_BECOME sh -c "$cmd"; then
|
||||
echo "$0: please manually run '$cmd' as root to create $dest" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
set -e
|
||||
set -euo pipefail
|
||||
|
||||
script=$(nix-build -A outputs.hydraJobs.installerScriptForGHA --no-out-link)
|
||||
installerHash=$(echo "$script" | cut -b12-43 -)
|
||||
nix build -L ".#installerScriptForGHA" ".#binaryTarball"
|
||||
|
||||
installerURL=https://$CACHIX_NAME.cachix.org/serve/$installerHash/install
|
||||
|
||||
echo "::set-output name=installerURL::$installerURL"
|
||||
mkdir -p out
|
||||
cp ./result/install "out/install"
|
||||
name="$(basename "$(realpath ./result-1)")"
|
||||
# everything before the first dash
|
||||
cp -r ./result-1 "out/${name%%-*}"
|
||||
|
||||
@@ -2,6 +2,9 @@
|
||||
|
||||
set -eo pipefail
|
||||
|
||||
# stock path to avoid unexpected command versions
|
||||
PATH="$(/usr/bin/getconf PATH)"
|
||||
|
||||
((NEW_NIX_FIRST_BUILD_UID=351))
|
||||
((TEMP_NIX_FIRST_BUILD_UID=31000))
|
||||
|
||||
|
||||
22
scripts/serve-installer-for-github-actions
Executable file
22
scripts/serve-installer-for-github-actions
Executable 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"
|
||||
@@ -450,7 +450,7 @@ ref<eval_cache::EvalCache> openEvalCache(
|
||||
std::shared_ptr<flake::LockedFlake> lockedFlake)
|
||||
{
|
||||
auto fingerprint = evalSettings.useEvalCache && evalSettings.pureEval
|
||||
? lockedFlake->getFingerprint(state.store)
|
||||
? lockedFlake->getFingerprint(state.store, state.fetchSettings)
|
||||
: std::nullopt;
|
||||
auto rootLoader = [&state, lockedFlake]()
|
||||
{
|
||||
|
||||
@@ -16,13 +16,25 @@ static std::string doRenderMarkdownToTerminal(std::string_view markdown)
|
||||
{
|
||||
int windowWidth = getWindowSize().second;
|
||||
|
||||
struct lowdown_opts opts
|
||||
{
|
||||
.type = LOWDOWN_TERM,
|
||||
.maxdepth = 20,
|
||||
#if HAVE_LOWDOWN_1_4
|
||||
struct lowdown_opts_term opts_term {
|
||||
.cols = (size_t) std::max(windowWidth - 5, 60),
|
||||
.hmargin = 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,
|
||||
.oflags = LOWDOWN_TERM_NOLINK,
|
||||
};
|
||||
|
||||
@@ -4,8 +4,6 @@ project('nix-cmd', 'cpp',
|
||||
'cpp_std=c++2a',
|
||||
# TODO(Qyriad): increase the warning level
|
||||
'warning_level=1',
|
||||
'debug=true',
|
||||
'optimization=2',
|
||||
'errorlogs=true', # Please print logs for tests that fail
|
||||
],
|
||||
meson_version : '>= 1.1',
|
||||
@@ -36,6 +34,8 @@ deps_public += nlohmann_json
|
||||
lowdown = dependency('lowdown', version : '>= 0.9.0', required : get_option('markdown'))
|
||||
deps_private += lowdown
|
||||
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')
|
||||
if readline_flavor == 'editline'
|
||||
|
||||
@@ -76,10 +76,6 @@ mkMesonLibrary (finalAttrs: {
|
||||
(lib.mesonOption "readline-flavor" readlineFlavor)
|
||||
];
|
||||
|
||||
env = lib.optionalAttrs (stdenv.isLinux && !(stdenv.hostPlatform.isStatic && stdenv.system == "aarch64-linux")) {
|
||||
LDFLAGS = "-fuse-ld=gold";
|
||||
};
|
||||
|
||||
meta = {
|
||||
platforms = lib.platforms.unix ++ lib.platforms.windows;
|
||||
};
|
||||
|
||||
@@ -4,8 +4,6 @@ project('nix-expr-c', 'cpp',
|
||||
'cpp_std=c++2a',
|
||||
# TODO(Qyriad): increase the warning level
|
||||
'warning_level=1',
|
||||
'debug=true',
|
||||
'optimization=2',
|
||||
'errorlogs=true', # Please print logs for tests that fail
|
||||
],
|
||||
meson_version : '>= 1.1',
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
{ lib
|
||||
, stdenv
|
||||
, mkMesonLibrary
|
||||
|
||||
, nix-store-c
|
||||
@@ -47,10 +46,6 @@ mkMesonLibrary (finalAttrs: {
|
||||
mesonFlags = [
|
||||
];
|
||||
|
||||
env = lib.optionalAttrs (stdenv.isLinux && !(stdenv.hostPlatform.isStatic && stdenv.system == "aarch64-linux")) {
|
||||
LDFLAGS = "-fuse-ld=gold";
|
||||
};
|
||||
|
||||
meta = {
|
||||
platforms = lib.platforms.unix ++ lib.platforms.windows;
|
||||
};
|
||||
|
||||
@@ -4,8 +4,6 @@ project('nix-expr-test-support', 'cpp',
|
||||
'cpp_std=c++2a',
|
||||
# TODO(Qyriad): increase the warning level
|
||||
'warning_level=1',
|
||||
'debug=true',
|
||||
'optimization=2',
|
||||
'errorlogs=true', # Please print logs for tests that fail
|
||||
],
|
||||
meson_version : '>= 1.1',
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
{ lib
|
||||
, stdenv
|
||||
, mkMesonLibrary
|
||||
|
||||
, nix-store-test-support
|
||||
@@ -51,10 +50,6 @@ mkMesonLibrary (finalAttrs: {
|
||||
mesonFlags = [
|
||||
];
|
||||
|
||||
env = lib.optionalAttrs (stdenv.isLinux && !(stdenv.hostPlatform.isStatic && stdenv.system == "aarch64-linux")) {
|
||||
LDFLAGS = "-fuse-ld=gold";
|
||||
};
|
||||
|
||||
meta = {
|
||||
platforms = lib.platforms.unix ++ lib.platforms.windows;
|
||||
};
|
||||
|
||||
@@ -691,15 +691,15 @@ namespace nix {
|
||||
ASSERT_TRACE2("elemAt \"foo\" (-1)",
|
||||
TypeError,
|
||||
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)",
|
||||
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",
|
||||
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",
|
||||
TypeError,
|
||||
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 []",
|
||||
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",
|
||||
TypeError,
|
||||
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 []",
|
||||
Error,
|
||||
HintFmt("'tail' called on an empty list"));
|
||||
HintFmt("'builtins.tail' called on an empty list"));
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -4,8 +4,6 @@ project('nix-expr-tests', 'cpp',
|
||||
'cpp_std=c++2a',
|
||||
# TODO(Qyriad): increase the warning level
|
||||
'warning_level=1',
|
||||
'debug=true',
|
||||
'optimization=2',
|
||||
'errorlogs=true', # Please print logs for tests that fail
|
||||
],
|
||||
meson_version : '>= 1.1',
|
||||
|
||||
@@ -56,10 +56,6 @@ mkMesonExecutable (finalAttrs: {
|
||||
mesonFlags = [
|
||||
];
|
||||
|
||||
env = lib.optionalAttrs (stdenv.isLinux && !(stdenv.hostPlatform.isStatic && stdenv.system == "aarch64-linux")) {
|
||||
LDFLAGS = "-fuse-ld=gold";
|
||||
};
|
||||
|
||||
passthru = {
|
||||
tests = {
|
||||
run = runCommand "${finalAttrs.pname}-run" {
|
||||
|
||||
@@ -41,10 +41,17 @@ let
|
||||
(key: node:
|
||||
let
|
||||
|
||||
parentNode = allNodes.${getInputByPath lockFile.root node.parent};
|
||||
|
||||
sourceInfo =
|
||||
if overrides ? ${key}
|
||||
then
|
||||
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
|
||||
# FIXME: remove obsolete node.info.
|
||||
# Note: lock file entries are always final.
|
||||
|
||||
@@ -347,6 +347,16 @@ void EvalState::allowPath(const StorePath & 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)
|
||||
{
|
||||
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
|
||||
well. */
|
||||
if (hasPrefix(uri, "/")) {
|
||||
if (isAbsolute(uri)) {
|
||||
if (auto rootFS2 = rootFS.dynamic_pointer_cast<AllowListSourceAccessor>())
|
||||
rootFS2->checkAccess(CanonPath(uri));
|
||||
return;
|
||||
@@ -3099,10 +3109,7 @@ std::optional<SourcePath> EvalState::resolveLookupPathPath(const LookupPath::Pat
|
||||
allowPath(path.path.abs());
|
||||
if (store->isInStore(path.path.abs())) {
|
||||
try {
|
||||
StorePathSet closure;
|
||||
store->computeFSClosure(store->toStorePath(path.path.abs()).first, closure);
|
||||
for (auto & p : closure)
|
||||
allowPath(p);
|
||||
allowClosure(store->toStorePath(path.path.abs()).first);
|
||||
} catch (InvalidPath &) { }
|
||||
}
|
||||
}
|
||||
@@ -3178,12 +3185,16 @@ std::ostream & operator << (std::ostream & str, const ExternalValueBase & v) {
|
||||
return v.print(str);
|
||||
}
|
||||
|
||||
void forceNoNullByte(std::string_view s)
|
||||
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);
|
||||
throw Error("input string '%s' cannot be represented as Nix string because it contains null bytes", str);
|
||||
Error error("input string '%s' cannot be represented as Nix string because it contains null bytes", str);
|
||||
if (pos) {
|
||||
error.atPos(pos());
|
||||
}
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -400,6 +400,11 @@ public:
|
||||
*/
|
||||
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.
|
||||
*/
|
||||
|
||||
@@ -1,5 +1,13 @@
|
||||
#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 {
|
||||
|
||||
void initLoc(YYLTYPE * loc);
|
||||
|
||||
@@ -41,16 +41,18 @@ namespace nix {
|
||||
|
||||
// we make use of the fact that the parser receives a private copy of the input
|
||||
// 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 c;
|
||||
// the input string is terminated with *two* NULs, so we can safely take
|
||||
// *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 == '\\') {
|
||||
c = *s++;
|
||||
c = s[i++];
|
||||
if (c == 'n') *t = '\n';
|
||||
else if (c == 'r') *t = '\r';
|
||||
else if (c == 't') *t = '\t';
|
||||
@@ -59,12 +61,14 @@ static StringToken unescapeStr(SymbolTable & symbols, char * s, size_t length)
|
||||
else if (c == '\r') {
|
||||
/* Normalise CR and CR/LF into LF. */
|
||||
*t = '\n';
|
||||
if (*s == '\n') s++; /* cr/lf */
|
||||
if (s[i] == '\n') i++; /* cr/lf */
|
||||
}
|
||||
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)
|
||||
@@ -175,7 +179,7 @@ or { return OR_KW; }
|
||||
/* It is impossible to match strings ending with '$' with one
|
||||
regex because trailing contexts are only valid at the end
|
||||
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;
|
||||
}
|
||||
<STRING>\$\{ { PUSH_STATE(DEFAULT); return DOLLAR_CURLY; }
|
||||
@@ -191,6 +195,7 @@ or { return OR_KW; }
|
||||
\'\'(\ *\n)? { PUSH_STATE(IND_STRING); return IND_STRING_OPEN; }
|
||||
<IND_STRING>([^\$\']|\$[^\{\']|\'[^\'\$])+ {
|
||||
yylval->str = {yytext, (size_t) yyleng, true};
|
||||
forceNoNullByte(yylval->str, [&]() { return state->positions[CUR_POS]; });
|
||||
return IND_STR;
|
||||
}
|
||||
<IND_STRING>\'\'\$ |
|
||||
@@ -203,7 +208,7 @@ or { return OR_KW; }
|
||||
return IND_STR;
|
||||
}
|
||||
<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;
|
||||
}
|
||||
<IND_STRING>\$\{ { PUSH_STATE(DEFAULT); return DOLLAR_CURLY; }
|
||||
|
||||
@@ -4,8 +4,6 @@ project('nix-expr', 'cpp',
|
||||
'cpp_std=c++2a',
|
||||
# TODO(Qyriad): increase the warning level
|
||||
'warning_level=1',
|
||||
'debug=true',
|
||||
'optimization=2',
|
||||
'errorlogs=true', # Please print logs for tests that fail
|
||||
],
|
||||
meson_version : '>= 1.1',
|
||||
|
||||
@@ -168,7 +168,7 @@ struct ExprVar : Expr
|
||||
the set stored in the environment that is `level` levels up
|
||||
from the current one.*/
|
||||
Level level;
|
||||
Displacement displ;
|
||||
Displacement displ = 0;
|
||||
|
||||
ExprVar(Symbol name) : name(name) { };
|
||||
ExprVar(const PosIdx & pos, Symbol name) : pos(pos), name(name) { };
|
||||
@@ -242,7 +242,7 @@ struct ExprAttrs : Expr
|
||||
Kind kind;
|
||||
Expr * e;
|
||||
PosIdx pos;
|
||||
Displacement displ; // displacement
|
||||
Displacement displ = 0; // displacement
|
||||
AttrDef(Expr * e, const PosIdx & pos, Kind kind = Kind::Plain)
|
||||
: kind(kind), e(e), pos(pos) { };
|
||||
AttrDef() { };
|
||||
|
||||
@@ -96,8 +96,6 @@ mkMesonLibrary (finalAttrs: {
|
||||
# https://github.com/NixOS/nixpkgs/issues/86131.
|
||||
BOOST_INCLUDEDIR = "${lib.getDev boost}/include";
|
||||
BOOST_LIBRARYDIR = "${lib.getLib boost}/lib";
|
||||
} // lib.optionalAttrs (stdenv.isLinux && !(stdenv.hostPlatform.isStatic && stdenv.system == "aarch64-linux")) {
|
||||
LDFLAGS = "-fuse-ld=gold";
|
||||
};
|
||||
|
||||
meta = {
|
||||
|
||||
@@ -119,11 +119,9 @@ StringMap EvalState::realiseContext(const NixStringContext & context, StorePathS
|
||||
if (store != buildStore) copyClosure(*buildStore, *store, outputsToCopyAndAllow);
|
||||
|
||||
if (isIFD) {
|
||||
for (auto & outputPath : outputsToCopyAndAllow) {
|
||||
/* Add the output of this derivations to the allowed
|
||||
paths. */
|
||||
allowPath(outputPath);
|
||||
}
|
||||
/* Allow access to the output closures of this derivation. */
|
||||
for (auto & outputPath : outputsToCopyAndAllow)
|
||||
allowClosure(outputPath);
|
||||
}
|
||||
|
||||
return res;
|
||||
@@ -2047,7 +2045,7 @@ static RegisterPrimOp primop_readFileType({
|
||||
.args = {"p"},
|
||||
.doc = R"(
|
||||
Determine the directory entry type of a filesystem node, being
|
||||
one of "directory", "regular", "symlink", or "unknown".
|
||||
one of `"directory"`, `"regular"`, `"symlink"`, or `"unknown"`.
|
||||
)",
|
||||
.fun = prim_readFileType,
|
||||
});
|
||||
@@ -3259,23 +3257,19 @@ static RegisterPrimOp primop_isList({
|
||||
.fun = prim_isList,
|
||||
});
|
||||
|
||||
static void elemAt(EvalState & state, const PosIdx pos, Value & list, int n, Value & v)
|
||||
{
|
||||
state.forceList(list, pos, "while evaluating the first argument passed to builtins.elemAt");
|
||||
if (n < 0 || (unsigned int) n >= list.listSize())
|
||||
state.error<EvalError>(
|
||||
"list index %1% is out of bounds",
|
||||
n
|
||||
).atPos(pos).debugThrow();
|
||||
state.forceValue(*list.listElems()[n], pos);
|
||||
v = *list.listElems()[n];
|
||||
}
|
||||
|
||||
/* Return the n-1'th element of a list. */
|
||||
static void prim_elemAt(EvalState & state, const PosIdx pos, Value * * args, Value & v)
|
||||
{
|
||||
NixInt::Inner elem = state.forceInt(*args[1], pos, "while evaluating the second argument passed to builtins.elemAt").value;
|
||||
elemAt(state, pos, *args[0], elem, v);
|
||||
NixInt::Inner n = state.forceInt(*args[1], pos, "while evaluating the second argument passed to 'builtins.elemAt'").value;
|
||||
state.forceList(*args[0], pos, "while evaluating the first argument passed to 'builtins.elemAt'");
|
||||
if (n < 0 || (unsigned int) n >= args[0]->listSize())
|
||||
state.error<EvalError>(
|
||||
"'builtins.elemAt' called with index %d on a list of size %d",
|
||||
n,
|
||||
args[0]->listSize()
|
||||
).atPos(pos).debugThrow();
|
||||
state.forceValue(*args[0]->listElems()[n], pos);
|
||||
v = *args[0]->listElems()[n];
|
||||
}
|
||||
|
||||
static RegisterPrimOp primop_elemAt({
|
||||
@@ -3291,7 +3285,13 @@ static RegisterPrimOp primop_elemAt({
|
||||
/* Return the first element of a list. */
|
||||
static void prim_head(EvalState & state, const PosIdx pos, Value * * args, Value & v)
|
||||
{
|
||||
elemAt(state, pos, *args[0], 0, v);
|
||||
state.forceList(*args[0], pos, "while evaluating the first argument passed to 'builtins.head'");
|
||||
if (args[0]->listSize() == 0)
|
||||
state.error<EvalError>(
|
||||
"'builtins.head' called on an empty list"
|
||||
).atPos(pos).debugThrow();
|
||||
state.forceValue(*args[0]->listElems()[0], pos);
|
||||
v = *args[0]->listElems()[0];
|
||||
}
|
||||
|
||||
static RegisterPrimOp primop_head({
|
||||
@@ -3310,9 +3310,9 @@ static RegisterPrimOp primop_head({
|
||||
don't want to use it! */
|
||||
static void prim_tail(EvalState & state, const PosIdx pos, Value * * args, Value & v)
|
||||
{
|
||||
state.forceList(*args[0], pos, "while evaluating the first argument passed to builtins.tail");
|
||||
state.forceList(*args[0], pos, "while evaluating the first argument passed to 'builtins.tail'");
|
||||
if (args[0]->listSize() == 0)
|
||||
state.error<EvalError>("'tail' called on an empty list").atPos(pos).debugThrow();
|
||||
state.error<EvalError>("'builtins.tail' called on an empty list").atPos(pos).debugThrow();
|
||||
|
||||
auto list = state.buildList(args[0]->listSize() - 1);
|
||||
for (const auto & [n, v] : enumerate(list))
|
||||
@@ -4059,7 +4059,7 @@ static RegisterPrimOp primop_toString({
|
||||
});
|
||||
|
||||
/* `substring start len str' returns the substring of `str' starting
|
||||
at character position `min(start, stringLength str)' inclusive and
|
||||
at byte position `min(start, stringLength str)' inclusive and
|
||||
ending at `min(start + len, stringLength str)'. `start' must be
|
||||
non-negative. */
|
||||
static void prim_substring(EvalState & state, const PosIdx pos, Value * * args, Value & v)
|
||||
@@ -4098,7 +4098,7 @@ static RegisterPrimOp primop_substring({
|
||||
.name = "__substring",
|
||||
.args = {"start", "len", "s"},
|
||||
.doc = R"(
|
||||
Return the substring of *s* from character position *start*
|
||||
Return the substring of *s* from byte position *start*
|
||||
(zero-based) up to but not including *start + len*. If *start* is
|
||||
greater than the length of the string, an empty string is returned.
|
||||
If *start + len* lies beyond the end of the string or *len* is `-1`,
|
||||
|
||||
@@ -182,7 +182,7 @@ static void fetchTree(
|
||||
if (!state.settings.pureEval && !input.isDirect() && experimentalFeatureSettings.isEnabled(Xp::Flakes))
|
||||
input = lookupInRegistries(state.store, input).first;
|
||||
|
||||
if (state.settings.pureEval && !input.isLocked()) {
|
||||
if (state.settings.pureEval && !input.isConsideredLocked(state.fetchSettings)) {
|
||||
auto fetcher = "fetchTree";
|
||||
if (params.isFetchGit)
|
||||
fetcher = "fetchGit";
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
*/
|
||||
|
||||
#include <limits>
|
||||
#include <stddef.h>
|
||||
|
||||
namespace nix {
|
||||
|
||||
|
||||
@@ -108,7 +108,11 @@ json printValueAsJSON(EvalState & state, bool strict,
|
||||
void printValueAsJSON(EvalState & state, bool strict,
|
||||
Value & v, const PosIdx pos, std::ostream & str, NixStringContext & context, bool copyToStore)
|
||||
{
|
||||
str << printValueAsJSON(state, strict, v, pos, context, copyToStore);
|
||||
try {
|
||||
str << printValueAsJSON(state, strict, v, pos, context, copyToStore);
|
||||
} catch (nlohmann::json::exception & e) {
|
||||
throw JSONSerializationError("JSON serialization error: %s", e.what());
|
||||
}
|
||||
}
|
||||
|
||||
json ExternalValueBase::printValueAsJSON(EvalState & state, bool strict,
|
||||
|
||||
@@ -16,4 +16,7 @@ nlohmann::json printValueAsJSON(EvalState & state, bool strict,
|
||||
void printValueAsJSON(EvalState & state, bool strict,
|
||||
Value & v, const PosIdx pos, std::ostream & str, NixStringContext & context, bool copyToStore = true);
|
||||
|
||||
|
||||
MakeError(JSONSerializationError, Error);
|
||||
|
||||
}
|
||||
|
||||
@@ -510,6 +510,6 @@ typedef std::shared_ptr<Value *> RootValue;
|
||||
|
||||
RootValue allocRootValue(Value * v);
|
||||
|
||||
void forceNoNullByte(std::string_view s);
|
||||
void forceNoNullByte(std::string_view s, std::function<Pos()> = nullptr);
|
||||
|
||||
}
|
||||
|
||||
@@ -4,8 +4,6 @@ project('nix-fetchers-tests', 'cpp',
|
||||
'cpp_std=c++2a',
|
||||
# TODO(Qyriad): increase the warning level
|
||||
'warning_level=1',
|
||||
'debug=true',
|
||||
'optimization=2',
|
||||
'errorlogs=true', # Please print logs for tests that fail
|
||||
],
|
||||
meson_version : '>= 1.1',
|
||||
|
||||
@@ -54,10 +54,6 @@ mkMesonExecutable (finalAttrs: {
|
||||
mesonFlags = [
|
||||
];
|
||||
|
||||
env = lib.optionalAttrs (stdenv.isLinux && !(stdenv.hostPlatform.isStatic && stdenv.system == "aarch64-linux")) {
|
||||
LDFLAGS = "-fuse-ld=gold";
|
||||
};
|
||||
|
||||
passthru = {
|
||||
tests = {
|
||||
run = runCommand "${finalAttrs.pname}-run" {
|
||||
|
||||
@@ -70,6 +70,22 @@ struct Settings : public Config
|
||||
Setting<bool> warnDirty{this, true, "warn-dirty",
|
||||
"Whether to warn about dirty Git/Mercurial trees."};
|
||||
|
||||
Setting<bool> allowDirtyLocks{
|
||||
this,
|
||||
false,
|
||||
"allow-dirty-locks",
|
||||
R"(
|
||||
Whether to allow dirty inputs (such as dirty Git workdirs)
|
||||
to be locked via their NAR hash. This is generally bad
|
||||
practice since Nix has no way to obtain such inputs if they
|
||||
are subsequently modified. Therefore lock files with dirty
|
||||
locks should generally only be used for local testing, and
|
||||
should not be pushed to other users.
|
||||
)",
|
||||
{},
|
||||
true,
|
||||
Xp::Flakes};
|
||||
|
||||
Setting<bool> trustTarballsFromGitForges{
|
||||
this, true, "trust-tarballs-from-git-forges",
|
||||
R"(
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
#include "fetch-to-store.hh"
|
||||
#include "json-utils.hh"
|
||||
#include "store-path-accessor.hh"
|
||||
#include "fetch-settings.hh"
|
||||
|
||||
#include <nlohmann/json.hpp>
|
||||
|
||||
@@ -66,7 +67,7 @@ Input Input::fromURL(
|
||||
}
|
||||
}
|
||||
|
||||
throw Error("input '%s' is unsupported", url.url);
|
||||
throw Error("input '%s' is unsupported", url);
|
||||
}
|
||||
|
||||
Input Input::fromAttrs(const Settings & settings, Attrs && attrs)
|
||||
@@ -113,7 +114,15 @@ Input Input::fromAttrs(const Settings & settings, Attrs && attrs)
|
||||
|
||||
std::optional<std::string> Input::getFingerprint(ref<Store> store) const
|
||||
{
|
||||
return scheme ? scheme->getFingerprint(store, *this) : std::nullopt;
|
||||
if (!scheme) return std::nullopt;
|
||||
|
||||
if (cachedFingerprint) return *cachedFingerprint;
|
||||
|
||||
auto fingerprint = scheme->getFingerprint(store, *this);
|
||||
|
||||
cachedFingerprint = fingerprint;
|
||||
|
||||
return fingerprint;
|
||||
}
|
||||
|
||||
ParsedURL Input::toURL() const
|
||||
@@ -146,11 +155,23 @@ bool Input::isLocked() const
|
||||
return scheme && scheme->isLocked(*this);
|
||||
}
|
||||
|
||||
bool Input::isConsideredLocked(
|
||||
const Settings & settings) const
|
||||
{
|
||||
return isLocked() || (settings.allowDirtyLocks && getNarHash());
|
||||
}
|
||||
|
||||
bool Input::isFinal() const
|
||||
{
|
||||
return maybeGetBoolAttr(attrs, "__final").value_or(false);
|
||||
}
|
||||
|
||||
std::optional<std::string> Input::isRelative() const
|
||||
{
|
||||
assert(scheme);
|
||||
return scheme->isRelative(*this);
|
||||
}
|
||||
|
||||
Attrs Input::toAttrs() const
|
||||
{
|
||||
return attrs;
|
||||
@@ -307,7 +328,7 @@ std::pair<ref<SourceAccessor>, Input> Input::getAccessorUnchecked(ref<Store> sto
|
||||
|
||||
auto accessor = makeStorePathAccessor(store, storePath);
|
||||
|
||||
accessor->fingerprint = scheme->getFingerprint(store, *this);
|
||||
accessor->fingerprint = getFingerprint(store);
|
||||
|
||||
return {accessor, *this};
|
||||
} catch (Error & e) {
|
||||
@@ -318,7 +339,7 @@ std::pair<ref<SourceAccessor>, Input> Input::getAccessorUnchecked(ref<Store> sto
|
||||
auto [accessor, result] = scheme->getAccessor(store, *this);
|
||||
|
||||
assert(!accessor->fingerprint);
|
||||
accessor->fingerprint = scheme->getFingerprint(store, result);
|
||||
accessor->fingerprint = result.getFingerprint(store);
|
||||
|
||||
return {accessor, std::move(result)};
|
||||
}
|
||||
@@ -337,7 +358,7 @@ void Input::clone(const Path & destDir) const
|
||||
scheme->clone(*this, destDir);
|
||||
}
|
||||
|
||||
std::optional<Path> Input::getSourcePath() const
|
||||
std::optional<std::filesystem::path> Input::getSourcePath() const
|
||||
{
|
||||
assert(scheme);
|
||||
return scheme->getSourcePath(*this);
|
||||
@@ -440,7 +461,7 @@ Input InputScheme::applyOverrides(
|
||||
return input;
|
||||
}
|
||||
|
||||
std::optional<Path> InputScheme::getSourcePath(const Input & input) const
|
||||
std::optional<std::filesystem::path> InputScheme::getSourcePath(const Input & input) const
|
||||
{
|
||||
return {};
|
||||
}
|
||||
|
||||
@@ -42,9 +42,9 @@ struct Input
|
||||
Attrs attrs;
|
||||
|
||||
/**
|
||||
* path of the parent of this input, used for relative path resolution
|
||||
* Cached result of getFingerprint().
|
||||
*/
|
||||
std::optional<Path> parent;
|
||||
mutable std::optional<std::optional<std::string>> cachedFingerprint;
|
||||
|
||||
public:
|
||||
/**
|
||||
@@ -90,6 +90,21 @@ public:
|
||||
*/
|
||||
bool isLocked() const;
|
||||
|
||||
/**
|
||||
* Return whether the input is either locked, or, if
|
||||
* `allow-dirty-locks` is enabled, it has a NAR hash. In the
|
||||
* latter case, we can verify the input but we may not be able to
|
||||
* fetch it from anywhere.
|
||||
*/
|
||||
bool isConsideredLocked(
|
||||
const Settings & settings) const;
|
||||
|
||||
/**
|
||||
* Only for relative path flakes, i.e. 'path:./foo', returns the
|
||||
* relative path, i.e. './foo'.
|
||||
*/
|
||||
std::optional<std::string> isRelative() const;
|
||||
|
||||
/**
|
||||
* Return whether this is a "final" input, meaning that fetching
|
||||
* it will not add, remove or change any attributes. (See
|
||||
@@ -104,6 +119,11 @@ public:
|
||||
|
||||
bool operator ==(const Input & other) const noexcept;
|
||||
|
||||
bool operator <(const Input & other) const
|
||||
{
|
||||
return attrs < other.attrs;
|
||||
}
|
||||
|
||||
bool contains(const Input & other) const;
|
||||
|
||||
/**
|
||||
@@ -144,7 +164,7 @@ public:
|
||||
|
||||
void clone(const Path & destDir) const;
|
||||
|
||||
std::optional<Path> getSourcePath() const;
|
||||
std::optional<std::filesystem::path> getSourcePath() const;
|
||||
|
||||
/**
|
||||
* Write a file to this input, for input types that support
|
||||
@@ -227,7 +247,7 @@ struct InputScheme
|
||||
|
||||
virtual void clone(const Input & input, const Path & destDir) const;
|
||||
|
||||
virtual std::optional<Path> getSourcePath(const Input & input) const;
|
||||
virtual std::optional<std::filesystem::path> getSourcePath(const Input & input) const;
|
||||
|
||||
virtual void putFile(
|
||||
const Input & input,
|
||||
@@ -250,6 +270,9 @@ struct InputScheme
|
||||
|
||||
virtual bool isLocked(const Input & input) const
|
||||
{ return false; }
|
||||
|
||||
virtual std::optional<std::string> isRelative(const Input & input) const
|
||||
{ return std::nullopt; }
|
||||
};
|
||||
|
||||
void registerInputScheme(std::shared_ptr<InputScheme> && fetcher);
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
#include "signals.hh"
|
||||
#include "users.hh"
|
||||
#include "fs-sink.hh"
|
||||
#include "sync.hh"
|
||||
|
||||
#include <git2/attr.h>
|
||||
#include <git2/blob.h>
|
||||
@@ -437,7 +438,12 @@ struct GitRepoImpl : GitRepo, std::enable_shared_from_this<GitRepoImpl>
|
||||
{
|
||||
if (!(statusFlags & GIT_STATUS_INDEX_DELETED) &&
|
||||
!(statusFlags & GIT_STATUS_WT_DELETED))
|
||||
{
|
||||
info.files.insert(CanonPath(path));
|
||||
if (statusFlags != GIT_STATUS_CURRENT)
|
||||
info.dirtyFiles.insert(CanonPath(path));
|
||||
} else
|
||||
info.deletedFiles.insert(CanonPath(path));
|
||||
if (statusFlags != GIT_STATUS_CURRENT)
|
||||
info.isDirty = true;
|
||||
return 0;
|
||||
@@ -1262,4 +1268,17 @@ ref<GitRepo> getTarballCache()
|
||||
return GitRepo::openRepo(repoDir, true, true);
|
||||
}
|
||||
|
||||
GitRepo::WorkdirInfo GitRepo::getCachedWorkdirInfo(const std::filesystem::path & path)
|
||||
{
|
||||
static Sync<std::map<std::filesystem::path, WorkdirInfo>> _cache;
|
||||
{
|
||||
auto cache(_cache.lock());
|
||||
auto i = cache->find(path);
|
||||
if (i != cache->end()) return i->second;
|
||||
}
|
||||
auto workdirInfo = GitRepo::openRepo(path)->getWorkdirInfo();
|
||||
_cache.lock()->emplace(path, workdirInfo);
|
||||
return workdirInfo;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -59,12 +59,20 @@ struct GitRepo
|
||||
modified or added, but excluding deleted files. */
|
||||
std::set<CanonPath> files;
|
||||
|
||||
/* All modified or added files. */
|
||||
std::set<CanonPath> dirtyFiles;
|
||||
|
||||
/* The deleted files. */
|
||||
std::set<CanonPath> deletedFiles;
|
||||
|
||||
/* The submodules listed in .gitmodules of this workdir. */
|
||||
std::vector<Submodule> submodules;
|
||||
};
|
||||
|
||||
virtual WorkdirInfo getWorkdirInfo() = 0;
|
||||
|
||||
static WorkdirInfo getCachedWorkdirInfo(const std::filesystem::path & path);
|
||||
|
||||
/* Get the ref that HEAD points to. */
|
||||
virtual std::optional<std::string> getWorkdirRef() = 0;
|
||||
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
#include "finally.hh"
|
||||
#include "fetch-settings.hh"
|
||||
#include "json-utils.hh"
|
||||
#include "archive.hh"
|
||||
|
||||
#include <regex>
|
||||
#include <string.h>
|
||||
@@ -296,7 +297,7 @@ struct GitInputScheme : InputScheme
|
||||
|
||||
Strings args = {"clone"};
|
||||
|
||||
args.push_back(repoInfo.url);
|
||||
args.push_back(repoInfo.locationToArg());
|
||||
|
||||
if (auto ref = input.getRef()) {
|
||||
args.push_back("--branch");
|
||||
@@ -310,11 +311,9 @@ struct GitInputScheme : InputScheme
|
||||
runProgram("git", true, args, {}, true);
|
||||
}
|
||||
|
||||
std::optional<Path> getSourcePath(const Input & input) const override
|
||||
std::optional<std::filesystem::path> getSourcePath(const Input & input) const override
|
||||
{
|
||||
auto repoInfo = getRepoInfo(input);
|
||||
if (repoInfo.isLocal) return repoInfo.url;
|
||||
return std::nullopt;
|
||||
return getRepoInfo(input).getPath();
|
||||
}
|
||||
|
||||
void putFile(
|
||||
@@ -324,14 +323,15 @@ struct GitInputScheme : InputScheme
|
||||
std::optional<std::string> commitMsg) const override
|
||||
{
|
||||
auto repoInfo = getRepoInfo(input);
|
||||
if (!repoInfo.isLocal)
|
||||
auto repoPath = repoInfo.getPath();
|
||||
if (!repoPath)
|
||||
throw Error("cannot commit '%s' to Git repository '%s' because it's not a working tree", path, input.to_string());
|
||||
|
||||
writeFile((CanonPath(repoInfo.url) / path).abs(), contents);
|
||||
writeFile(*repoPath / path.rel(), contents);
|
||||
|
||||
auto result = runProgram(RunOptions {
|
||||
.program = "git",
|
||||
.args = {"-C", repoInfo.url, "--git-dir", repoInfo.gitDir, "check-ignore", "--quiet", std::string(path.rel())},
|
||||
.args = {"-C", repoPath->string(), "--git-dir", repoInfo.gitDir, "check-ignore", "--quiet", std::string(path.rel())},
|
||||
});
|
||||
auto exitCode =
|
||||
#ifndef WIN32 // TODO abstract over exit status handling on Windows
|
||||
@@ -344,7 +344,7 @@ struct GitInputScheme : InputScheme
|
||||
if (exitCode != 0) {
|
||||
// The path is not `.gitignore`d, we can add the file.
|
||||
runProgram("git", true,
|
||||
{ "-C", repoInfo.url, "--git-dir", repoInfo.gitDir, "add", "--intent-to-add", "--", std::string(path.rel()) });
|
||||
{ "-C", repoPath->string(), "--git-dir", repoInfo.gitDir, "add", "--intent-to-add", "--", std::string(path.rel()) });
|
||||
|
||||
|
||||
if (commitMsg) {
|
||||
@@ -352,7 +352,7 @@ struct GitInputScheme : InputScheme
|
||||
logger->pause();
|
||||
Finally restoreLogger([]() { logger->resume(); });
|
||||
runProgram("git", true,
|
||||
{ "-C", repoInfo.url, "--git-dir", repoInfo.gitDir, "commit", std::string(path.rel()), "-F", "-" },
|
||||
{ "-C", repoPath->string(), "--git-dir", repoInfo.gitDir, "commit", std::string(path.rel()), "-F", "-" },
|
||||
*commitMsg);
|
||||
}
|
||||
}
|
||||
@@ -360,24 +360,41 @@ struct GitInputScheme : InputScheme
|
||||
|
||||
struct RepoInfo
|
||||
{
|
||||
/* Whether this is a local, non-bare repository. */
|
||||
bool isLocal = false;
|
||||
/* Either the path of the repo (for local, non-bare repos), or
|
||||
the URL (which is never a `file` URL). */
|
||||
std::variant<std::filesystem::path, ParsedURL> location;
|
||||
|
||||
/* Working directory info: the complete list of files, and
|
||||
whether the working directory is dirty compared to HEAD. */
|
||||
GitRepo::WorkdirInfo workdirInfo;
|
||||
|
||||
/* URL of the repo, or its path if isLocal. Never a `file` URL. */
|
||||
std::string url;
|
||||
std::string locationToArg() const
|
||||
{
|
||||
return std::visit(
|
||||
overloaded {
|
||||
[&](const std::filesystem::path & path)
|
||||
{ return path.string(); },
|
||||
[&](const ParsedURL & url)
|
||||
{ return url.to_string(); }
|
||||
}, location);
|
||||
}
|
||||
|
||||
std::optional<std::filesystem::path> getPath() const
|
||||
{
|
||||
if (auto path = std::get_if<std::filesystem::path>(&location))
|
||||
return *path;
|
||||
else
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
void warnDirty(const Settings & settings) const
|
||||
{
|
||||
if (workdirInfo.isDirty) {
|
||||
if (!settings.allowDirty)
|
||||
throw Error("Git tree '%s' is dirty", url);
|
||||
throw Error("Git tree '%s' is dirty", locationToArg());
|
||||
|
||||
if (settings.warnDirty)
|
||||
warn("Git tree '%s' is dirty", url);
|
||||
warn("Git tree '%s' is dirty", locationToArg());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -424,18 +441,36 @@ struct GitInputScheme : InputScheme
|
||||
static bool forceHttp = getEnv("_NIX_FORCE_HTTP") == "1"; // for testing
|
||||
auto url = parseURL(getStrAttr(input.attrs, "url"));
|
||||
bool isBareRepository = url.scheme == "file" && !pathExists(url.path + "/.git");
|
||||
repoInfo.isLocal = url.scheme == "file" && !forceHttp && !isBareRepository;
|
||||
repoInfo.url = repoInfo.isLocal ? url.path : url.base;
|
||||
//
|
||||
// FIXME: here we turn a possibly relative path into an absolute path.
|
||||
// This allows relative git flake inputs to be resolved against the
|
||||
// **current working directory** (as in POSIX), which tends to work out
|
||||
// ok in the context of flakes, but is the wrong behavior,
|
||||
// as it should resolve against the flake.nix base directory instead.
|
||||
//
|
||||
// See: https://discourse.nixos.org/t/57783 and #9708
|
||||
//
|
||||
if (url.scheme == "file" && !forceHttp && !isBareRepository) {
|
||||
if (!isAbsolute(url.path)) {
|
||||
warn(
|
||||
"Fetching Git repository '%s', which uses a path relative to the current directory. "
|
||||
"This is not supported and will stop working in a future release. "
|
||||
"See https://github.com/NixOS/nix/issues/12281 for details.",
|
||||
url);
|
||||
}
|
||||
repoInfo.location = std::filesystem::absolute(url.path);
|
||||
} else
|
||||
repoInfo.location = url;
|
||||
|
||||
// If this is a local directory and no ref or revision is
|
||||
// given, then allow the use of an unclean working tree.
|
||||
if (!input.getRef() && !input.getRev() && repoInfo.isLocal)
|
||||
repoInfo.workdirInfo = GitRepo::openRepo(repoInfo.url)->getWorkdirInfo();
|
||||
if (auto repoPath = repoInfo.getPath(); !input.getRef() && !input.getRev() && repoPath)
|
||||
repoInfo.workdirInfo = GitRepo::getCachedWorkdirInfo(*repoPath);
|
||||
|
||||
return repoInfo;
|
||||
}
|
||||
|
||||
uint64_t getLastModified(const RepoInfo & repoInfo, const std::string & repoDir, const Hash & rev) const
|
||||
uint64_t getLastModified(const RepoInfo & repoInfo, const std::filesystem::path & repoDir, const Hash & rev) const
|
||||
{
|
||||
Cache::Key key{"gitLastModified", {{"rev", rev.gitRev()}}};
|
||||
|
||||
@@ -451,7 +486,7 @@ struct GitInputScheme : InputScheme
|
||||
return lastModified;
|
||||
}
|
||||
|
||||
uint64_t getRevCount(const RepoInfo & repoInfo, const std::string & repoDir, const Hash & rev) const
|
||||
uint64_t getRevCount(const RepoInfo & repoInfo, const std::filesystem::path & repoDir, const Hash & rev) const
|
||||
{
|
||||
Cache::Key key{"gitRevCount", {{"rev", rev.gitRev()}}};
|
||||
|
||||
@@ -460,7 +495,7 @@ struct GitInputScheme : InputScheme
|
||||
if (auto revCountAttrs = cache->lookup(key))
|
||||
return getIntAttr(*revCountAttrs, "revCount");
|
||||
|
||||
Activity act(*logger, lvlChatty, actUnknown, fmt("getting Git revision count of '%s'", repoInfo.url));
|
||||
Activity act(*logger, lvlChatty, actUnknown, fmt("getting Git revision count of '%s'", repoInfo.locationToArg()));
|
||||
|
||||
auto revCount = GitRepo::openRepo(repoDir)->getRevCount(rev);
|
||||
|
||||
@@ -471,11 +506,15 @@ struct GitInputScheme : InputScheme
|
||||
|
||||
std::string getDefaultRef(const RepoInfo & repoInfo) const
|
||||
{
|
||||
auto head = repoInfo.isLocal
|
||||
? GitRepo::openRepo(repoInfo.url)->getWorkdirRef()
|
||||
: readHeadCached(repoInfo.url);
|
||||
auto head = std::visit(
|
||||
overloaded {
|
||||
[&](const std::filesystem::path & path)
|
||||
{ return GitRepo::openRepo(path)->getWorkdirRef(); },
|
||||
[&](const ParsedURL & url)
|
||||
{ return readHeadCached(url.to_string()); }
|
||||
}, repoInfo.location);
|
||||
if (!head) {
|
||||
warn("could not read HEAD ref from repo at '%s', using 'master'", repoInfo.url);
|
||||
warn("could not read HEAD ref from repo at '%s', using 'master'", repoInfo.locationToArg());
|
||||
return "master";
|
||||
}
|
||||
return *head;
|
||||
@@ -518,29 +557,30 @@ struct GitInputScheme : InputScheme
|
||||
auto ref = originalRef ? *originalRef : getDefaultRef(repoInfo);
|
||||
input.attrs.insert_or_assign("ref", ref);
|
||||
|
||||
Path repoDir;
|
||||
std::filesystem::path repoDir;
|
||||
|
||||
if (repoInfo.isLocal) {
|
||||
repoDir = repoInfo.url;
|
||||
if (auto repoPath = repoInfo.getPath()) {
|
||||
repoDir = *repoPath;
|
||||
if (!input.getRev())
|
||||
input.attrs.insert_or_assign("rev", GitRepo::openRepo(repoDir)->resolveRef(ref).gitRev());
|
||||
} else {
|
||||
Path cacheDir = getCachePath(repoInfo.url, getShallowAttr(input));
|
||||
auto repoUrl = std::get<ParsedURL>(repoInfo.location);
|
||||
std::filesystem::path cacheDir = getCachePath(repoUrl.to_string(), getShallowAttr(input));
|
||||
repoDir = cacheDir;
|
||||
repoInfo.gitDir = ".";
|
||||
|
||||
createDirs(dirOf(cacheDir));
|
||||
PathLocks cacheDirLock({cacheDir});
|
||||
std::filesystem::create_directories(cacheDir.parent_path());
|
||||
PathLocks cacheDirLock({cacheDir.string()});
|
||||
|
||||
auto repo = GitRepo::openRepo(cacheDir, true, true);
|
||||
|
||||
// We need to set the origin so resolving submodule URLs works
|
||||
repo->setRemote("origin", repoInfo.url);
|
||||
repo->setRemote("origin", repoUrl.to_string());
|
||||
|
||||
Path localRefFile =
|
||||
auto localRefFile =
|
||||
ref.compare(0, 5, "refs/") == 0
|
||||
? cacheDir + "/" + ref
|
||||
: cacheDir + "/refs/heads/" + ref;
|
||||
? cacheDir / ref
|
||||
: cacheDir / "refs/heads" / ref;
|
||||
|
||||
bool doFetch;
|
||||
time_t now = time(0);
|
||||
@@ -556,7 +596,7 @@ struct GitInputScheme : InputScheme
|
||||
/* If the local ref is older than ‘tarball-ttl’ seconds, do a
|
||||
git fetch to update the local ref to the remote ref. */
|
||||
struct stat st;
|
||||
doFetch = stat(localRefFile.c_str(), &st) != 0 ||
|
||||
doFetch = stat(localRefFile.string().c_str(), &st) != 0 ||
|
||||
!isCacheFileWithinTtl(now, st);
|
||||
}
|
||||
}
|
||||
@@ -574,11 +614,11 @@ struct GitInputScheme : InputScheme
|
||||
? ref
|
||||
: "refs/heads/" + ref;
|
||||
|
||||
repo->fetch(repoInfo.url, fmt("%s:%s", fetchRef, fetchRef), getShallowAttr(input));
|
||||
repo->fetch(repoUrl.to_string(), fmt("%s:%s", fetchRef, fetchRef), getShallowAttr(input));
|
||||
} catch (Error & e) {
|
||||
if (!pathExists(localRefFile)) throw;
|
||||
if (!std::filesystem::exists(localRefFile)) throw;
|
||||
logError(e.info());
|
||||
warn("could not update local clone of Git repository '%s'; continuing with the most recent version", repoInfo.url);
|
||||
warn("could not update local clone of Git repository '%s'; continuing with the most recent version", repoInfo.locationToArg());
|
||||
}
|
||||
|
||||
try {
|
||||
@@ -587,8 +627,8 @@ struct GitInputScheme : InputScheme
|
||||
} catch (Error & e) {
|
||||
warn("could not update mtime for file '%s': %s", localRefFile, e.info().msg);
|
||||
}
|
||||
if (!originalRef && !storeCachedHead(repoInfo.url, ref))
|
||||
warn("could not update cached head '%s' for '%s'", ref, repoInfo.url);
|
||||
if (!originalRef && !storeCachedHead(repoUrl.to_string(), ref))
|
||||
warn("could not update cached head '%s' for '%s'", ref, repoInfo.locationToArg());
|
||||
}
|
||||
|
||||
if (auto rev = input.getRev()) {
|
||||
@@ -600,8 +640,7 @@ struct GitInputScheme : InputScheme
|
||||
"allRefs = true;" ANSI_NORMAL " to " ANSI_BOLD "fetchGit" ANSI_NORMAL ".",
|
||||
rev->gitRev(),
|
||||
ref,
|
||||
repoInfo.url
|
||||
);
|
||||
repoInfo.locationToArg());
|
||||
} else
|
||||
input.attrs.insert_or_assign("rev", repo->resolveRef(ref).gitRev());
|
||||
|
||||
@@ -613,7 +652,7 @@ struct GitInputScheme : InputScheme
|
||||
auto isShallow = repo->isShallow();
|
||||
|
||||
if (isShallow && !getShallowAttr(input))
|
||||
throw Error("'%s' is a shallow Git repository, but shallow repositories are only allowed when `shallow = true;` is specified", repoInfo.url);
|
||||
throw Error("'%s' is a shallow Git repository, but shallow repositories are only allowed when `shallow = true;` is specified", repoInfo.locationToArg());
|
||||
|
||||
// FIXME: check whether rev is an ancestor of ref?
|
||||
|
||||
@@ -628,7 +667,7 @@ struct GitInputScheme : InputScheme
|
||||
infoAttrs.insert_or_assign("revCount",
|
||||
getRevCount(repoInfo, repoDir, rev));
|
||||
|
||||
printTalkative("using revision %s of repo '%s'", rev.gitRev(), repoInfo.url);
|
||||
printTalkative("using revision %s of repo '%s'", rev.gitRev(), repoInfo.locationToArg());
|
||||
|
||||
verifyCommit(input, repo);
|
||||
|
||||
@@ -682,21 +721,23 @@ struct GitInputScheme : InputScheme
|
||||
RepoInfo & repoInfo,
|
||||
Input && input) const
|
||||
{
|
||||
auto repoPath = repoInfo.getPath().value();
|
||||
|
||||
if (getSubmodulesAttr(input))
|
||||
/* Create mountpoints for the submodules. */
|
||||
for (auto & submodule : repoInfo.workdirInfo.submodules)
|
||||
repoInfo.workdirInfo.files.insert(submodule.path);
|
||||
|
||||
auto repo = GitRepo::openRepo(repoInfo.url, false, false);
|
||||
auto repo = GitRepo::openRepo(repoPath, false, false);
|
||||
|
||||
auto exportIgnore = getExportIgnoreAttr(input);
|
||||
|
||||
ref<SourceAccessor> accessor =
|
||||
repo->getAccessor(repoInfo.workdirInfo,
|
||||
exportIgnore,
|
||||
makeNotAllowedError(repoInfo.url));
|
||||
makeNotAllowedError(repoInfo.locationToArg()));
|
||||
|
||||
accessor->setPathDisplay(repoInfo.url);
|
||||
accessor->setPathDisplay(repoInfo.locationToArg());
|
||||
|
||||
/* If the repo has submodules, return a mounted input accessor
|
||||
consisting of the accessor for the top-level repo and the
|
||||
@@ -705,10 +746,10 @@ struct GitInputScheme : InputScheme
|
||||
std::map<CanonPath, nix::ref<SourceAccessor>> mounts;
|
||||
|
||||
for (auto & submodule : repoInfo.workdirInfo.submodules) {
|
||||
auto submodulePath = CanonPath(repoInfo.url) / submodule.path;
|
||||
auto submodulePath = repoPath / submodule.path.rel();
|
||||
fetchers::Attrs attrs;
|
||||
attrs.insert_or_assign("type", "git");
|
||||
attrs.insert_or_assign("url", submodulePath.abs());
|
||||
attrs.insert_or_assign("url", submodulePath.string());
|
||||
attrs.insert_or_assign("exportIgnore", Explicit<bool>{ exportIgnore });
|
||||
attrs.insert_or_assign("submodules", Explicit<bool>{ true });
|
||||
// TODO: fall back to getAccessorFromCommit-like fetch when submodules aren't checked out
|
||||
@@ -732,7 +773,7 @@ struct GitInputScheme : InputScheme
|
||||
}
|
||||
|
||||
if (!repoInfo.workdirInfo.isDirty) {
|
||||
auto repo = GitRepo::openRepo(repoInfo.url);
|
||||
auto repo = GitRepo::openRepo(repoPath);
|
||||
|
||||
if (auto ref = repo->getWorkdirRef())
|
||||
input.attrs.insert_or_assign("ref", *ref);
|
||||
@@ -742,7 +783,7 @@ struct GitInputScheme : InputScheme
|
||||
|
||||
input.attrs.insert_or_assign("rev", rev.gitRev());
|
||||
input.attrs.insert_or_assign("revCount",
|
||||
rev == nullRev ? 0 : getRevCount(repoInfo, repoInfo.url, rev));
|
||||
rev == nullRev ? 0 : getRevCount(repoInfo, repoPath, rev));
|
||||
|
||||
verifyCommit(input, repo);
|
||||
} else {
|
||||
@@ -761,7 +802,7 @@ struct GitInputScheme : InputScheme
|
||||
input.attrs.insert_or_assign(
|
||||
"lastModified",
|
||||
repoInfo.workdirInfo.headRev
|
||||
? getLastModified(repoInfo, repoInfo.url, *repoInfo.workdirInfo.headRev)
|
||||
? getLastModified(repoInfo, repoPath, *repoInfo.workdirInfo.headRev)
|
||||
: 0);
|
||||
|
||||
return {accessor, std::move(input)};
|
||||
@@ -784,7 +825,7 @@ struct GitInputScheme : InputScheme
|
||||
}
|
||||
|
||||
auto [accessor, final] =
|
||||
input.getRef() || input.getRev() || !repoInfo.isLocal
|
||||
input.getRef() || input.getRev() || !repoInfo.getPath()
|
||||
? getAccessorFromCommit(store, repoInfo, std::move(input))
|
||||
: getAccessorFromWorkdir(store, repoInfo, std::move(input));
|
||||
|
||||
@@ -793,10 +834,33 @@ struct GitInputScheme : InputScheme
|
||||
|
||||
std::optional<std::string> getFingerprint(ref<Store> store, const Input & input) const override
|
||||
{
|
||||
auto makeFingerprint = [&](const Hash & rev)
|
||||
{
|
||||
return rev.gitRev() + (getSubmodulesAttr(input) ? ";s" : "") + (getExportIgnoreAttr(input) ? ";e" : "");
|
||||
};
|
||||
|
||||
if (auto rev = input.getRev())
|
||||
return rev->gitRev() + (getSubmodulesAttr(input) ? ";s" : "") + (getExportIgnoreAttr(input) ? ";e" : "");
|
||||
else
|
||||
return makeFingerprint(*rev);
|
||||
else {
|
||||
auto repoInfo = getRepoInfo(input);
|
||||
if (auto repoPath = repoInfo.getPath(); repoPath && repoInfo.workdirInfo.headRev && repoInfo.workdirInfo.submodules.empty()) {
|
||||
/* Calculate a fingerprint that takes into account the
|
||||
deleted and modified/added files. */
|
||||
HashSink hashSink{HashAlgorithm::SHA512};
|
||||
for (auto & file : repoInfo.workdirInfo.dirtyFiles) {
|
||||
writeString("modified:", hashSink);
|
||||
writeString(file.abs(), hashSink);
|
||||
dumpPath((*repoPath / file.rel()).string(), hashSink);
|
||||
}
|
||||
for (auto & file : repoInfo.workdirInfo.deletedFiles) {
|
||||
writeString("deleted:", hashSink);
|
||||
writeString(file.abs(), hashSink);
|
||||
}
|
||||
return makeFingerprint(*repoInfo.workdirInfo.headRev)
|
||||
+ ";d=" + hashSink.finish().first.to_string(HashFormat::Base16, false);
|
||||
}
|
||||
return std::nullopt;
|
||||
}
|
||||
}
|
||||
|
||||
bool isLocked(const Input & input) const override
|
||||
|
||||
@@ -50,7 +50,7 @@ struct GitArchiveInputScheme : InputScheme
|
||||
else if (std::regex_match(path[2], refRegex))
|
||||
ref = path[2];
|
||||
else
|
||||
throw BadURL("in URL '%s', '%s' is not a commit hash or branch/tag name", url.url, path[2]);
|
||||
throw BadURL("in URL '%s', '%s' is not a commit hash or branch/tag name", url, path[2]);
|
||||
} else if (size > 3) {
|
||||
std::string rs;
|
||||
for (auto i = std::next(path.begin(), 2); i != path.end(); i++) {
|
||||
@@ -63,34 +63,34 @@ struct GitArchiveInputScheme : InputScheme
|
||||
if (std::regex_match(rs, refRegex)) {
|
||||
ref = rs;
|
||||
} else {
|
||||
throw BadURL("in URL '%s', '%s' is not a branch/tag name", url.url, rs);
|
||||
throw BadURL("in URL '%s', '%s' is not a branch/tag name", url, rs);
|
||||
}
|
||||
} else if (size < 2)
|
||||
throw BadURL("URL '%s' is invalid", url.url);
|
||||
throw BadURL("URL '%s' is invalid", url);
|
||||
|
||||
for (auto &[name, value] : url.query) {
|
||||
if (name == "rev") {
|
||||
if (rev)
|
||||
throw BadURL("URL '%s' contains multiple commit hashes", url.url);
|
||||
throw BadURL("URL '%s' contains multiple commit hashes", url);
|
||||
rev = Hash::parseAny(value, HashAlgorithm::SHA1);
|
||||
}
|
||||
else if (name == "ref") {
|
||||
if (!std::regex_match(value, refRegex))
|
||||
throw BadURL("URL '%s' contains an invalid branch/tag name", url.url);
|
||||
throw BadURL("URL '%s' contains an invalid branch/tag name", url);
|
||||
if (ref)
|
||||
throw BadURL("URL '%s' contains multiple branch/tag names", url.url);
|
||||
throw BadURL("URL '%s' contains multiple branch/tag names", url);
|
||||
ref = value;
|
||||
}
|
||||
else if (name == "host") {
|
||||
if (!std::regex_match(value, hostRegex))
|
||||
throw BadURL("URL '%s' contains an invalid instance host", url.url);
|
||||
throw BadURL("URL '%s' contains an invalid instance host", url);
|
||||
host_url = value;
|
||||
}
|
||||
// FIXME: barf on unsupported attributes
|
||||
}
|
||||
|
||||
if (ref && rev)
|
||||
throw BadURL("URL '%s' contains both a commit hash and a branch/tag name %s %s", url.url, *ref, rev->gitRev());
|
||||
throw BadURL("URL '%s' contains both a commit hash and a branch/tag name %s %s", url, *ref, rev->gitRev());
|
||||
|
||||
Input input{settings};
|
||||
input.attrs.insert_or_assign("type", std::string { schemeName() });
|
||||
|
||||
@@ -26,16 +26,16 @@ struct IndirectInputScheme : InputScheme
|
||||
else if (std::regex_match(path[1], refRegex))
|
||||
ref = path[1];
|
||||
else
|
||||
throw BadURL("in flake URL '%s', '%s' is not a commit hash or branch/tag name", url.url, path[1]);
|
||||
throw BadURL("in flake URL '%s', '%s' is not a commit hash or branch/tag name", url, path[1]);
|
||||
} else if (path.size() == 3) {
|
||||
if (!std::regex_match(path[1], refRegex))
|
||||
throw BadURL("in flake URL '%s', '%s' is not a branch/tag name", url.url, path[1]);
|
||||
throw BadURL("in flake URL '%s', '%s' is not a branch/tag name", url, path[1]);
|
||||
ref = path[1];
|
||||
if (!std::regex_match(path[2], revRegex))
|
||||
throw BadURL("in flake URL '%s', '%s' is not a commit hash", url.url, path[2]);
|
||||
throw BadURL("in flake URL '%s', '%s' is not a commit hash", url, path[2]);
|
||||
rev = Hash::parseAny(path[2], HashAlgorithm::SHA1);
|
||||
} else
|
||||
throw BadURL("GitHub URL '%s' is invalid", url.url);
|
||||
throw BadURL("GitHub URL '%s' is invalid", url);
|
||||
|
||||
std::string id = path[0];
|
||||
if (!std::regex_match(id, flakeRegex))
|
||||
|
||||
@@ -126,7 +126,7 @@ struct MercurialInputScheme : InputScheme
|
||||
return res;
|
||||
}
|
||||
|
||||
std::optional<Path> getSourcePath(const Input & input) const override
|
||||
std::optional<std::filesystem::path> getSourcePath(const Input & input) const override
|
||||
{
|
||||
auto url = parseURL(getStrAttr(input.attrs, "url"));
|
||||
if (url.scheme == "file" && !input.getRef() && !input.getRev())
|
||||
@@ -161,7 +161,7 @@ struct MercurialInputScheme : InputScheme
|
||||
{
|
||||
auto url = parseURL(getStrAttr(input.attrs, "url"));
|
||||
bool isLocal = url.scheme == "file";
|
||||
return {isLocal, isLocal ? url.path : url.base};
|
||||
return {isLocal, isLocal ? url.path : url.to_string()};
|
||||
}
|
||||
|
||||
StorePath fetchToStore(ref<Store> store, Input & input) const
|
||||
|
||||
@@ -4,8 +4,6 @@ project('nix-fetchers', 'cpp',
|
||||
'cpp_std=c++2a',
|
||||
# TODO(Qyriad): increase the warning level
|
||||
'warning_level=1',
|
||||
'debug=true',
|
||||
'optimization=2',
|
||||
'errorlogs=true', # Please print logs for tests that fail
|
||||
],
|
||||
meson_version : '>= 1.1',
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
{ lib
|
||||
, stdenv
|
||||
, mkMesonLibrary
|
||||
|
||||
, nix-util
|
||||
@@ -49,10 +48,6 @@ mkMesonLibrary (finalAttrs: {
|
||||
echo ${version} > ../../.version
|
||||
'';
|
||||
|
||||
env = lib.optionalAttrs (stdenv.isLinux && !(stdenv.hostPlatform.isStatic && stdenv.system == "aarch64-linux")) {
|
||||
LDFLAGS = "-fuse-ld=gold";
|
||||
};
|
||||
|
||||
meta = {
|
||||
platforms = lib.platforms.unix ++ lib.platforms.windows;
|
||||
};
|
||||
|
||||
@@ -14,7 +14,7 @@ struct PathInputScheme : InputScheme
|
||||
if (url.scheme != "path") return {};
|
||||
|
||||
if (url.authority && *url.authority != "")
|
||||
throw Error("path URL '%s' should not have an authority ('%s')", url.url, *url.authority);
|
||||
throw Error("path URL '%s' should not have an authority ('%s')", url, *url.authority);
|
||||
|
||||
Input input{settings};
|
||||
input.attrs.insert_or_assign("type", "path");
|
||||
@@ -27,10 +27,10 @@ struct PathInputScheme : InputScheme
|
||||
if (auto n = string2Int<uint64_t>(value))
|
||||
input.attrs.insert_or_assign(name, *n);
|
||||
else
|
||||
throw Error("path URL '%s' has invalid parameter '%s'", url.to_string(), name);
|
||||
throw Error("path URL '%s' has invalid parameter '%s'", url, name);
|
||||
}
|
||||
else
|
||||
throw Error("path URL '%s' has unsupported parameter '%s'", url.to_string(), name);
|
||||
throw Error("path URL '%s' has unsupported parameter '%s'", url, name);
|
||||
|
||||
return input;
|
||||
}
|
||||
@@ -80,9 +80,9 @@ struct PathInputScheme : InputScheme
|
||||
};
|
||||
}
|
||||
|
||||
std::optional<Path> getSourcePath(const Input & input) const override
|
||||
std::optional<std::filesystem::path> getSourcePath(const Input & input) const override
|
||||
{
|
||||
return getStrAttr(input.attrs, "path");
|
||||
return getAbsPath(input);
|
||||
}
|
||||
|
||||
void putFile(
|
||||
@@ -91,13 +91,13 @@ struct PathInputScheme : InputScheme
|
||||
std::string_view contents,
|
||||
std::optional<std::string> commitMsg) const override
|
||||
{
|
||||
writeFile((CanonPath(getAbsPath(input)) / path).abs(), contents);
|
||||
writeFile(getAbsPath(input) / path.rel(), contents);
|
||||
}
|
||||
|
||||
std::optional<std::string> isRelative(const Input & input) const
|
||||
std::optional<std::string> isRelative(const Input & input) const override
|
||||
{
|
||||
auto path = getStrAttr(input.attrs, "path");
|
||||
if (hasPrefix(path, "/"))
|
||||
if (isAbsolute(path))
|
||||
return std::nullopt;
|
||||
else
|
||||
return path;
|
||||
@@ -108,12 +108,12 @@ struct PathInputScheme : InputScheme
|
||||
return (bool) input.getNarHash();
|
||||
}
|
||||
|
||||
CanonPath getAbsPath(const Input & input) const
|
||||
std::filesystem::path getAbsPath(const Input & input) const
|
||||
{
|
||||
auto path = getStrAttr(input.attrs, "path");
|
||||
|
||||
if (path[0] == '/')
|
||||
return CanonPath(path);
|
||||
if (isAbsolute(path))
|
||||
return canonPath(path);
|
||||
|
||||
throw Error("cannot fetch input '%s' because it uses a relative path", input.to_string());
|
||||
}
|
||||
@@ -121,31 +121,14 @@ struct PathInputScheme : InputScheme
|
||||
std::pair<ref<SourceAccessor>, Input> getAccessor(ref<Store> store, const Input & _input) const override
|
||||
{
|
||||
Input input(_input);
|
||||
std::string absPath;
|
||||
auto path = getStrAttr(input.attrs, "path");
|
||||
|
||||
if (path[0] != '/') {
|
||||
if (!input.parent)
|
||||
throw Error("cannot fetch input '%s' because it uses a relative path", input.to_string());
|
||||
auto absPath = getAbsPath(input);
|
||||
|
||||
auto parent = canonPath(*input.parent);
|
||||
|
||||
// the path isn't relative, prefix it
|
||||
absPath = nix::absPath(path, parent);
|
||||
|
||||
// for security, ensure that if the parent is a store path, it's inside it
|
||||
if (store->isInStore(parent)) {
|
||||
auto storePath = store->printStorePath(store->toStorePath(parent).first);
|
||||
if (!isDirOrInDir(absPath, storePath))
|
||||
throw BadStorePath("relative path '%s' points outside of its parent's store path '%s'", path, storePath);
|
||||
}
|
||||
} else
|
||||
absPath = path;
|
||||
|
||||
Activity act(*logger, lvlTalkative, actUnknown, fmt("copying '%s'", absPath));
|
||||
Activity act(*logger, lvlTalkative, actUnknown, fmt("copying '%s' to the store", absPath));
|
||||
|
||||
// FIXME: check whether access to 'path' is allowed.
|
||||
auto storePath = store->maybeParseStorePath(absPath);
|
||||
auto storePath = store->maybeParseStorePath(absPath.string());
|
||||
|
||||
if (storePath)
|
||||
store->addTempRoot(*storePath);
|
||||
@@ -154,7 +137,7 @@ struct PathInputScheme : InputScheme
|
||||
if (!storePath || storePath->name() != "source" || !store->isValidPath(*storePath)) {
|
||||
// FIXME: try to substitute storePath.
|
||||
auto src = sinkToSource([&](Sink & sink) {
|
||||
mtime = dumpPathAndGetMtime(absPath, sink, defaultPathFilter);
|
||||
mtime = dumpPathAndGetMtime(absPath.string(), sink, defaultPathFilter);
|
||||
});
|
||||
storePath = store->addToStoreFromDump(*src, "source");
|
||||
}
|
||||
@@ -176,7 +159,7 @@ struct PathInputScheme : InputScheme
|
||||
store object and the subpath. */
|
||||
auto path = getAbsPath(input);
|
||||
try {
|
||||
auto [storePath, subPath] = store->toStorePath(path.abs());
|
||||
auto [storePath, subPath] = store->toStorePath(path.string());
|
||||
auto info = store->queryPathInfo(storePath);
|
||||
return fmt("path:%s:%s", info->narHash.to_string(HashFormat::Base16, false), subPath);
|
||||
} catch (Error &) {
|
||||
|
||||
@@ -153,7 +153,7 @@ static std::shared_ptr<Registry> getGlobalRegistry(const Settings & settings, re
|
||||
return std::make_shared<Registry>(settings, Registry::Global); // empty registry
|
||||
}
|
||||
|
||||
if (!hasPrefix(path, "/")) {
|
||||
if (!isAbsolute(path)) {
|
||||
auto storePath = downloadFile(store, path, "flake-registry.json").storePath;
|
||||
if (auto store2 = store.dynamic_pointer_cast<LocalFSStore>())
|
||||
store2->addPermRoot(storePath, getCacheDir() + "/flake-registry.json");
|
||||
@@ -178,7 +178,8 @@ Registries getRegistries(const Settings & settings, ref<Store> store)
|
||||
|
||||
std::pair<Input, Attrs> lookupInRegistries(
|
||||
ref<Store> store,
|
||||
const Input & _input)
|
||||
const Input & _input,
|
||||
const RegistryFilter & filter)
|
||||
{
|
||||
Attrs extraAttrs;
|
||||
int n = 0;
|
||||
@@ -190,6 +191,7 @@ std::pair<Input, Attrs> lookupInRegistries(
|
||||
if (n > 100) throw Error("cycle detected in flake registry for '%s'", input.to_string());
|
||||
|
||||
for (auto & registry : getRegistries(*input.settings, store)) {
|
||||
if (filter && !filter(registry->type)) continue;
|
||||
// FIXME: O(n)
|
||||
for (auto & entry : registry->entries) {
|
||||
if (entry.exact) {
|
||||
|
||||
@@ -65,8 +65,15 @@ void overrideRegistry(
|
||||
const Input & to,
|
||||
const Attrs & extraAttrs);
|
||||
|
||||
using RegistryFilter = std::function<bool(Registry::RegistryType)>;
|
||||
|
||||
/**
|
||||
* Rewrite a flakeref using the registries. If `filter` is set, only
|
||||
* use the registries for which the filter function returns true.
|
||||
*/
|
||||
std::pair<Input, Attrs> lookupInRegistries(
|
||||
ref<Store> store,
|
||||
const Input & input);
|
||||
const Input & input,
|
||||
const RegistryFilter & filter = {});
|
||||
|
||||
}
|
||||
|
||||
@@ -4,8 +4,6 @@ project('nix-flake-c', 'cpp',
|
||||
'cpp_std=c++2a',
|
||||
# TODO(Qyriad): increase the warning level
|
||||
'warning_level=1',
|
||||
'debug=true',
|
||||
'optimization=2',
|
||||
'errorlogs=true', # Please print logs for tests that fail
|
||||
],
|
||||
meson_version : '>= 1.1',
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
{ lib
|
||||
, stdenv
|
||||
, mkMesonLibrary
|
||||
|
||||
, nix-store-c
|
||||
@@ -49,10 +48,6 @@ mkMesonLibrary (finalAttrs: {
|
||||
mesonFlags = [
|
||||
];
|
||||
|
||||
env = lib.optionalAttrs (stdenv.isLinux && !(stdenv.hostPlatform.isStatic && stdenv.system == "aarch64-linux")) {
|
||||
LDFLAGS = "-fuse-ld=gold";
|
||||
};
|
||||
|
||||
meta = {
|
||||
platforms = lib.platforms.unix ++ lib.platforms.windows;
|
||||
};
|
||||
|
||||
@@ -7,18 +7,60 @@ namespace nix {
|
||||
|
||||
/* ----------- tests for flake/flakeref.hh --------------------------------------------------*/
|
||||
|
||||
/* ----------------------------------------------------------------------------
|
||||
* to_string
|
||||
* --------------------------------------------------------------------------*/
|
||||
TEST(parseFlakeRef, path) {
|
||||
experimentalFeatureSettings.experimentalFeatures.get().insert(Xp::Flakes);
|
||||
|
||||
fetchers::Settings fetchSettings;
|
||||
|
||||
{
|
||||
auto s = "/foo/bar";
|
||||
auto flakeref = parseFlakeRef(fetchSettings, s);
|
||||
ASSERT_EQ(flakeref.to_string(), "path:/foo/bar");
|
||||
}
|
||||
|
||||
{
|
||||
auto s = "/foo/bar?revCount=123&rev=aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa";
|
||||
auto flakeref = parseFlakeRef(fetchSettings, s);
|
||||
ASSERT_EQ(flakeref.to_string(), "path:/foo/bar?rev=aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa&revCount=123");
|
||||
}
|
||||
|
||||
{
|
||||
auto s = "/foo/bar?xyzzy=123";
|
||||
EXPECT_THROW(
|
||||
parseFlakeRef(fetchSettings, s),
|
||||
Error);
|
||||
}
|
||||
|
||||
{
|
||||
auto s = "/foo/bar#bla";
|
||||
EXPECT_THROW(
|
||||
parseFlakeRef(fetchSettings, s),
|
||||
Error);
|
||||
}
|
||||
|
||||
{
|
||||
auto s = "/foo/bar#bla";
|
||||
auto [flakeref, fragment] = parseFlakeRefWithFragment(fetchSettings, s);
|
||||
ASSERT_EQ(flakeref.to_string(), "path:/foo/bar");
|
||||
ASSERT_EQ(fragment, "bla");
|
||||
}
|
||||
|
||||
{
|
||||
auto s = "/foo/bar?revCount=123#bla";
|
||||
auto [flakeref, fragment] = parseFlakeRefWithFragment(fetchSettings, s);
|
||||
ASSERT_EQ(flakeref.to_string(), "path:/foo/bar?revCount=123");
|
||||
ASSERT_EQ(fragment, "bla");
|
||||
}
|
||||
}
|
||||
|
||||
TEST(to_string, doesntReencodeUrl) {
|
||||
fetchers::Settings fetchSettings;
|
||||
auto s = "http://localhost:8181/test/+3d.tar.gz";
|
||||
auto flakeref = parseFlakeRef(fetchSettings, s);
|
||||
auto parsed = flakeref.to_string();
|
||||
auto unparsed = flakeref.to_string();
|
||||
auto expected = "http://localhost:8181/test/%2B3d.tar.gz";
|
||||
|
||||
ASSERT_EQ(parsed, expected);
|
||||
ASSERT_EQ(unparsed, expected);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -4,8 +4,6 @@ project('nix-flake-tests', 'cpp',
|
||||
'cpp_std=c++2a',
|
||||
# TODO(Qyriad): increase the warning level
|
||||
'warning_level=1',
|
||||
'debug=true',
|
||||
'optimization=2',
|
||||
'errorlogs=true', # Please print logs for tests that fail
|
||||
],
|
||||
meson_version : '>= 1.1',
|
||||
|
||||
@@ -56,10 +56,6 @@ mkMesonExecutable (finalAttrs: {
|
||||
mesonFlags = [
|
||||
];
|
||||
|
||||
env = lib.optionalAttrs (stdenv.isLinux && !(stdenv.hostPlatform.isStatic && stdenv.system == "aarch64-linux")) {
|
||||
LDFLAGS = "-fuse-ld=gold";
|
||||
};
|
||||
|
||||
passthru = {
|
||||
tests = {
|
||||
run = runCommand "${finalAttrs.pname}-run" {
|
||||
|
||||
@@ -21,29 +21,29 @@ using namespace flake;
|
||||
|
||||
namespace flake {
|
||||
|
||||
typedef std::pair<StorePath, FlakeRef> FetchedFlake;
|
||||
typedef std::vector<std::pair<FlakeRef, FetchedFlake>> FlakeCache;
|
||||
struct FetchedFlake
|
||||
{
|
||||
FlakeRef lockedRef;
|
||||
StorePath storePath;
|
||||
};
|
||||
|
||||
typedef std::map<FlakeRef, FetchedFlake> FlakeCache;
|
||||
|
||||
static std::optional<FetchedFlake> lookupInFlakeCache(
|
||||
const FlakeCache & flakeCache,
|
||||
const FlakeRef & flakeRef)
|
||||
{
|
||||
// FIXME: inefficient.
|
||||
for (auto & i : flakeCache) {
|
||||
if (flakeRef == i.first) {
|
||||
debug("mapping '%s' to previously seen input '%s' -> '%s",
|
||||
flakeRef, i.first, i.second.second);
|
||||
return i.second;
|
||||
}
|
||||
}
|
||||
|
||||
return std::nullopt;
|
||||
auto i = flakeCache.find(flakeRef);
|
||||
if (i == flakeCache.end()) return std::nullopt;
|
||||
debug("mapping '%s' to previously seen input '%s' -> '%s",
|
||||
flakeRef, i->first, i->second.lockedRef);
|
||||
return i->second;
|
||||
}
|
||||
|
||||
static std::tuple<StorePath, FlakeRef, FlakeRef> fetchOrSubstituteTree(
|
||||
EvalState & state,
|
||||
const FlakeRef & originalRef,
|
||||
bool allowLookup,
|
||||
bool useRegistries,
|
||||
FlakeCache & flakeCache)
|
||||
{
|
||||
auto fetched = lookupInFlakeCache(flakeCache, originalRef);
|
||||
@@ -51,32 +51,39 @@ static std::tuple<StorePath, FlakeRef, FlakeRef> fetchOrSubstituteTree(
|
||||
|
||||
if (!fetched) {
|
||||
if (originalRef.input.isDirect()) {
|
||||
fetched.emplace(originalRef.fetchTree(state.store));
|
||||
auto [storePath, lockedRef] = originalRef.fetchTree(state.store);
|
||||
fetched.emplace(FetchedFlake{.lockedRef = lockedRef, .storePath = storePath});
|
||||
} else {
|
||||
if (allowLookup) {
|
||||
resolvedRef = originalRef.resolve(state.store);
|
||||
auto fetchedResolved = lookupInFlakeCache(flakeCache, originalRef);
|
||||
if (!fetchedResolved) fetchedResolved.emplace(resolvedRef.fetchTree(state.store));
|
||||
flakeCache.push_back({resolvedRef, *fetchedResolved});
|
||||
fetched.emplace(*fetchedResolved);
|
||||
if (useRegistries) {
|
||||
resolvedRef = originalRef.resolve(
|
||||
state.store,
|
||||
[](fetchers::Registry::RegistryType type) {
|
||||
/* Only use the global registry and CLI flags
|
||||
to resolve indirect flakerefs. */
|
||||
return type == fetchers::Registry::Flag || type == fetchers::Registry::Global;
|
||||
});
|
||||
fetched = lookupInFlakeCache(flakeCache, originalRef);
|
||||
if (!fetched) {
|
||||
auto [storePath, lockedRef] = resolvedRef.fetchTree(state.store);
|
||||
fetched.emplace(FetchedFlake{.lockedRef = lockedRef, .storePath = storePath});
|
||||
}
|
||||
flakeCache.insert_or_assign(resolvedRef, *fetched);
|
||||
}
|
||||
else {
|
||||
throw Error("'%s' is an indirect flake reference, but registry lookups are not allowed", originalRef);
|
||||
}
|
||||
}
|
||||
flakeCache.push_back({originalRef, *fetched});
|
||||
flakeCache.insert_or_assign(originalRef, *fetched);
|
||||
}
|
||||
|
||||
auto [storePath, lockedRef] = *fetched;
|
||||
|
||||
debug("got tree '%s' from '%s'",
|
||||
state.store->printStorePath(storePath), lockedRef);
|
||||
state.store->printStorePath(fetched->storePath), fetched->lockedRef);
|
||||
|
||||
state.allowPath(storePath);
|
||||
state.allowPath(fetched->storePath);
|
||||
|
||||
assert(!originalRef.input.getNarHash() || storePath == originalRef.input.computeStorePath(*state.store));
|
||||
assert(!originalRef.input.getNarHash() || fetched->storePath == originalRef.input.computeStorePath(*state.store));
|
||||
|
||||
return {std::move(storePath), resolvedRef, lockedRef};
|
||||
return {fetched->storePath, resolvedRef, fetched->lockedRef};
|
||||
}
|
||||
|
||||
static void forceTrivialValue(EvalState & state, Value & value, const PosIdx pos)
|
||||
@@ -95,12 +102,19 @@ static void expectType(EvalState & state, ValueType type,
|
||||
}
|
||||
|
||||
static std::map<FlakeId, FlakeInput> parseFlakeInputs(
|
||||
EvalState & state, Value * value, const PosIdx pos,
|
||||
const std::optional<Path> & baseDir, InputPath lockRootPath);
|
||||
EvalState & state,
|
||||
Value * value,
|
||||
const PosIdx pos,
|
||||
const InputPath & lockRootPath,
|
||||
const SourcePath & flakeDir);
|
||||
|
||||
static FlakeInput parseFlakeInput(EvalState & state,
|
||||
std::string_view inputName, Value * value, const PosIdx pos,
|
||||
const std::optional<Path> & baseDir, InputPath lockRootPath)
|
||||
static FlakeInput parseFlakeInput(
|
||||
EvalState & state,
|
||||
std::string_view inputName,
|
||||
Value * value,
|
||||
const PosIdx pos,
|
||||
const InputPath & lockRootPath,
|
||||
const SourcePath & flakeDir)
|
||||
{
|
||||
expectType(state, nAttrs, *value, pos);
|
||||
|
||||
@@ -117,14 +131,25 @@ static FlakeInput parseFlakeInput(EvalState & state,
|
||||
for (auto & attr : *value->attrs()) {
|
||||
try {
|
||||
if (attr.name == sUrl) {
|
||||
expectType(state, nString, *attr.value, attr.pos);
|
||||
url = attr.value->string_view();
|
||||
forceTrivialValue(state, *attr.value, pos);
|
||||
if (attr.value->type() == nString)
|
||||
url = attr.value->string_view();
|
||||
else if (attr.value->type() == nPath) {
|
||||
auto path = attr.value->path();
|
||||
if (path.accessor != flakeDir.accessor)
|
||||
throw Error("input path '%s' at %s must be in the same source tree as %s",
|
||||
path, state.positions[attr.pos], flakeDir);
|
||||
url = "path:" + flakeDir.path.makeRelative(path.path);
|
||||
}
|
||||
else
|
||||
throw Error("expected a string or a path but got %s at %s",
|
||||
showType(attr.value->type()), state.positions[attr.pos]);
|
||||
attrs.emplace("url", *url);
|
||||
} else if (attr.name == sFlake) {
|
||||
expectType(state, nBool, *attr.value, attr.pos);
|
||||
input.isFlake = attr.value->boolean();
|
||||
} else if (attr.name == sInputs) {
|
||||
input.overrides = parseFlakeInputs(state, attr.value, attr.pos, baseDir, lockRootPath);
|
||||
input.overrides = parseFlakeInputs(state, attr.value, attr.pos, lockRootPath, flakeDir);
|
||||
} else if (attr.name == sFollows) {
|
||||
expectType(state, nString, *attr.value, attr.pos);
|
||||
auto follows(parseInputPath(attr.value->c_str()));
|
||||
@@ -182,7 +207,7 @@ static FlakeInput parseFlakeInput(EvalState & state,
|
||||
if (!attrs.empty())
|
||||
throw Error("unexpected flake input attribute '%s', at %s", attrs.begin()->first, state.positions[pos]);
|
||||
if (url)
|
||||
input.ref = parseFlakeRef(state.fetchSettings, *url, baseDir, true, input.isFlake);
|
||||
input.ref = parseFlakeRef(state.fetchSettings, *url, {}, true, input.isFlake, true);
|
||||
}
|
||||
|
||||
if (!input.follows && !input.ref)
|
||||
@@ -192,8 +217,11 @@ static FlakeInput parseFlakeInput(EvalState & state,
|
||||
}
|
||||
|
||||
static std::map<FlakeId, FlakeInput> parseFlakeInputs(
|
||||
EvalState & state, Value * value, const PosIdx pos,
|
||||
const std::optional<Path> & baseDir, InputPath lockRootPath)
|
||||
EvalState & state,
|
||||
Value * value,
|
||||
const PosIdx pos,
|
||||
const InputPath & lockRootPath,
|
||||
const SourcePath & flakeDir)
|
||||
{
|
||||
std::map<FlakeId, FlakeInput> inputs;
|
||||
|
||||
@@ -205,8 +233,8 @@ static std::map<FlakeId, FlakeInput> parseFlakeInputs(
|
||||
state.symbols[inputAttr.name],
|
||||
inputAttr.value,
|
||||
inputAttr.pos,
|
||||
baseDir,
|
||||
lockRootPath));
|
||||
lockRootPath,
|
||||
flakeDir));
|
||||
}
|
||||
|
||||
return inputs;
|
||||
@@ -220,7 +248,8 @@ static Flake readFlake(
|
||||
const SourcePath & rootDir,
|
||||
const InputPath & lockRootPath)
|
||||
{
|
||||
auto flakePath = rootDir / CanonPath(resolvedRef.subdir) / "flake.nix";
|
||||
auto flakeDir = rootDir / CanonPath(resolvedRef.subdir);
|
||||
auto flakePath = flakeDir / "flake.nix";
|
||||
|
||||
// NOTE evalFile forces vInfo to be an attrset because mustBeTrivial is true.
|
||||
Value vInfo;
|
||||
@@ -241,7 +270,7 @@ static Flake readFlake(
|
||||
auto sInputs = state.symbols.create("inputs");
|
||||
|
||||
if (auto inputs = vInfo.attrs()->get(sInputs))
|
||||
flake.inputs = parseFlakeInputs(state, inputs->value, inputs->pos, flakePath.parent().path.abs(), lockRootPath); // FIXME
|
||||
flake.inputs = parseFlakeInputs(state, inputs->value, inputs->pos, lockRootPath, flakeDir);
|
||||
|
||||
auto sOutputs = state.symbols.create("outputs");
|
||||
|
||||
@@ -316,25 +345,20 @@ static Flake readFlake(
|
||||
static Flake getFlake(
|
||||
EvalState & state,
|
||||
const FlakeRef & originalRef,
|
||||
bool allowLookup,
|
||||
bool useRegistries,
|
||||
FlakeCache & flakeCache,
|
||||
InputPath lockRootPath)
|
||||
const InputPath & lockRootPath)
|
||||
{
|
||||
auto [storePath, resolvedRef, lockedRef] = fetchOrSubstituteTree(
|
||||
state, originalRef, allowLookup, flakeCache);
|
||||
state, originalRef, useRegistries, flakeCache);
|
||||
|
||||
return readFlake(state, originalRef, resolvedRef, lockedRef, state.rootPath(state.store->toRealPath(storePath)), lockRootPath);
|
||||
}
|
||||
|
||||
Flake getFlake(EvalState & state, const FlakeRef & originalRef, bool allowLookup, FlakeCache & flakeCache)
|
||||
{
|
||||
return getFlake(state, originalRef, allowLookup, flakeCache, {});
|
||||
}
|
||||
|
||||
Flake getFlake(EvalState & state, const FlakeRef & originalRef, bool allowLookup)
|
||||
Flake getFlake(EvalState & state, const FlakeRef & originalRef, bool useRegistries)
|
||||
{
|
||||
FlakeCache flakeCache;
|
||||
return getFlake(state, originalRef, allowLookup, flakeCache);
|
||||
return getFlake(state, originalRef, useRegistries, flakeCache, {});
|
||||
}
|
||||
|
||||
static LockFile readLockFile(
|
||||
@@ -360,7 +384,7 @@ LockedFlake lockFlake(
|
||||
|
||||
auto useRegistries = lockFlags.useRegistries.value_or(settings.useRegistries);
|
||||
|
||||
auto flake = getFlake(state, topRef, useRegistries, flakeCache);
|
||||
auto flake = getFlake(state, topRef, useRegistries, flakeCache, {});
|
||||
|
||||
if (lockFlags.applyNixConfig) {
|
||||
flake.config.apply(settings);
|
||||
@@ -379,13 +403,29 @@ LockedFlake lockFlake(
|
||||
|
||||
debug("old lock file: %s", oldLockFile);
|
||||
|
||||
std::map<InputPath, FlakeInput> overrides;
|
||||
struct OverrideTarget
|
||||
{
|
||||
FlakeInput input;
|
||||
SourcePath sourcePath;
|
||||
std::optional<InputPath> parentInputPath; // FIXME: rename to inputPathPrefix?
|
||||
};
|
||||
|
||||
std::map<InputPath, OverrideTarget> overrides;
|
||||
std::set<InputPath> explicitCliOverrides;
|
||||
std::set<InputPath> overridesUsed, updatesUsed;
|
||||
std::map<ref<Node>, SourcePath> nodePaths;
|
||||
|
||||
for (auto & i : lockFlags.inputOverrides) {
|
||||
overrides.insert_or_assign(i.first, FlakeInput { .ref = i.second });
|
||||
overrides.emplace(
|
||||
i.first,
|
||||
OverrideTarget {
|
||||
.input = FlakeInput { .ref = i.second },
|
||||
/* Note: any relative overrides
|
||||
(e.g. `--override-input B/C "path:./foo/bar"`)
|
||||
are interpreted relative to the top-level
|
||||
flake. */
|
||||
.sourcePath = flake.path,
|
||||
});
|
||||
explicitCliOverrides.insert(i.first);
|
||||
}
|
||||
|
||||
@@ -398,8 +438,8 @@ LockedFlake lockFlake(
|
||||
ref<Node> node,
|
||||
const InputPath & inputPathPrefix,
|
||||
std::shared_ptr<const Node> oldNode,
|
||||
const InputPath & lockRootPath,
|
||||
const Path & parentPath,
|
||||
const InputPath & followsPrefix,
|
||||
const SourcePath & sourcePath,
|
||||
bool trustLock)>
|
||||
computeLocks;
|
||||
|
||||
@@ -414,8 +454,13 @@ LockedFlake lockFlake(
|
||||
/* The old node, if any, from which locks can be
|
||||
copied. */
|
||||
std::shared_ptr<const Node> oldNode,
|
||||
const InputPath & lockRootPath,
|
||||
const Path & parentPath,
|
||||
/* The prefix relative to which 'follows' should be
|
||||
interpreted. When a node is initially locked, it's
|
||||
relative to the node's flake; when it's already locked,
|
||||
it's relative to the root of the lock file. */
|
||||
const InputPath & followsPrefix,
|
||||
/* The source path of this node's flake. */
|
||||
const SourcePath & sourcePath,
|
||||
bool trustLock)
|
||||
{
|
||||
debug("computing lock file node '%s'", printInputPath(inputPathPrefix));
|
||||
@@ -427,7 +472,12 @@ LockedFlake lockFlake(
|
||||
auto inputPath(inputPathPrefix);
|
||||
inputPath.push_back(id);
|
||||
inputPath.push_back(idOverride);
|
||||
overrides.insert_or_assign(inputPath, inputOverride);
|
||||
overrides.emplace(inputPath,
|
||||
OverrideTarget {
|
||||
.input = inputOverride,
|
||||
.sourcePath = sourcePath,
|
||||
.parentInputPath = inputPathPrefix
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -459,13 +509,18 @@ LockedFlake lockFlake(
|
||||
auto i = overrides.find(inputPath);
|
||||
bool hasOverride = i != overrides.end();
|
||||
bool hasCliOverride = explicitCliOverrides.contains(inputPath);
|
||||
if (hasOverride) {
|
||||
if (hasOverride)
|
||||
overridesUsed.insert(inputPath);
|
||||
// Respect the “flakeness” of the input even if we
|
||||
// override it
|
||||
i->second.isFlake = input2.isFlake;
|
||||
}
|
||||
auto & input = hasOverride ? i->second : input2;
|
||||
auto input = hasOverride ? i->second.input : input2;
|
||||
|
||||
/* Resolve relative 'path:' inputs relative to
|
||||
the source path of the overrider. */
|
||||
auto overridenSourcePath = hasOverride ? i->second.sourcePath : sourcePath;
|
||||
|
||||
/* Respect the "flakeness" of the input even if we
|
||||
override it. */
|
||||
if (hasOverride)
|
||||
input.isFlake = input2.isFlake;
|
||||
|
||||
/* Resolve 'follows' later (since it may refer to an input
|
||||
path we haven't processed yet. */
|
||||
@@ -481,6 +536,33 @@ LockedFlake lockFlake(
|
||||
|
||||
assert(input.ref);
|
||||
|
||||
auto overridenParentPath =
|
||||
input.ref->input.isRelative()
|
||||
? std::optional<InputPath>(hasOverride ? i->second.parentInputPath : inputPathPrefix)
|
||||
: std::nullopt;
|
||||
|
||||
auto resolveRelativePath = [&]() -> std::optional<SourcePath>
|
||||
{
|
||||
if (auto relativePath = input.ref->input.isRelative()) {
|
||||
return SourcePath {
|
||||
overridenSourcePath.accessor,
|
||||
CanonPath(*relativePath, overridenSourcePath.path.parent().value())
|
||||
};
|
||||
} else
|
||||
return std::nullopt;
|
||||
};
|
||||
|
||||
/* Get the input flake, resolve 'path:./...'
|
||||
flakerefs relative to the parent flake. */
|
||||
auto getInputFlake = [&]()
|
||||
{
|
||||
if (auto resolvedPath = resolveRelativePath()) {
|
||||
return readFlake(state, *input.ref, *input.ref, *input.ref, *resolvedPath, inputPath);
|
||||
} else {
|
||||
return getFlake(state, *input.ref, useRegistries, flakeCache, inputPath);
|
||||
}
|
||||
};
|
||||
|
||||
/* Do we have an entry in the existing lock file?
|
||||
And the input is not in updateInputs? */
|
||||
std::shared_ptr<LockedNode> oldLock;
|
||||
@@ -494,6 +576,7 @@ LockedFlake lockFlake(
|
||||
|
||||
if (oldLock
|
||||
&& oldLock->originalRef == *input.ref
|
||||
&& oldLock->parentPath == overridenParentPath
|
||||
&& !hasCliOverride)
|
||||
{
|
||||
debug("keeping existing input '%s'", inputPathS);
|
||||
@@ -502,7 +585,10 @@ LockedFlake lockFlake(
|
||||
didn't change and there is no override from a
|
||||
higher level flake. */
|
||||
auto childNode = make_ref<LockedNode>(
|
||||
oldLock->lockedRef, oldLock->originalRef, oldLock->isFlake);
|
||||
oldLock->lockedRef,
|
||||
oldLock->originalRef,
|
||||
oldLock->isFlake,
|
||||
oldLock->parentPath);
|
||||
|
||||
node->inputs.insert_or_assign(id, childNode);
|
||||
|
||||
@@ -544,7 +630,7 @@ LockedFlake lockFlake(
|
||||
break;
|
||||
}
|
||||
}
|
||||
auto absoluteFollows(lockRootPath);
|
||||
auto absoluteFollows(followsPrefix);
|
||||
absoluteFollows.insert(absoluteFollows.end(), follows->begin(), follows->end());
|
||||
fakeInputs.emplace(i.first, FlakeInput {
|
||||
.follows = absoluteFollows,
|
||||
@@ -554,11 +640,12 @@ LockedFlake lockFlake(
|
||||
}
|
||||
|
||||
if (mustRefetch) {
|
||||
auto inputFlake = getFlake(state, oldLock->lockedRef, false, flakeCache, inputPath);
|
||||
auto inputFlake = getInputFlake();
|
||||
nodePaths.emplace(childNode, inputFlake.path.parent());
|
||||
computeLocks(inputFlake.inputs, childNode, inputPath, oldLock, lockRootPath, parentPath, false);
|
||||
computeLocks(inputFlake.inputs, childNode, inputPath, oldLock, followsPrefix,
|
||||
inputFlake.path, false);
|
||||
} else {
|
||||
computeLocks(fakeInputs, childNode, inputPath, oldLock, lockRootPath, parentPath, true);
|
||||
computeLocks(fakeInputs, childNode, inputPath, oldLock, followsPrefix, sourcePath, true);
|
||||
}
|
||||
|
||||
} else {
|
||||
@@ -566,7 +653,9 @@ LockedFlake lockFlake(
|
||||
this input. */
|
||||
debug("creating new input '%s'", inputPathS);
|
||||
|
||||
if (!lockFlags.allowUnlocked && !input.ref->input.isLocked())
|
||||
if (!lockFlags.allowUnlocked
|
||||
&& !input.ref->input.isLocked()
|
||||
&& !input.ref->input.isRelative())
|
||||
throw Error("cannot update unlocked flake input '%s' in pure mode", inputPathS);
|
||||
|
||||
/* Note: in case of an --override-input, we use
|
||||
@@ -579,17 +668,13 @@ LockedFlake lockFlake(
|
||||
auto ref = (input2.ref && explicitCliOverrides.contains(inputPath)) ? *input2.ref : *input.ref;
|
||||
|
||||
if (input.isFlake) {
|
||||
Path localPath = parentPath;
|
||||
FlakeRef localRef = *input.ref;
|
||||
auto inputFlake = getInputFlake();
|
||||
|
||||
// If this input is a path, recurse it down.
|
||||
// This allows us to resolve path inputs relative to the current flake.
|
||||
if (localRef.input.getType() == "path")
|
||||
localPath = absPath(*input.ref->input.getSourcePath(), parentPath);
|
||||
|
||||
auto inputFlake = getFlake(state, localRef, useRegistries, flakeCache, inputPath);
|
||||
|
||||
auto childNode = make_ref<LockedNode>(inputFlake.lockedRef, ref);
|
||||
auto childNode = make_ref<LockedNode>(
|
||||
inputFlake.lockedRef,
|
||||
ref,
|
||||
true,
|
||||
overridenParentPath);
|
||||
|
||||
node->inputs.insert_or_assign(id, childNode);
|
||||
|
||||
@@ -610,18 +695,27 @@ LockedFlake lockFlake(
|
||||
oldLock
|
||||
? std::dynamic_pointer_cast<const Node>(oldLock)
|
||||
: readLockFile(state.fetchSettings, inputFlake.lockFilePath()).root.get_ptr(),
|
||||
oldLock ? lockRootPath : inputPath,
|
||||
localPath,
|
||||
oldLock ? followsPrefix : inputPath,
|
||||
inputFlake.path,
|
||||
false);
|
||||
}
|
||||
|
||||
else {
|
||||
auto [storePath, resolvedRef, lockedRef] = fetchOrSubstituteTree(
|
||||
state, *input.ref, useRegistries, flakeCache);
|
||||
auto [path, lockedRef] = [&]() -> std::tuple<SourcePath, FlakeRef>
|
||||
{
|
||||
// Handle non-flake 'path:./...' inputs.
|
||||
if (auto resolvedPath = resolveRelativePath()) {
|
||||
return {*resolvedPath, *input.ref};
|
||||
} else {
|
||||
auto [storePath, resolvedRef, lockedRef] = fetchOrSubstituteTree(
|
||||
state, *input.ref, useRegistries, flakeCache);
|
||||
return {state.rootPath(state.store->toRealPath(storePath)), lockedRef};
|
||||
}
|
||||
}();
|
||||
|
||||
auto childNode = make_ref<LockedNode>(lockedRef, ref, false);
|
||||
auto childNode = make_ref<LockedNode>(lockedRef, ref, false, overridenParentPath);
|
||||
|
||||
nodePaths.emplace(childNode, state.rootPath(state.store->toRealPath(storePath)));
|
||||
nodePaths.emplace(childNode, path);
|
||||
|
||||
node->inputs.insert_or_assign(id, childNode);
|
||||
}
|
||||
@@ -634,9 +728,6 @@ LockedFlake lockFlake(
|
||||
}
|
||||
};
|
||||
|
||||
// Bring in the current ref for relative path resolution if we have it
|
||||
auto parentPath = flake.path.parent().path.abs();
|
||||
|
||||
nodePaths.emplace(newLockFile.root, flake.path.parent());
|
||||
|
||||
computeLocks(
|
||||
@@ -645,7 +736,7 @@ LockedFlake lockFlake(
|
||||
{},
|
||||
lockFlags.recreateLockFile ? nullptr : oldLockFile.root.get_ptr(),
|
||||
{},
|
||||
parentPath,
|
||||
flake.path,
|
||||
false);
|
||||
|
||||
for (auto & i : lockFlags.inputOverrides)
|
||||
@@ -671,7 +762,11 @@ LockedFlake lockFlake(
|
||||
|
||||
if (lockFlags.writeLockFile) {
|
||||
if (sourcePath || lockFlags.outputLockFilePath) {
|
||||
if (auto unlockedInput = newLockFile.isUnlocked()) {
|
||||
if (auto unlockedInput = newLockFile.isUnlocked(state.fetchSettings)) {
|
||||
if (lockFlags.failOnUnlocked)
|
||||
throw Error(
|
||||
"Will not write lock file of flake '%s' because it has an unlocked input ('%s'). "
|
||||
"Use '--allow-dirty-locks' to allow this anyway.", topRef, *unlockedInput);
|
||||
if (state.fetchSettings.warnDirty)
|
||||
warn("will not write lock file of flake '%s' because it has an unlocked input ('%s')", topRef, *unlockedInput);
|
||||
} else {
|
||||
@@ -686,9 +781,9 @@ LockedFlake lockFlake(
|
||||
writeFile(*lockFlags.outputLockFilePath, newLockFileS);
|
||||
} else {
|
||||
auto relPath = (topRef.subdir == "" ? "" : topRef.subdir + "/") + "flake.lock";
|
||||
auto outputLockFilePath = *sourcePath + "/" + relPath;
|
||||
auto outputLockFilePath = *sourcePath / relPath;
|
||||
|
||||
bool lockFileExists = pathExists(outputLockFilePath);
|
||||
bool lockFileExists = fs::symlink_exists(outputLockFilePath);
|
||||
|
||||
auto s = chomp(diff);
|
||||
if (lockFileExists) {
|
||||
@@ -724,8 +819,7 @@ LockedFlake lockFlake(
|
||||
repo, so we should re-read it. FIXME: we could
|
||||
also just clear the 'rev' field... */
|
||||
auto prevLockedRef = flake.lockedRef;
|
||||
FlakeCache dummyCache;
|
||||
flake = getFlake(state, topRef, useRegistries, dummyCache);
|
||||
flake = getFlake(state, topRef, useRegistries);
|
||||
|
||||
if (lockFlags.commitLockFile &&
|
||||
flake.lockedRef.input.getRev() &&
|
||||
@@ -970,9 +1064,11 @@ static RegisterPrimOp r4({
|
||||
|
||||
}
|
||||
|
||||
std::optional<Fingerprint> LockedFlake::getFingerprint(ref<Store> store) const
|
||||
std::optional<Fingerprint> LockedFlake::getFingerprint(
|
||||
ref<Store> store,
|
||||
const fetchers::Settings & fetchSettings) const
|
||||
{
|
||||
if (lockFile.isUnlocked()) return std::nullopt;
|
||||
if (lockFile.isUnlocked(fetchSettings)) return std::nullopt;
|
||||
|
||||
auto fingerprint = flake.lockedRef.input.getFingerprint(store);
|
||||
if (!fingerprint) return std::nullopt;
|
||||
|
||||
@@ -110,7 +110,7 @@ struct Flake
|
||||
}
|
||||
};
|
||||
|
||||
Flake getFlake(EvalState & state, const FlakeRef & flakeRef, bool allowLookup);
|
||||
Flake getFlake(EvalState & state, const FlakeRef & flakeRef, bool useRegistries);
|
||||
|
||||
/**
|
||||
* Fingerprint of a locked flake; used as a cache key.
|
||||
@@ -129,7 +129,9 @@ struct LockedFlake
|
||||
*/
|
||||
std::map<ref<Node>, SourcePath> nodePaths;
|
||||
|
||||
std::optional<Fingerprint> getFingerprint(ref<Store> store) const;
|
||||
std::optional<Fingerprint> getFingerprint(
|
||||
ref<Store> store,
|
||||
const fetchers::Settings & fetchSettings) const;
|
||||
};
|
||||
|
||||
struct LockFlags
|
||||
@@ -156,6 +158,11 @@ struct LockFlags
|
||||
*/
|
||||
bool writeLockFile = true;
|
||||
|
||||
/**
|
||||
* Throw an exception when the flake has an unlocked input.
|
||||
*/
|
||||
bool failOnUnlocked = false;
|
||||
|
||||
/**
|
||||
* Whether to use the registries to lookup indirect flake
|
||||
* references like 'nixpkgs'.
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
#include "url.hh"
|
||||
#include "url-parts.hh"
|
||||
#include "fetchers.hh"
|
||||
#include "registry.hh"
|
||||
|
||||
namespace nix {
|
||||
|
||||
@@ -36,7 +35,9 @@ std::ostream & operator << (std::ostream & str, const FlakeRef & flakeRef)
|
||||
return str;
|
||||
}
|
||||
|
||||
FlakeRef FlakeRef::resolve(ref<Store> store) const
|
||||
FlakeRef FlakeRef::resolve(
|
||||
ref<Store> store,
|
||||
const fetchers::RegistryFilter & filter) const
|
||||
{
|
||||
auto [input2, extraAttrs] = lookupInRegistries(store, input);
|
||||
return FlakeRef(std::move(input2), fetchers::maybeGetStrAttr(extraAttrs, "dir").value_or(subdir));
|
||||
@@ -47,9 +48,10 @@ FlakeRef parseFlakeRef(
|
||||
const std::string & url,
|
||||
const std::optional<Path> & baseDir,
|
||||
bool allowMissing,
|
||||
bool isFlake)
|
||||
bool isFlake,
|
||||
bool preserveRelativePaths)
|
||||
{
|
||||
auto [flakeRef, fragment] = parseFlakeRefWithFragment(fetchSettings, url, baseDir, allowMissing, isFlake);
|
||||
auto [flakeRef, fragment] = parseFlakeRefWithFragment(fetchSettings, url, baseDir, allowMissing, isFlake, preserveRelativePaths);
|
||||
if (fragment != "")
|
||||
throw Error("unexpected fragment '%s' in flake reference '%s'", fragment, url);
|
||||
return flakeRef;
|
||||
@@ -86,30 +88,24 @@ std::pair<FlakeRef, std::string> parsePathFlakeRefWithFragment(
|
||||
const std::string & url,
|
||||
const std::optional<Path> & baseDir,
|
||||
bool allowMissing,
|
||||
bool isFlake)
|
||||
bool isFlake,
|
||||
bool preserveRelativePaths)
|
||||
{
|
||||
std::string path = url;
|
||||
std::string fragment = "";
|
||||
std::map<std::string, std::string> query;
|
||||
auto pathEnd = url.find_first_of("#?");
|
||||
auto fragmentStart = pathEnd;
|
||||
if (pathEnd != std::string::npos && url[pathEnd] == '?') {
|
||||
fragmentStart = url.find("#");
|
||||
}
|
||||
if (pathEnd != std::string::npos) {
|
||||
path = url.substr(0, pathEnd);
|
||||
}
|
||||
if (fragmentStart != std::string::npos) {
|
||||
fragment = percentDecode(url.substr(fragmentStart+1));
|
||||
}
|
||||
if (pathEnd != std::string::npos && fragmentStart != std::string::npos && url[pathEnd] == '?') {
|
||||
query = decodeQuery(url.substr(pathEnd + 1, fragmentStart - pathEnd - 1));
|
||||
}
|
||||
static std::regex pathFlakeRegex(
|
||||
R"(([^?#]*)(\?([^#]*))?(#(.*))?)",
|
||||
std::regex::ECMAScript);
|
||||
|
||||
std::smatch match;
|
||||
auto succeeds = std::regex_match(url, match, pathFlakeRegex);
|
||||
assert(succeeds);
|
||||
auto path = match[1].str();
|
||||
auto query = decodeQuery(match[3]);
|
||||
auto fragment = percentDecode(match[5].str());
|
||||
|
||||
if (baseDir) {
|
||||
/* Check if 'url' is a path (either absolute or relative
|
||||
to 'baseDir'). If so, search upward to the root of the
|
||||
repo (i.e. the directory containing .git). */
|
||||
to 'baseDir'). If so, search upward to the root of the
|
||||
repo (i.e. the directory containing .git). */
|
||||
|
||||
path = absPath(path, baseDir);
|
||||
|
||||
@@ -158,11 +154,7 @@ std::pair<FlakeRef, std::string> parsePathFlakeRefWithFragment(
|
||||
|
||||
while (flakeRoot != "/") {
|
||||
if (pathExists(flakeRoot + "/.git")) {
|
||||
auto base = std::string("git+file://") + flakeRoot;
|
||||
|
||||
auto parsedURL = ParsedURL{
|
||||
.url = base, // FIXME
|
||||
.base = base,
|
||||
.scheme = "git+file",
|
||||
.authority = "",
|
||||
.path = flakeRoot,
|
||||
@@ -188,16 +180,17 @@ std::pair<FlakeRef, std::string> parsePathFlakeRefWithFragment(
|
||||
}
|
||||
|
||||
} else {
|
||||
if (!hasPrefix(path, "/"))
|
||||
if (!preserveRelativePaths && !isAbsolute(path))
|
||||
throw BadURL("flake reference '%s' is not an absolute path", url);
|
||||
path = canonPath(path + "/" + getOr(query, "dir", ""));
|
||||
}
|
||||
|
||||
fetchers::Attrs attrs;
|
||||
attrs.insert_or_assign("type", "path");
|
||||
attrs.insert_or_assign("path", path);
|
||||
|
||||
return std::make_pair(FlakeRef(fetchers::Input::fromAttrs(fetchSettings, std::move(attrs)), ""), fragment);
|
||||
return fromParsedURL(fetchSettings, {
|
||||
.scheme = "path",
|
||||
.authority = "",
|
||||
.path = path,
|
||||
.query = query,
|
||||
.fragment = fragment
|
||||
}, isFlake);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -207,8 +200,7 @@ std::pair<FlakeRef, std::string> parsePathFlakeRefWithFragment(
|
||||
static std::optional<std::pair<FlakeRef, std::string>> parseFlakeIdRef(
|
||||
const fetchers::Settings & fetchSettings,
|
||||
const std::string & url,
|
||||
bool isFlake
|
||||
)
|
||||
bool isFlake)
|
||||
{
|
||||
std::smatch match;
|
||||
|
||||
@@ -219,8 +211,6 @@ static std::optional<std::pair<FlakeRef, std::string>> parseFlakeIdRef(
|
||||
|
||||
if (std::regex_match(url, match, flakeRegex)) {
|
||||
auto parsedURL = ParsedURL{
|
||||
.url = url,
|
||||
.base = "flake:" + match.str(1),
|
||||
.scheme = "flake",
|
||||
.authority = "",
|
||||
.path = match[1],
|
||||
@@ -238,11 +228,15 @@ std::optional<std::pair<FlakeRef, std::string>> parseURLFlakeRef(
|
||||
const fetchers::Settings & fetchSettings,
|
||||
const std::string & url,
|
||||
const std::optional<Path> & baseDir,
|
||||
bool isFlake
|
||||
)
|
||||
bool isFlake)
|
||||
{
|
||||
try {
|
||||
return fromParsedURL(fetchSettings, parseURL(url), isFlake);
|
||||
auto parsed = parseURL(url);
|
||||
if (baseDir
|
||||
&& (parsed.scheme == "path" || parsed.scheme == "git+file")
|
||||
&& !isAbsolute(parsed.path))
|
||||
parsed.path = absPath(parsed.path, *baseDir);
|
||||
return fromParsedURL(fetchSettings, std::move(parsed), isFlake);
|
||||
} catch (BadURL &) {
|
||||
return std::nullopt;
|
||||
}
|
||||
@@ -253,7 +247,8 @@ std::pair<FlakeRef, std::string> parseFlakeRefWithFragment(
|
||||
const std::string & url,
|
||||
const std::optional<Path> & baseDir,
|
||||
bool allowMissing,
|
||||
bool isFlake)
|
||||
bool isFlake,
|
||||
bool preserveRelativePaths)
|
||||
{
|
||||
using namespace fetchers;
|
||||
|
||||
@@ -262,7 +257,7 @@ std::pair<FlakeRef, std::string> parseFlakeRefWithFragment(
|
||||
} else if (auto res = parseURLFlakeRef(fetchSettings, url, baseDir, isFlake)) {
|
||||
return *res;
|
||||
} else {
|
||||
return parsePathFlakeRefWithFragment(fetchSettings, url, baseDir, allowMissing, isFlake);
|
||||
return parsePathFlakeRefWithFragment(fetchSettings, url, baseDir, allowMissing, isFlake, preserveRelativePaths);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
#include "types.hh"
|
||||
#include "fetchers.hh"
|
||||
#include "outputs-spec.hh"
|
||||
#include "registry.hh"
|
||||
|
||||
namespace nix {
|
||||
|
||||
@@ -48,6 +49,11 @@ struct FlakeRef
|
||||
|
||||
bool operator ==(const FlakeRef & other) const = default;
|
||||
|
||||
bool operator <(const FlakeRef & other) const
|
||||
{
|
||||
return std::tie(input, subdir) < std::tie(other.input, other.subdir);
|
||||
}
|
||||
|
||||
FlakeRef(fetchers::Input && input, const Path & subdir)
|
||||
: input(std::move(input)), subdir(subdir)
|
||||
{ }
|
||||
@@ -57,7 +63,9 @@ struct FlakeRef
|
||||
|
||||
fetchers::Attrs toAttrs() const;
|
||||
|
||||
FlakeRef resolve(ref<Store> store) const;
|
||||
FlakeRef resolve(
|
||||
ref<Store> store,
|
||||
const fetchers::RegistryFilter & filter = {}) const;
|
||||
|
||||
static FlakeRef fromAttrs(
|
||||
const fetchers::Settings & fetchSettings,
|
||||
@@ -76,7 +84,8 @@ FlakeRef parseFlakeRef(
|
||||
const std::string & url,
|
||||
const std::optional<Path> & baseDir = {},
|
||||
bool allowMissing = false,
|
||||
bool isFlake = true);
|
||||
bool isFlake = true,
|
||||
bool preserveRelativePaths = false);
|
||||
|
||||
/**
|
||||
* @param baseDir Optional [base directory](https://nixos.org/manual/nix/unstable/glossary#gloss-base-directory)
|
||||
@@ -94,7 +103,8 @@ std::pair<FlakeRef, std::string> parseFlakeRefWithFragment(
|
||||
const std::string & url,
|
||||
const std::optional<Path> & baseDir = {},
|
||||
bool allowMissing = false,
|
||||
bool isFlake = true);
|
||||
bool isFlake = true,
|
||||
bool preserveRelativePaths = false);
|
||||
|
||||
/**
|
||||
* @param baseDir Optional [base directory](https://nixos.org/manual/nix/unstable/glossary#gloss-base-directory)
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
#include <nlohmann/json.hpp>
|
||||
|
||||
#include "strings.hh"
|
||||
#include "flake/settings.hh"
|
||||
|
||||
namespace nix::flake {
|
||||
|
||||
@@ -42,9 +43,10 @@ LockedNode::LockedNode(
|
||||
: lockedRef(getFlakeRef(fetchSettings, json, "locked", "info")) // FIXME: remove "info"
|
||||
, originalRef(getFlakeRef(fetchSettings, json, "original", nullptr))
|
||||
, isFlake(json.find("flake") != json.end() ? (bool) json["flake"] : true)
|
||||
, parentPath(json.find("parent") != json.end() ? (std::optional<InputPath>) json["parent"] : std::nullopt)
|
||||
{
|
||||
if (!lockedRef.input.isLocked())
|
||||
throw Error("lock file contains unlocked input '%s'",
|
||||
if (!lockedRef.input.isConsideredLocked(fetchSettings) && !lockedRef.input.isRelative())
|
||||
throw Error("Lock file contains unlocked input '%s'. Use '--allow-dirty-locks' to accept this lock file.",
|
||||
fetchers::attrsToJSON(lockedRef.input.toAttrs()));
|
||||
|
||||
// For backward compatibility, lock file entries are implicitly final.
|
||||
@@ -197,10 +199,12 @@ std::pair<nlohmann::json, LockFile::KeyMap> LockFile::toJSON() const
|
||||
/* For backward compatibility, omit the "__final"
|
||||
attribute. We never allow non-final inputs in lock files
|
||||
anyway. */
|
||||
assert(lockedNode->lockedRef.input.isFinal());
|
||||
assert(lockedNode->lockedRef.input.isFinal() || lockedNode->lockedRef.input.isRelative());
|
||||
n["locked"].erase("__final");
|
||||
if (!lockedNode->isFlake)
|
||||
n["flake"] = false;
|
||||
if (lockedNode->parentPath)
|
||||
n["parent"] = *lockedNode->parentPath;
|
||||
}
|
||||
|
||||
nodes[key] = std::move(n);
|
||||
@@ -228,7 +232,7 @@ std::ostream & operator <<(std::ostream & stream, const LockFile & lockFile)
|
||||
return stream;
|
||||
}
|
||||
|
||||
std::optional<FlakeRef> LockFile::isUnlocked() const
|
||||
std::optional<FlakeRef> LockFile::isUnlocked(const fetchers::Settings & fetchSettings) const
|
||||
{
|
||||
std::set<ref<const Node>> nodes;
|
||||
|
||||
@@ -247,7 +251,10 @@ std::optional<FlakeRef> LockFile::isUnlocked() const
|
||||
for (auto & i : nodes) {
|
||||
if (i == ref<const Node>(root)) continue;
|
||||
auto node = i.dynamic_pointer_cast<const LockedNode>();
|
||||
if (node && (!node->lockedRef.input.isLocked() || !node->lockedRef.input.isFinal()))
|
||||
if (node
|
||||
&& (!node->lockedRef.input.isConsideredLocked(fetchSettings)
|
||||
|| !node->lockedRef.input.isFinal())
|
||||
&& !node->lockedRef.input.isRelative())
|
||||
return node->lockedRef;
|
||||
}
|
||||
|
||||
|
||||
@@ -38,11 +38,19 @@ struct LockedNode : Node
|
||||
FlakeRef lockedRef, originalRef;
|
||||
bool isFlake = true;
|
||||
|
||||
/* The node relative to which relative source paths
|
||||
(e.g. 'path:../foo') are interpreted. */
|
||||
std::optional<InputPath> parentPath;
|
||||
|
||||
LockedNode(
|
||||
const FlakeRef & lockedRef,
|
||||
const FlakeRef & originalRef,
|
||||
bool isFlake = true)
|
||||
: lockedRef(lockedRef), originalRef(originalRef), isFlake(isFlake)
|
||||
bool isFlake = true,
|
||||
std::optional<InputPath> parentPath = {})
|
||||
: lockedRef(lockedRef)
|
||||
, originalRef(originalRef)
|
||||
, isFlake(isFlake)
|
||||
, parentPath(parentPath)
|
||||
{ }
|
||||
|
||||
LockedNode(
|
||||
@@ -71,7 +79,7 @@ struct LockFile
|
||||
* Check whether this lock file has any unlocked or non-final
|
||||
* inputs. If so, return one.
|
||||
*/
|
||||
std::optional<FlakeRef> isUnlocked() const;
|
||||
std::optional<FlakeRef> isUnlocked(const fetchers::Settings & fetchSettings) const;
|
||||
|
||||
bool operator ==(const LockFile & other) const;
|
||||
|
||||
|
||||
@@ -29,7 +29,7 @@ struct Settings : public Config
|
||||
this,
|
||||
false,
|
||||
"accept-flake-config",
|
||||
"Whether to accept nix configuration from a flake without prompting.",
|
||||
"Whether to accept Nix configuration settings from a flake without prompting.",
|
||||
{},
|
||||
true,
|
||||
Xp::Flakes};
|
||||
|
||||
@@ -4,8 +4,6 @@ project('nix-flake', 'cpp',
|
||||
'cpp_std=c++2a',
|
||||
# TODO(Qyriad): increase the warning level
|
||||
'warning_level=1',
|
||||
'debug=true',
|
||||
'optimization=2',
|
||||
'errorlogs=true', # Please print logs for tests that fail
|
||||
],
|
||||
meson_version : '>= 1.1',
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
{ lib
|
||||
, stdenv
|
||||
, mkMesonLibrary
|
||||
|
||||
, nix-util
|
||||
@@ -48,10 +47,6 @@ mkMesonLibrary (finalAttrs: {
|
||||
echo ${version} > ../../.version
|
||||
'';
|
||||
|
||||
env = lib.optionalAttrs (stdenv.isLinux && !(stdenv.hostPlatform.isStatic && stdenv.system == "aarch64-linux")) {
|
||||
LDFLAGS = "-fuse-ld=gold";
|
||||
};
|
||||
|
||||
meta = {
|
||||
platforms = lib.platforms.unix ++ lib.platforms.windows;
|
||||
};
|
||||
|
||||
@@ -4,8 +4,6 @@ project('nix-main-c', 'cpp',
|
||||
'cpp_std=c++2a',
|
||||
# TODO(Qyriad): increase the warning level
|
||||
'warning_level=1',
|
||||
'debug=true',
|
||||
'optimization=2',
|
||||
'errorlogs=true', # Please print logs for tests that fail
|
||||
],
|
||||
meson_version : '>= 1.1',
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
{ lib
|
||||
, stdenv
|
||||
, mkMesonLibrary
|
||||
|
||||
, nix-util-c
|
||||
@@ -51,10 +50,6 @@ mkMesonLibrary (finalAttrs: {
|
||||
mesonFlags = [
|
||||
];
|
||||
|
||||
env = lib.optionalAttrs (stdenv.isLinux && !(stdenv.hostPlatform.isStatic && stdenv.system == "aarch64-linux")) {
|
||||
LDFLAGS = "-fuse-ld=gold";
|
||||
};
|
||||
|
||||
meta = {
|
||||
platforms = lib.platforms.unix ++ lib.platforms.windows;
|
||||
};
|
||||
|
||||
@@ -4,8 +4,6 @@ project('nix-main', 'cpp',
|
||||
'cpp_std=c++2a',
|
||||
# TODO(Qyriad): increase the warning level
|
||||
'warning_level=1',
|
||||
'debug=true',
|
||||
'optimization=2',
|
||||
'errorlogs=true', # Please print logs for tests that fail
|
||||
],
|
||||
meson_version : '>= 1.1',
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
{ lib
|
||||
, stdenv
|
||||
, mkMesonLibrary
|
||||
|
||||
, openssl
|
||||
@@ -45,10 +44,6 @@ mkMesonLibrary (finalAttrs: {
|
||||
echo ${version} > ../../.version
|
||||
'';
|
||||
|
||||
env = lib.optionalAttrs (stdenv.isLinux && !(stdenv.hostPlatform.isStatic && stdenv.system == "aarch64-linux")) {
|
||||
LDFLAGS = "-fuse-ld=gold";
|
||||
};
|
||||
|
||||
meta = {
|
||||
platforms = lib.platforms.unix ++ lib.platforms.windows;
|
||||
};
|
||||
|
||||
@@ -287,23 +287,21 @@ public:
|
||||
|
||||
else if (type == resBuildLogLine || type == resPostBuildLogLine) {
|
||||
auto lastLine = chomp(getS(fields, 0));
|
||||
if (!lastLine.empty()) {
|
||||
auto i = state->its.find(act);
|
||||
assert(i != state->its.end());
|
||||
ActInfo info = *i->second;
|
||||
if (printBuildLogs) {
|
||||
auto suffix = "> ";
|
||||
if (type == resPostBuildLogLine) {
|
||||
suffix = " (post)> ";
|
||||
}
|
||||
log(*state, lvlInfo, ANSI_FAINT + info.name.value_or("unnamed") + suffix + ANSI_NORMAL + lastLine);
|
||||
} else {
|
||||
state->activities.erase(i->second);
|
||||
info.lastLine = lastLine;
|
||||
state->activities.emplace_back(info);
|
||||
i->second = std::prev(state->activities.end());
|
||||
update(*state);
|
||||
auto i = state->its.find(act);
|
||||
assert(i != state->its.end());
|
||||
ActInfo info = *i->second;
|
||||
if (printBuildLogs) {
|
||||
auto suffix = "> ";
|
||||
if (type == resPostBuildLogLine) {
|
||||
suffix = " (post)> ";
|
||||
}
|
||||
log(*state, lvlInfo, ANSI_FAINT + info.name.value_or("unnamed") + suffix + ANSI_NORMAL + lastLine);
|
||||
} else {
|
||||
state->activities.erase(i->second);
|
||||
info.lastLine = lastLine;
|
||||
state->activities.emplace_back(info);
|
||||
i->second = std::prev(state->activities.end());
|
||||
update(*state);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -4,8 +4,6 @@ project('nix-store-c', 'cpp',
|
||||
'cpp_std=c++2a',
|
||||
# TODO(Qyriad): increase the warning level
|
||||
'warning_level=1',
|
||||
'debug=true',
|
||||
'optimization=2',
|
||||
'errorlogs=true', # Please print logs for tests that fail
|
||||
],
|
||||
meson_version : '>= 1.1',
|
||||
|
||||
@@ -67,6 +67,17 @@ nix_err nix_store_get_uri(nix_c_context * context, Store * store, nix_get_string
|
||||
NIXC_CATCH_ERRS
|
||||
}
|
||||
|
||||
nix_err
|
||||
nix_store_get_storedir(nix_c_context * context, Store * store, nix_get_string_callback callback, void * user_data)
|
||||
{
|
||||
if (context)
|
||||
context->last_err_code = NIX_OK;
|
||||
try {
|
||||
return call_nix_get_string_callback(store->ptr->storeDir, callback, user_data);
|
||||
}
|
||||
NIXC_CATCH_ERRS
|
||||
}
|
||||
|
||||
nix_err
|
||||
nix_store_get_version(nix_c_context * context, Store * store, nix_get_string_callback callback, void * user_data)
|
||||
{
|
||||
@@ -89,6 +100,18 @@ bool nix_store_is_valid_path(nix_c_context * context, Store * store, StorePath *
|
||||
NIXC_CATCH_ERRS_RES(false);
|
||||
}
|
||||
|
||||
nix_err nix_store_real_path(
|
||||
nix_c_context * context, Store * store, StorePath * path, nix_get_string_callback callback, void * user_data)
|
||||
{
|
||||
if (context)
|
||||
context->last_err_code = NIX_OK;
|
||||
try {
|
||||
auto res = store->ptr->toRealPath(path->path);
|
||||
return call_nix_get_string_callback(res, callback, user_data);
|
||||
}
|
||||
NIXC_CATCH_ERRS
|
||||
}
|
||||
|
||||
StorePath * nix_store_parse_path(nix_c_context * context, Store * store, const char * path)
|
||||
{
|
||||
if (context)
|
||||
|
||||
@@ -48,12 +48,27 @@ nix_err nix_libstore_init_no_load_config(nix_c_context * context);
|
||||
* Store instances may share state and resources behind the scenes.
|
||||
*
|
||||
* @param[out] context Optional, stores error information
|
||||
* @param[in] uri URI of the Nix store, copied. See [*Store URL format* in the Nix Reference
|
||||
*
|
||||
* @param[in] uri @parblock
|
||||
* URI of the Nix store, copied.
|
||||
*
|
||||
* If `NULL`, the store from the settings will be used.
|
||||
* Note that `"auto"` holds a strange middle ground, reading part of the general environment, but not all of it. It
|
||||
* ignores `NIX_REMOTE` and the `store` option. For this reason, `NULL` is most likely the better choice.
|
||||
*
|
||||
* For supported store URLs, see [*Store URL format* in the Nix Reference
|
||||
* Manual](https://nixos.org/manual/nix/stable/store/types/#store-url-format).
|
||||
* @param[in] params optional, null-terminated array of key-value pairs, e.g. {{"endpoint",
|
||||
* "https://s3.local"}}. See [*Store Types* in the Nix Reference
|
||||
* Manual](https://nixos.org/manual/nix/stable/store/types).
|
||||
* @endparblock
|
||||
*
|
||||
* @param[in] params @parblock
|
||||
* optional, null-terminated array of key-value pairs, e.g. {{"endpoint",
|
||||
* "https://s3.local"}}.
|
||||
*
|
||||
* See [*Store Types* in the Nix Reference Manual](https://nixos.org/manual/nix/stable/store/types).
|
||||
* @endparblock
|
||||
*
|
||||
* @return a Store pointer, NULL in case of errors
|
||||
*
|
||||
* @see nix_store_free
|
||||
*/
|
||||
Store * nix_store_open(nix_c_context * context, const char * uri, const char *** params);
|
||||
@@ -78,7 +93,18 @@ void nix_store_free(Store * store);
|
||||
*/
|
||||
nix_err nix_store_get_uri(nix_c_context * context, Store * store, nix_get_string_callback callback, void * user_data);
|
||||
|
||||
// returns: owned StorePath*
|
||||
/**
|
||||
* @brief get the storeDir of a Nix store, typically `"/nix/store"`
|
||||
* @param[out] context Optional, stores error information
|
||||
* @param[in] store nix store reference
|
||||
* @param[in] callback Called with the URI.
|
||||
* @param[in] user_data optional, arbitrary data, passed to the callback when it's called.
|
||||
* @see nix_get_string_callback
|
||||
* @return error code, NIX_OK on success.
|
||||
*/
|
||||
nix_err
|
||||
nix_store_get_storedir(nix_c_context * context, Store * store, nix_get_string_callback callback, void * user_data);
|
||||
|
||||
/**
|
||||
* @brief Parse a Nix store path into a StorePath
|
||||
*
|
||||
@@ -123,6 +149,26 @@ void nix_store_path_free(StorePath * p);
|
||||
* @return true or false, error info in context
|
||||
*/
|
||||
bool nix_store_is_valid_path(nix_c_context * context, Store * store, StorePath * path);
|
||||
|
||||
/**
|
||||
* @brief Get the physical location of a store path
|
||||
*
|
||||
* A store may reside at a different location than its `storeDir` suggests.
|
||||
* This situation is called a relocated store.
|
||||
* Relocated stores are used during NixOS installation, as well as in restricted computing environments that don't offer
|
||||
* a writable `/nix/store`.
|
||||
*
|
||||
* Not all types of stores support this operation.
|
||||
*
|
||||
* @param[in] context Optional, stores error information
|
||||
* @param[in] store nix store reference
|
||||
* @param[in] path the path to get the real path from
|
||||
* @param[in] callback called with the real path
|
||||
* @param[in] user_data arbitrary data, passed to the callback when it's called.
|
||||
*/
|
||||
nix_err nix_store_real_path(
|
||||
nix_c_context * context, Store * store, StorePath * path, nix_get_string_callback callback, void * user_data);
|
||||
|
||||
// nix_err nix_store_ensure(Store*, const char*);
|
||||
// nix_err nix_store_build_paths(Store*);
|
||||
/**
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
{ lib
|
||||
, stdenv
|
||||
, mkMesonLibrary
|
||||
|
||||
, nix-util-c
|
||||
@@ -47,10 +46,6 @@ mkMesonLibrary (finalAttrs: {
|
||||
mesonFlags = [
|
||||
];
|
||||
|
||||
env = lib.optionalAttrs (stdenv.isLinux && !(stdenv.hostPlatform.isStatic && stdenv.system == "aarch64-linux")) {
|
||||
LDFLAGS = "-fuse-ld=gold";
|
||||
};
|
||||
|
||||
meta = {
|
||||
platforms = lib.platforms.unix ++ lib.platforms.windows;
|
||||
};
|
||||
|
||||
@@ -4,8 +4,6 @@ project('nix-store-test-support', 'cpp',
|
||||
'cpp_std=c++2a',
|
||||
# TODO(Qyriad): increase the warning level
|
||||
'warning_level=1',
|
||||
'debug=true',
|
||||
'optimization=2',
|
||||
'errorlogs=true', # Please print logs for tests that fail
|
||||
],
|
||||
meson_version : '>= 1.1',
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user