Compare commits
479 Commits
eval-optim
...
2.6.1
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e044ccb67c | ||
|
|
d0632f4c48 | ||
|
|
573b3aab66 | ||
|
|
5d00dc9d67 | ||
|
|
bda64b4b65 | ||
|
|
a07ecbc548 | ||
|
|
32a7724c22 | ||
|
|
45ed4e6178 | ||
|
|
21c23031c1 | ||
|
|
a1cd7e5860 | ||
|
|
039c9008b1 | ||
|
|
9faad4c5eb | ||
|
|
9ce2511206 | ||
|
|
716a08827e | ||
|
|
8cbbaf23e8 | ||
|
|
e66550c917 | ||
|
|
bed8270c0c | ||
|
|
c4fc9b6a8d | ||
|
|
3ff6186af1 | ||
|
|
7afbdf2545 | ||
|
|
4530574363 | ||
|
|
edf0cde1a7 | ||
|
|
5c3b6fbe00 | ||
|
|
3782017272 | ||
|
|
8c778e3d9d | ||
|
|
1e2bfd3da5 | ||
|
|
076945c808 | ||
|
|
ffb28eaa1e | ||
|
|
040cb7304a | ||
|
|
c9a4ddb9c0 | ||
|
|
fd5aa6ee3e | ||
|
|
12578ba672 | ||
|
|
7477974b93 | ||
|
|
f82a426502 | ||
|
|
1e0b7cdc3f | ||
|
|
0407436b0f | ||
|
|
5f08db69d1 | ||
|
|
d463e11760 | ||
|
|
c79087eb2a | ||
|
|
a078a645da | ||
|
|
128098040b | ||
|
|
e85cf34ea3 | ||
|
|
accfcfff6b | ||
|
|
c7223db871 | ||
|
|
fa53250c36 | ||
|
|
5ee937523d | ||
|
|
b03fe13b5b | ||
|
|
9510ad10c5 | ||
|
|
067076287b | ||
|
|
89f8917a32 | ||
|
|
87fdd23025 | ||
|
|
92e8230215 | ||
|
|
dec7748119 | ||
|
|
624f18ad90 | ||
|
|
6dd271b7b4 | ||
|
|
55c58580be | ||
|
|
d038a67bd3 | ||
|
|
e36add56cf | ||
|
|
dd7c2e0695 | ||
|
|
bc443511eb | ||
|
|
7d4f86f032 | ||
|
|
3e5a9ad7ff | ||
|
|
4af88a4c91 | ||
|
|
9901cb96c7 | ||
|
|
d6e3fbc6f5 | ||
|
|
04432f2510 | ||
|
|
5fe1ec8a05 | ||
|
|
3876238546 | ||
|
|
2ad2678c0b | ||
|
|
5b243a2b4b | ||
|
|
4dda1f92aa | ||
|
|
6448ea84ab | ||
|
|
f6f0bcf11f | ||
|
|
50be51d9a8 | ||
|
|
3157028fc1 | ||
|
|
d62a9390fc | ||
|
|
dc4730ee94 | ||
|
|
5753f6efbb | ||
|
|
52ee7ec002 | ||
|
|
776eb97a43 | ||
|
|
fc2443a67c | ||
|
|
34b66aab00 | ||
|
|
8cf54f754d | ||
|
|
008ddef4b0 | ||
|
|
bb24d1edd7 | ||
|
|
e1720b8e9d | ||
|
|
84507daaaa | ||
|
|
dd3aa1e515 | ||
|
|
3fff0196cd | ||
|
|
34f2eebd5b | ||
|
|
b9f5dccdbe | ||
|
|
f055cc5a0b | ||
|
|
1dace02866 | ||
|
|
de4489a672 | ||
|
|
e3690ab393 | ||
|
|
2dead20924 | ||
|
|
ad60dfde2a | ||
|
|
c9fc975259 | ||
|
|
81cd0a113b | ||
|
|
72f42093e7 | ||
|
|
34e3bd10e3 | ||
|
|
eee0bcee22 | ||
|
|
61a9d16d5c | ||
|
|
5e9653c370 | ||
|
|
66424cd29f | ||
|
|
6401e443a4 | ||
|
|
ef45787aae | ||
|
|
1bebb1095a | ||
|
|
44c92a1667 | ||
|
|
a2811f8499 | ||
|
|
a51c457204 | ||
|
|
e61c4bc25a | ||
|
|
73fcc40fa4 | ||
|
|
5838354d34 | ||
|
|
2267c773f0 | ||
|
|
9aa486c4be | ||
|
|
89b4df8d92 | ||
|
|
d023903b6f | ||
|
|
8a446aff75 | ||
|
|
9f9f39a24b | ||
|
|
e9a4abdb5d | ||
|
|
f39d94a55b | ||
|
|
19fd6e585d | ||
|
|
df11e75d0e | ||
|
|
120ca245d1 | ||
|
|
c66865dff1 | ||
|
|
fcddaa4b9b | ||
|
|
e1a0359b59 | ||
|
|
be64fb9b51 | ||
|
|
26a8b220eb | ||
|
|
e36fdbbfd9 | ||
|
|
f71d84672b | ||
|
|
5c7d3b351f | ||
|
|
3ec02c6743 | ||
|
|
65257614ea | ||
|
|
bdc577936f | ||
|
|
6d7844695b | ||
|
|
2b4c944823 | ||
|
|
17daec0b83 | ||
|
|
ca5baf2392 | ||
|
|
ed93aec3c3 | ||
|
|
263a8d293c | ||
|
|
cc08364315 | ||
|
|
6d9a6d2cc3 | ||
|
|
8f214a3771 | ||
|
|
9dd45dfc3d | ||
|
|
1ffacad8a5 | ||
|
|
00c993f48b | ||
|
|
96d08fcd66 | ||
|
|
70dfcbbb37 | ||
|
|
d7b286fc77 | ||
|
|
1c099dee67 | ||
|
|
d9aabb7acb | ||
|
|
d0c8e9254e | ||
|
|
a26351da02 | ||
|
|
ed3bc63666 | ||
|
|
92c7d33ee3 | ||
|
|
2dc29e0d93 | ||
|
|
581f774284 | ||
|
|
2664a216e5 | ||
|
|
0e90b13ab1 | ||
|
|
a4ab0a74d9 | ||
|
|
f404189368 | ||
|
|
af553b2090 | ||
|
|
b16643b6fc | ||
|
|
6ca2db2dad | ||
|
|
dc89dfa7b3 | ||
|
|
7feb741e00 | ||
|
|
b666a2ca8c | ||
|
|
c4a03bc4ae | ||
|
|
1da1b2b345 | ||
|
|
9747ea84b4 | ||
|
|
ddd7839154 | ||
|
|
5b5f68f1dd | ||
|
|
0a7c1da9f3 | ||
|
|
dc83298449 | ||
|
|
534bc5a3d1 | ||
|
|
89faff93e2 | ||
|
|
881b610266 | ||
|
|
25cea2b737 | ||
|
|
3228a38e1c | ||
|
|
d90f9d4b99 | ||
|
|
cbbd21ec07 | ||
|
|
de273bbff2 | ||
|
|
09b245690a | ||
|
|
9dea5768ef | ||
|
|
1827cf5a9b | ||
|
|
5d06836b9f | ||
|
|
10a8b5d3ae | ||
|
|
c3f68b5db7 | ||
|
|
6e6e998930 | ||
|
|
23ea1e46cc | ||
|
|
ab902521b1 | ||
|
|
6f1b3d711e | ||
|
|
ec8f24ed3a | ||
|
|
c260640dec | ||
|
|
8c46629b83 | ||
|
|
d9fbe3e208 | ||
|
|
7caf9f33f5 | ||
|
|
6942ee8a84 | ||
|
|
59a5f35802 | ||
|
|
c4862fb1f9 | ||
|
|
c3f9344cae | ||
|
|
11ee875a6d | ||
|
|
f66923efde | ||
|
|
1b6adfe18f | ||
|
|
18e4851752 | ||
|
|
8868da45a8 | ||
|
|
99d617bcde | ||
|
|
bb8a53ab08 | ||
|
|
6234e1c811 | ||
|
|
3fc8042f77 | ||
|
|
bcd4d2e4c6 | ||
|
|
02cf49288a | ||
|
|
95bd5da341 | ||
|
|
ade870764a | ||
|
|
32a62b0d25 | ||
|
|
46d86e06ba | ||
|
|
9529de2eed | ||
|
|
18cb094aab | ||
|
|
6ae5f39ea0 | ||
|
|
e2e5f3a78f | ||
|
|
cc6406cc59 | ||
|
|
2567b74c66 | ||
|
|
775bdc0d9e | ||
|
|
d17d46cfc2 | ||
|
|
df1edd143f | ||
|
|
05fcc681ac | ||
|
|
6d30f9e6fe | ||
|
|
2eec2f765a | ||
|
|
55dbb7f1cc | ||
|
|
f133001dc8 | ||
|
|
3f417c8d1c | ||
|
|
853771b589 | ||
|
|
84497119ad | ||
|
|
ec33014e23 | ||
|
|
33e96820d5 | ||
|
|
3542d4fe16 | ||
|
|
f24ac3115f | ||
|
|
0e0de90b35 | ||
|
|
be1055f2cc | ||
|
|
20b1290103 | ||
|
|
f9437b4f18 | ||
|
|
4194b68250 | ||
|
|
5d64c33fa5 | ||
|
|
c7b66caaca | ||
|
|
4ce112406b | ||
|
|
0bd93ed747 | ||
|
|
55e3a1108c | ||
|
|
bc57b3854e | ||
|
|
971382cab0 | ||
|
|
75654bacc5 | ||
|
|
9b2ff20e3b | ||
|
|
b6d08a9e3f | ||
|
|
167e864697 | ||
|
|
92ff5b4254 | ||
|
|
21206caf13 | ||
|
|
853ef1304c | ||
|
|
8b5088b62f | ||
|
|
a5e9b5428f | ||
|
|
ae21aab456 | ||
|
|
33926ed1e7 | ||
|
|
0a2fa2d684 | ||
|
|
d1aaa7ef71 | ||
|
|
fa58bff1ab | ||
|
|
76af9375c1 | ||
|
|
8093456111 | ||
|
|
d9b3adca75 | ||
|
|
52f5fa948a | ||
|
|
ad9322a48f | ||
|
|
b6cc0a704d | ||
|
|
2e606e87c4 | ||
|
|
6db9ca8ca4 | ||
|
|
69b9198875 | ||
|
|
90700736c7 | ||
|
|
2ff71b0213 | ||
|
|
77b3e4b4ee | ||
|
|
b42c1bea42 | ||
|
|
77141dded4 | ||
|
|
8cbf862e6f | ||
|
|
3bf7a868ee | ||
|
|
7489811736 | ||
|
|
c32a5f4d38 | ||
|
|
782837d934 | ||
|
|
fb662e0acf | ||
|
|
21520297da | ||
|
|
f3f32f0c30 | ||
|
|
2f5c913d4a | ||
|
|
95157b4e66 | ||
|
|
70a717f7a8 | ||
|
|
1f15441103 | ||
|
|
1a9bfdc4ca | ||
|
|
d22ce8b68d | ||
|
|
092c375cda | ||
|
|
5f64b69d23 | ||
|
|
a473e85c80 | ||
|
|
b96164f4af | ||
|
|
bd628cf3da | ||
|
|
92bcd77a67 | ||
|
|
6e0cbc666b | ||
|
|
e1420c66a4 | ||
|
|
ddf4fb750d | ||
|
|
99804465af | ||
|
|
3d36f48acb | ||
|
|
5c9a1ef30c | ||
|
|
45abf0fa5f | ||
|
|
90d8178009 | ||
|
|
a9bd06d0ea | ||
|
|
b73a1c0638 | ||
|
|
33a227503a | ||
|
|
1d5a881da5 | ||
|
|
3a0277305a | ||
|
|
152e3cda0c | ||
|
|
55275fcc59 | ||
|
|
3ef66cd23a | ||
|
|
b653fb9ccf | ||
|
|
c18238d92a | ||
|
|
6d166d19a6 | ||
|
|
288c252570 | ||
|
|
e588f4c655 | ||
|
|
05081bedc1 | ||
|
|
5fcf7f04a9 | ||
|
|
d5d0621250 | ||
|
|
6f46434f32 | ||
|
|
986906e687 | ||
|
|
1d2dbbb977 | ||
|
|
b6c8e57056 | ||
|
|
ca82967ee3 | ||
|
|
c47027f3a1 | ||
|
|
329b18711e | ||
|
|
1f7584d24c | ||
|
|
8388d2c7c6 | ||
|
|
d58f149140 | ||
|
|
2970ca18bf | ||
|
|
884674a8e2 | ||
|
|
09471d2680 | ||
|
|
6f4d1af971 | ||
|
|
585e97fa51 | ||
|
|
7a2c88add6 | ||
|
|
e7906ffd0e | ||
|
|
52c84c15e5 | ||
|
|
286eb81143 | ||
|
|
d41af23a6c | ||
|
|
b26cb0c9ac | ||
|
|
ee5f291709 | ||
|
|
861404a87b | ||
|
|
cd72a8c346 | ||
|
|
dbfcaa607a | ||
|
|
394506bc10 | ||
|
|
5be8fbd740 | ||
|
|
08b1ac3e38 | ||
|
|
b367f1061c | ||
|
|
f3ef2263bb | ||
|
|
f68699963c | ||
|
|
720ed47678 | ||
|
|
dcaa8dfd10 | ||
|
|
a2c7cf9cbd | ||
|
|
800e6e8194 | ||
|
|
db2e4489a5 | ||
|
|
0768c08d99 | ||
|
|
664ee49e0d | ||
|
|
4318ba2ec5 | ||
|
|
86b7962807 | ||
|
|
a18d9269a5 | ||
|
|
eff48e84d9 | ||
|
|
9cd8cffefc | ||
|
|
2eefdc7208 | ||
|
|
d5b36bdb58 | ||
|
|
f8d0311e75 | ||
|
|
06fb6aecea | ||
|
|
9de324f554 | ||
|
|
f836e3e2ce | ||
|
|
4b28798bfc | ||
|
|
e1192116d3 | ||
|
|
e88fb63e28 | ||
|
|
79f27500a4 | ||
|
|
0961c1068a | ||
|
|
72e67c4b2d | ||
|
|
f4ae804b35 | ||
|
|
0adced4b9e | ||
|
|
262a3c7ce3 | ||
|
|
acf381b061 | ||
|
|
205655e98b | ||
|
|
1d0c6a4b99 | ||
|
|
9e845df339 | ||
|
|
9653858ce6 | ||
|
|
bc14465e08 | ||
|
|
ca4d8ce9e2 | ||
|
|
e96faadcd6 | ||
|
|
46d2a5a10b | ||
|
|
480c883f36 | ||
|
|
d03e89e5d1 | ||
|
|
6d46b5b609 | ||
|
|
b191213b8b | ||
|
|
d7bae52b9d | ||
|
|
9a9afca712 | ||
|
|
e41cf8511f | ||
|
|
8368a8aff1 | ||
|
|
8c93a481af | ||
|
|
51ffc19f02 | ||
|
|
ec608e3def | ||
|
|
6463eaca14 | ||
|
|
6d0aa8d175 | ||
|
|
3771f931bf | ||
|
|
4ba355e593 | ||
|
|
9fe0343bfd | ||
|
|
671817a858 | ||
|
|
d1d223838b | ||
|
|
c5c7cca2be | ||
|
|
1d0bc96c96 | ||
|
|
164179983e | ||
|
|
ed33033926 | ||
|
|
a10a72b4e0 | ||
|
|
79d07d0980 | ||
|
|
eae54f2d52 | ||
|
|
d0e9e18489 | ||
|
|
83af9550a1 | ||
|
|
30496af598 | ||
|
|
bceda30498 | ||
|
|
f7afc26803 | ||
|
|
44aed58538 | ||
|
|
c78155b436 | ||
|
|
30e5c5c55f | ||
|
|
c1dea92dd6 | ||
|
|
a118a70649 | ||
|
|
c1bf9e39f1 | ||
|
|
93f7fb6e74 | ||
|
|
3884f7a69a | ||
|
|
07bffe7998 | ||
|
|
cc78901ccb | ||
|
|
52a3b2ee63 | ||
|
|
a218cfd6c7 | ||
|
|
edfc5b2f12 | ||
|
|
ceeecf2f9e | ||
|
|
67179472df | ||
|
|
f7859eef49 | ||
|
|
884ef336c4 | ||
|
|
0b005bc9d6 | ||
|
|
25d2316e8f | ||
|
|
69431edbc1 | ||
|
|
732dd90428 | ||
|
|
ff2af4d64e | ||
|
|
7a71621b7c | ||
|
|
6c2af1f201 | ||
|
|
d9c9d0e0eb | ||
|
|
736d6ab721 | ||
|
|
c9ecc0948b | ||
|
|
0fd96eeb09 | ||
|
|
b5cb31e032 | ||
|
|
d589782fb0 | ||
|
|
8e7359db64 | ||
|
|
9d4dcff37a | ||
|
|
3d6ee223d6 | ||
|
|
1f3c3a3785 | ||
|
|
93eadd5803 | ||
|
|
1a4c9ba50b | ||
|
|
abdf9f2a6e | ||
|
|
f4c869977c | ||
|
|
fbc70034b3 | ||
|
|
96670ed216 | ||
|
|
5b2aa61f1b | ||
|
|
b459a3e856 | ||
|
|
a50c027ece | ||
|
|
465a167c43 | ||
|
|
a2473823d7 | ||
|
|
769de259f0 | ||
|
|
8919b81dad | ||
|
|
ba81e871b2 | ||
|
|
b9234142f5 | ||
|
|
fa4abe46e2 | ||
|
|
fcb8af550f | ||
|
|
c345a4a1e8 | ||
|
|
ca8989daf3 | ||
|
|
85717eff15 | ||
|
|
d1da45855c | ||
|
|
9d67332e4b | ||
|
|
374fe49ff7 | ||
|
|
8438114399 | ||
|
|
136a613cce |
7
.github/PULL_REQUEST_TEMPLATE/pull_request_template.md
vendored
Normal file
7
.github/PULL_REQUEST_TEMPLATE/pull_request_template.md
vendored
Normal file
@@ -0,0 +1,7 @@
|
||||
**Release Notes**
|
||||
Please include relevant [release notes](https://github.com/NixOS/nix/blob/master/doc/manual/src/release-notes/rl-next.md) as needed.
|
||||
|
||||
|
||||
**Testing**
|
||||
|
||||
If this issue is a regression or something that should block release, please consider including a test either in the [testsuite](https://github.com/NixOS/nix/tree/master/tests) or as a [hydraJob]( https://github.com/NixOS/nix/blob/master/flake.nix#L396) so that it can be part of the [automatic checks](https://hydra.nixos.org/jobset/nix/master).
|
||||
26
.github/workflows/backport.yml
vendored
Normal file
26
.github/workflows/backport.yml
vendored
Normal file
@@ -0,0 +1,26 @@
|
||||
name: Backport
|
||||
on:
|
||||
pull_request_target:
|
||||
types: [closed, labeled]
|
||||
jobs:
|
||||
backport:
|
||||
name: Backport Pull Request
|
||||
if: github.repository_owner == 'NixOS' && github.event.pull_request.merged == true && (github.event_name != 'labeled' || startsWith('backport', github.event.label.name))
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
with:
|
||||
ref: ${{ github.event.pull_request.head.sha }}
|
||||
# required to find all branches
|
||||
fetch-depth: 0
|
||||
- name: Create backport PRs
|
||||
# should be kept in sync with `version`
|
||||
uses: zeebe-io/backport-action@v0.0.7
|
||||
with:
|
||||
# Config README: https://github.com/zeebe-io/backport-action#backport-action
|
||||
github_token: ${{ secrets.GITHUB_TOKEN }}
|
||||
github_workspace: ${{ github.workspace }}
|
||||
pull_description: |-
|
||||
Bot-based backport to `${target_branch}`, triggered by a label in #${pull_number}.
|
||||
# should be kept in sync with `uses`
|
||||
version: v0.0.5
|
||||
@@ -1,4 +1,4 @@
|
||||
name: "Test"
|
||||
name: "CI"
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
@@ -14,10 +14,10 @@ jobs:
|
||||
runs-on: ${{ matrix.os }}
|
||||
timeout-minutes: 60
|
||||
steps:
|
||||
- uses: actions/checkout@v2.3.5
|
||||
- uses: actions/checkout@v2.4.0
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- uses: cachix/install-nix-action@v14.1
|
||||
- uses: cachix/install-nix-action@v16
|
||||
- run: echo CACHIX_NAME="$(echo $GITHUB_REPOSITORY-install-tests | tr "[A-Z]/" "[a-z]-")" >> $GITHUB_ENV
|
||||
- uses: cachix/cachix-action@v10
|
||||
if: needs.check_cachix.outputs.secret == 'true'
|
||||
@@ -46,11 +46,11 @@ jobs:
|
||||
outputs:
|
||||
installerURL: ${{ steps.prepare-installer.outputs.installerURL }}
|
||||
steps:
|
||||
- uses: actions/checkout@v2.3.5
|
||||
- uses: actions/checkout@v2.4.0
|
||||
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@v14.1
|
||||
- uses: cachix/install-nix-action@v16
|
||||
- uses: cachix/cachix-action@v10
|
||||
with:
|
||||
name: '${{ env.CACHIX_NAME }}'
|
||||
@@ -67,10 +67,42 @@ jobs:
|
||||
os: [ubuntu-latest, macos-latest]
|
||||
runs-on: ${{ matrix.os }}
|
||||
steps:
|
||||
- uses: actions/checkout@v2.3.5
|
||||
- uses: actions/checkout@v2.4.0
|
||||
- run: echo CACHIX_NAME="$(echo $GITHUB_REPOSITORY-install-tests | tr "[A-Z]/" "[a-z]-")" >> $GITHUB_ENV
|
||||
- uses: cachix/install-nix-action@v14.1
|
||||
- uses: cachix/install-nix-action@v16
|
||||
with:
|
||||
install_url: '${{needs.installer.outputs.installerURL}}'
|
||||
install_options: "--tarball-url-prefix https://${{ env.CACHIX_NAME }}.cachix.org/serve"
|
||||
- run: nix-instantiate -E 'builtins.currentTime' --eval
|
||||
|
||||
docker_push_image:
|
||||
needs: [check_cachix, tests]
|
||||
if: >-
|
||||
github.event_name == 'push' &&
|
||||
github.ref_name == 'master' &&
|
||||
needs.check_cachix.outputs.secret == 'true'
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2.4.0
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- uses: cachix/install-nix-action@v16
|
||||
- run: echo CACHIX_NAME="$(echo $GITHUB_REPOSITORY-install-tests | tr "[A-Z]/" "[a-z]-")" >> $GITHUB_ENV
|
||||
- run: echo NIX_VERSION="$(nix-instantiate --eval -E '(import ./default.nix).defaultPackage.${builtins.currentSystem}.version' | tr -d \")" >> $GITHUB_ENV
|
||||
- uses: cachix/cachix-action@v10
|
||||
if: needs.check_cachix.outputs.secret == 'true'
|
||||
with:
|
||||
name: '${{ env.CACHIX_NAME }}'
|
||||
signingKey: '${{ secrets.CACHIX_SIGNING_KEY }}'
|
||||
authToken: '${{ secrets.CACHIX_AUTH_TOKEN }}'
|
||||
- run: nix-build -A checks.$(nix-instantiate --eval -E 'builtins.currentSystem' --json).dockerImage
|
||||
- run: docker load -i ./result/image.tar.gz
|
||||
- run: docker tag nix:$NIX_VERSION nixos/nix:$NIX_VERSION
|
||||
- run: docker tag nix:$NIX_VERSION nixos/nix:master
|
||||
- name: Login to Docker Hub
|
||||
uses: docker/login-action@v1
|
||||
with:
|
||||
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
||||
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||
- run: docker push nixos/nix:$NIX_VERSION
|
||||
- run: docker push nixos/nix:master
|
||||
16
.github/workflows/hydra_status.yml
vendored
Normal file
16
.github/workflows/hydra_status.yml
vendored
Normal file
@@ -0,0 +1,16 @@
|
||||
name: Hydra status
|
||||
on:
|
||||
schedule:
|
||||
- cron: "12,42 * * * *"
|
||||
workflow_dispatch:
|
||||
jobs:
|
||||
check_hydra_status:
|
||||
name: Check Hydra status
|
||||
if: github.repository_owner == 'NixOS'
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2.4.0
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- run: bash scripts/check-hydra-status.sh
|
||||
|
||||
2
.gitignore
vendored
2
.gitignore
vendored
@@ -26,8 +26,6 @@ perl/Makefile.config
|
||||
|
||||
# /scripts/
|
||||
/scripts/nix-profile.sh
|
||||
/scripts/nix-reduce-build
|
||||
/scripts/nix-http-export.cgi
|
||||
/scripts/nix-profile-daemon.sh
|
||||
|
||||
# /src/libexpr/
|
||||
|
||||
1
Makefile
1
Makefile
@@ -10,6 +10,7 @@ makefiles = \
|
||||
src/libexpr/local.mk \
|
||||
src/libcmd/local.mk \
|
||||
src/nix/local.mk \
|
||||
src/nlohmann/local.mk \
|
||||
src/resolve-system-dependencies/local.mk \
|
||||
scripts/local.mk \
|
||||
misc/bash/local.mk \
|
||||
|
||||
15
configure.ac
15
configure.ac
@@ -188,17 +188,24 @@ PKG_CHECK_MODULES([EDITLINE], [libeditline], [CXXFLAGS="$EDITLINE_CFLAGS $CXXFLA
|
||||
[AC_MSG_ERROR([Nix requires libeditline; it was not found via pkg-config, but via its header, but required functions do not work. Maybe it is too old? >= 1.14 is required.])])
|
||||
])
|
||||
|
||||
# Look for libsodium, an optional dependency.
|
||||
# Look for libsodium.
|
||||
PKG_CHECK_MODULES([SODIUM], [libsodium], [CXXFLAGS="$SODIUM_CFLAGS $CXXFLAGS"])
|
||||
|
||||
# Look for libbrotli{enc,dec}.
|
||||
PKG_CHECK_MODULES([LIBBROTLI], [libbrotlienc libbrotlidec], [CXXFLAGS="$LIBBROTLI_CFLAGS $CXXFLAGS"])
|
||||
|
||||
# Look for libcpuid.
|
||||
have_libcpuid=
|
||||
if test "$machine_name" = "x86_64"; then
|
||||
PKG_CHECK_MODULES([LIBCPUID], [libcpuid], [CXXFLAGS="$LIBCPUID_CFLAGS $CXXFLAGS"])
|
||||
have_libcpuid=1
|
||||
AC_DEFINE([HAVE_LIBCPUID], [1], [Use libcpuid])
|
||||
AC_ARG_ENABLE([cpuid],
|
||||
AS_HELP_STRING([--disable-cpuid], [Do not determine microarchitecture levels with libcpuid (relevant to x86_64 only)]))
|
||||
if test "x$enable_cpuid" != "xno"; then
|
||||
PKG_CHECK_MODULES([LIBCPUID], [libcpuid],
|
||||
[CXXFLAGS="$LIBCPUID_CFLAGS $CXXFLAGS"
|
||||
have_libcpuid=1
|
||||
AC_DEFINE([HAVE_LIBCPUID], [1], [Use libcpuid])]
|
||||
)
|
||||
fi
|
||||
fi
|
||||
AC_SUBST(HAVE_LIBCPUID, [$have_libcpuid])
|
||||
|
||||
|
||||
@@ -8,17 +8,19 @@ concatStrings (map
|
||||
let option = options.${name}; in
|
||||
" - `${name}` \n\n"
|
||||
+ concatStrings (map (s: " ${s}\n") (splitLines option.description)) + "\n\n"
|
||||
+ " **Default:** " + (
|
||||
if option.value == "" || option.value == []
|
||||
then "*empty*"
|
||||
else if isBool option.value
|
||||
then (if option.value then "`true`" else "`false`")
|
||||
else
|
||||
# n.b. a StringMap value type is specified as a string, but
|
||||
# this shows the value type. The empty stringmap is "null" in
|
||||
# JSON, but that converts to "{ }" here.
|
||||
(if isAttrs option.value then "`\"\"`"
|
||||
else "`" + toString option.value + "`")) + "\n\n"
|
||||
+ (if option.documentDefault
|
||||
then " **Default:** " + (
|
||||
if option.value == "" || option.value == []
|
||||
then "*empty*"
|
||||
else if isBool option.value
|
||||
then (if option.value then "`true`" else "`false`")
|
||||
else
|
||||
# n.b. a StringMap value type is specified as a string, but
|
||||
# this shows the value type. The empty stringmap is "null" in
|
||||
# JSON, but that converts to "{ }" here.
|
||||
(if isAttrs option.value then "`\"\"`"
|
||||
else "`" + toString option.value + "`")) + "\n\n"
|
||||
else " **Default:** *machine-specific*")
|
||||
+ (if option.aliases != []
|
||||
then " **Deprecated alias:** " + (concatStringsSep ", " (map (s: "`${s}`") option.aliases)) + "\n\n"
|
||||
else "")
|
||||
|
||||
@@ -12,11 +12,13 @@ man-pages := $(foreach n, \
|
||||
clean-files += $(d)/*.1 $(d)/*.5 $(d)/*.8
|
||||
|
||||
# Provide a dummy environment for nix, so that it will not access files outside the macOS sandbox.
|
||||
# Set cores to 0 because otherwise nix show-config resolves the cores based on the current machine
|
||||
dummy-env = env -i \
|
||||
HOME=/dummy \
|
||||
NIX_CONF_DIR=/dummy \
|
||||
NIX_SSL_CERT_FILE=/dummy/no-ca-bundle.crt \
|
||||
NIX_STATE_DIR=/dummy
|
||||
NIX_STATE_DIR=/dummy \
|
||||
NIX_CONFIG='cores = 0'
|
||||
|
||||
nix-eval = $(dummy-env) $(bindir)/nix eval --experimental-features nix-command -I nix/corepkgs=corepkgs --store dummy:// --impure --raw
|
||||
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
- [Prerequisites](installation/prerequisites-source.md)
|
||||
- [Obtaining a Source Distribution](installation/obtaining-source.md)
|
||||
- [Building Nix from Source](installation/building-source.md)
|
||||
- [Using Nix within Docker](installation/installing-docker.md)
|
||||
- [Security](installation/nix-security.md)
|
||||
- [Single-User Mode](installation/single-user.md)
|
||||
- [Multi-User Mode](installation/multi-user.md)
|
||||
@@ -70,7 +71,8 @@
|
||||
- [Hacking](contributing/hacking.md)
|
||||
- [CLI guideline](contributing/cli-guideline.md)
|
||||
- [Release Notes](release-notes/release-notes.md)
|
||||
- [Release X.Y (202?-??-??)](release-notes/rl-next.md)
|
||||
- [Release 2.6 (2022-01-24)](release-notes/rl-2.6.md)
|
||||
- [Release 2.5 (2021-12-13)](release-notes/rl-2.5.md)
|
||||
- [Release 2.4 (2021-11-01)](release-notes/rl-2.4.md)
|
||||
- [Release 2.3 (2019-09-04)](release-notes/rl-2.3.md)
|
||||
- [Release 2.2 (2019-01-11)](release-notes/rl-2.2.md)
|
||||
|
||||
@@ -53,8 +53,8 @@ example, the following command allows you to build a derivation for
|
||||
$ uname
|
||||
Linux
|
||||
|
||||
$ nix build \
|
||||
'(with import <nixpkgs> { system = "x86_64-darwin"; }; runCommand "foo" {} "uname > $out")' \
|
||||
$ nix build --impure \
|
||||
--expr '(with import <nixpkgs> { system = "x86_64-darwin"; }; runCommand "foo" {} "uname > $out")' \
|
||||
--builders 'ssh://mac x86_64-darwin'
|
||||
[1/0/1 built, 0.0 MiB DL] building foo on ssh://mac
|
||||
|
||||
|
||||
@@ -16,8 +16,9 @@ By default Nix reads settings from the following places:
|
||||
will be loaded in reverse order.
|
||||
|
||||
Otherwise it will look for `nix/nix.conf` files in `XDG_CONFIG_DIRS`
|
||||
and `XDG_CONFIG_HOME`. If these are unset, it will look in
|
||||
`$HOME/.config/nix/nix.conf`.
|
||||
and `XDG_CONFIG_HOME`. If unset, `XDG_CONFIG_DIRS` defaults to
|
||||
`/etc/xdg`, and `XDG_CONFIG_HOME` defaults to `$HOME/.config`
|
||||
as per [XDG Base Directory Specification](https://specifications.freedesktop.org/basedir-spec/basedir-spec-latest.html).
|
||||
|
||||
- If `NIX_CONFIG` is set, its contents is treated as the contents of
|
||||
a configuration file.
|
||||
|
||||
@@ -238,7 +238,16 @@ a number of possible ways:
|
||||
|
||||
## Examples
|
||||
|
||||
To install a specific version of `gcc` from the active Nix expression:
|
||||
To install a package using a specific attribute path from the active Nix expression:
|
||||
|
||||
```console
|
||||
$ nix-env -iA gcc40mips
|
||||
installing `gcc-4.0.2'
|
||||
$ nix-env -iA xorg.xorgserver
|
||||
installing `xorg-server-1.2.0'
|
||||
```
|
||||
|
||||
To install a specific version of `gcc` using the derivation name:
|
||||
|
||||
```console
|
||||
$ nix-env --install gcc-3.3.2
|
||||
@@ -246,6 +255,9 @@ installing `gcc-3.3.2'
|
||||
uninstalling `gcc-3.1'
|
||||
```
|
||||
|
||||
Using attribute path for selecting a package is preferred,
|
||||
as it is much faster and there will not be multiple matches.
|
||||
|
||||
Note the previously installed version is removed, since
|
||||
`--preserve-installed` was not specified.
|
||||
|
||||
@@ -256,13 +268,6 @@ $ nix-env --install gcc
|
||||
installing `gcc-3.3.2'
|
||||
```
|
||||
|
||||
To install using a specific attribute:
|
||||
|
||||
```console
|
||||
$ nix-env -i -A gcc40mips
|
||||
$ nix-env -i -A xorg.xorgserver
|
||||
```
|
||||
|
||||
To install all derivations in the Nix expression `foo.nix`:
|
||||
|
||||
```console
|
||||
@@ -374,22 +379,29 @@ For the other flags, see `--install`.
|
||||
## Examples
|
||||
|
||||
```console
|
||||
$ nix-env --upgrade gcc
|
||||
$ nix-env --upgrade -A nixpkgs.gcc
|
||||
upgrading `gcc-3.3.1' to `gcc-3.4'
|
||||
```
|
||||
|
||||
When there are no updates available, nothing will happen:
|
||||
|
||||
```console
|
||||
$ nix-env -u gcc-3.3.2 --always (switch to a specific version)
|
||||
$ nix-env --upgrade -A nixpkgs.pan
|
||||
```
|
||||
|
||||
Using `-A` is preferred when possible, as it is faster and unambiguous but
|
||||
it is also possible to upgrade to a specific version by matching the derivation name:
|
||||
|
||||
```console
|
||||
$ nix-env -u gcc-3.3.2 --always
|
||||
upgrading `gcc-3.4' to `gcc-3.3.2'
|
||||
```
|
||||
|
||||
```console
|
||||
$ nix-env --upgrade pan
|
||||
(no upgrades available, so nothing happens)
|
||||
```
|
||||
To try to upgrade everything
|
||||
(matching packages based on the part of the derivation name without version):
|
||||
|
||||
```console
|
||||
$ nix-env -u (try to upgrade everything)
|
||||
$ nix-env -u
|
||||
upgrading `hello-2.1.2' to `hello-2.1.3'
|
||||
upgrading `mozilla-1.2' to `mozilla-1.4'
|
||||
```
|
||||
|
||||
@@ -101,7 +101,8 @@ The following common options are supported:
|
||||
|
||||
- `NIX_BUILD_SHELL`\
|
||||
Shell used to start the interactive environment. Defaults to the
|
||||
`bash` found in `PATH`.
|
||||
`bash` found in `<nixpkgs>`, falling back to the `bash` found in
|
||||
`PATH` if not found.
|
||||
|
||||
# Examples
|
||||
|
||||
|
||||
@@ -162,11 +162,11 @@ Most Nix commands accept the following command-line options:
|
||||
}: ...
|
||||
```
|
||||
|
||||
So if you call this Nix expression (e.g., when you do `nix-env -i
|
||||
So if you call this Nix expression (e.g., when you do `nix-env -iA
|
||||
pkgname`), the function will be called automatically using the
|
||||
value [`builtins.currentSystem`](../expressions/builtins.md) for
|
||||
the `system` argument. You can override this using `--arg`, e.g.,
|
||||
`nix-env -i pkgname --arg system \"i686-freebsd\"`. (Note that
|
||||
`nix-env -iA pkgname --arg system \"i686-freebsd\"`. (Note that
|
||||
since the argument is a Nix string literal, you have to escape the
|
||||
quotes.)
|
||||
|
||||
|
||||
@@ -103,7 +103,7 @@ impacted the most by bad user experience.
|
||||
# Help is essential
|
||||
|
||||
Help should be built into your command line so that new users can gradually
|
||||
discover new features when they need them.
|
||||
discover new features when they need them.
|
||||
|
||||
## Looking for help
|
||||
|
||||
@@ -176,7 +176,7 @@ $ nix init --template=template#pyton
|
||||
------------------------------------------------------------------------
|
||||
Initializing Nix project at `/path/to/here`.
|
||||
Select a template for you new project:
|
||||
|> template#pyton
|
||||
|> template#python
|
||||
template#python-pip
|
||||
template#python-poetry
|
||||
```
|
||||
@@ -237,10 +237,10 @@ love, but if not done perfectly it will annoy users and leave bad impression.
|
||||
|
||||
# Input
|
||||
|
||||
Input to a command is provided via `ARGUMENTS` and `OPTIONS`.
|
||||
Input to a command is provided via `ARGUMENTS` and `OPTIONS`.
|
||||
|
||||
`ARGUMENTS` represent a required input for a function. When choosing to use
|
||||
`ARGUMENT` over function please be aware of the downsides that come with it:
|
||||
`ARGUMENTS` over `OPTIONS` please be aware of the downsides that come with it:
|
||||
|
||||
- User will need to remember the order of `ARGUMENTS`. This is not a problem if
|
||||
there is only one `ARGUMENT`.
|
||||
@@ -253,7 +253,7 @@ developer consider the downsides and choose wisely.
|
||||
|
||||
## Naming the `OPTIONS`
|
||||
|
||||
Then only naming convention - apart from the ones mentioned in Naming the
|
||||
The only naming convention - apart from the ones mentioned in Naming the
|
||||
`COMMANDS` section is how flags are named.
|
||||
|
||||
Flags are a type of `OPTION` that represent an option that can be turned ON of
|
||||
@@ -271,7 +271,7 @@ to improve the discoverability of possible input. A new user will most likely
|
||||
not know which `ARGUMENTS` and `OPTIONS` are required or which values are
|
||||
possible for those options.
|
||||
|
||||
In cases, the user might not provide the input or they provide wrong input,
|
||||
In case the user does not provide the input or they provide wrong input,
|
||||
rather than show the error, prompt a user with an option to find and select
|
||||
correct input (see examples).
|
||||
|
||||
@@ -302,7 +302,7 @@ $ nix build --option substitutors https://cache.example.org
|
||||
------------------------------------------------------------------------
|
||||
Warning! A security related question needs to be answered.
|
||||
------------------------------------------------------------------------
|
||||
The following substitutors will be used to in `my-project`:
|
||||
The following substitutors will be used to in `my-project`:
|
||||
- https://cache.example.org
|
||||
|
||||
Do you allow `my-project` to use above mentioned substitutors?
|
||||
@@ -342,7 +342,7 @@ also allowing them to redirect content to a file. For example:
|
||||
```shell
|
||||
$ nix build > build.txt
|
||||
------------------------------------------------------------------------
|
||||
Error! Atrribute `bin` missing at (1:94) from string.
|
||||
Error! Attribute `bin` missing at (1:94) from string.
|
||||
------------------------------------------------------------------------
|
||||
|
||||
1| with import <nixpkgs> { }; (pkgs.runCommandCC or pkgs.runCommand) "shell" { buildInputs = [ (surge.bin) ]; } ""
|
||||
@@ -408,7 +408,7 @@ Above command clearly states that command successfully completed. And in case
|
||||
of `nix build`, which is a command that might take some time to complete, it is
|
||||
equally important to also show that a command started.
|
||||
|
||||
## Text alignment
|
||||
## Text alignment
|
||||
|
||||
Text alignment is the number one design element that will present all of the
|
||||
Nix commands as a family and not as separate tools glued together.
|
||||
@@ -419,7 +419,7 @@ The format we should follow is:
|
||||
$ nix COMMAND
|
||||
VERB_1 NOUN and other words
|
||||
VERB__1 NOUN and other words
|
||||
|> Some details
|
||||
|> Some details
|
||||
```
|
||||
|
||||
Few rules that we can extract from above example:
|
||||
@@ -444,13 +444,13 @@ is not even notable, therefore relying on it wouldn’t make much sense.
|
||||
|
||||
**The bright text is much better supported** across terminals and color
|
||||
schemes. Most of the time the difference is perceived as if the bright text
|
||||
would be bold.
|
||||
would be bold.
|
||||
|
||||
## Colors
|
||||
|
||||
Humans are already conditioned by society to attach certain meaning to certain
|
||||
colors. While the meaning is not universal, a simple collection of colors is
|
||||
used to represent basic emotions.
|
||||
used to represent basic emotions.
|
||||
|
||||
Colors that can be used in output
|
||||
|
||||
@@ -555,7 +555,7 @@ $ nix build --option substitutors https://cache.example.org
|
||||
------------------------------------------------------------------------
|
||||
Warning! A security related question needs to be answered.
|
||||
------------------------------------------------------------------------
|
||||
The following substitutors will be used to in `my-project`:
|
||||
The following substitutors will be used to in `my-project`:
|
||||
- https://cache.example.org
|
||||
|
||||
Do you allow `my-project` to use above mentioned substitutors?
|
||||
@@ -566,7 +566,7 @@ $ nix build --option substitutors https://cache.example.org
|
||||
|
||||
There are many ways that you can control verbosity.
|
||||
|
||||
Verbosity levels are:
|
||||
Verbosity levels are:
|
||||
|
||||
- `ERROR` (level 0)
|
||||
- `WARN` (level 1)
|
||||
@@ -586,4 +586,4 @@ There are also two shortcuts, `--debug` to run in `DEBUG` verbosity level and
|
||||
|
||||
# Appendix 1: Commands naming exceptions
|
||||
|
||||
`nix init` and `nix repl` are well established
|
||||
`nix init` and `nix repl` are well established
|
||||
|
||||
@@ -35,6 +35,25 @@ variables are set up so that those dependencies can be found:
|
||||
$ nix-shell
|
||||
```
|
||||
|
||||
or if you have a flake-enabled nix:
|
||||
|
||||
```console
|
||||
$ nix develop
|
||||
```
|
||||
|
||||
To get a shell with a different compilation environment (e.g. stdenv,
|
||||
gccStdenv, clangStdenv, clang11Stdenv):
|
||||
|
||||
```console
|
||||
$ nix-shell -A devShells.x86_64-linux.clang11StdenvPackages
|
||||
```
|
||||
|
||||
or if you have a flake-enabled nix:
|
||||
|
||||
```console
|
||||
$ nix develop .#clang11StdenvPackages
|
||||
```
|
||||
|
||||
To build Nix itself in this shell:
|
||||
|
||||
```console
|
||||
|
||||
@@ -12,5 +12,5 @@ For instance, `derivation` is also available as `builtins.derivation`.
|
||||
<dl>
|
||||
<dt><code>derivation <var>attrs</var></code>;
|
||||
<code>builtins.derivation <var>attrs</var></code></dt>
|
||||
<dd><p><var>derivation</var> in described in
|
||||
<dd><p><var>derivation</var> is described in
|
||||
<a href="derivations.md">its own section</a>.</p></dd>
|
||||
|
||||
@@ -284,6 +284,10 @@ The points of interest are:
|
||||
function is called with the `localServer` argument set to `true` but
|
||||
the `db4` argument set to `null`, then the evaluation fails.
|
||||
|
||||
Note that `->` is the [logical
|
||||
implication](https://en.wikipedia.org/wiki/Truth_table#Logical_implication)
|
||||
Boolean operation.
|
||||
|
||||
2. This is a more subtle condition: if Subversion is built with Apache
|
||||
(`httpServer`) support, then the Expat library (an XML library) used
|
||||
by Subversion should be same as the one used by Apache. This is
|
||||
|
||||
@@ -17,12 +17,12 @@ order of precedence (from strongest to weakest binding).
|
||||
| String Concatenation | *string1* `+` *string2* | left | String concatenation. | 7 |
|
||||
| Not | `!` *e* | none | Boolean negation. | 8 |
|
||||
| Update | *e1* `//` *e2* | right | Return a set consisting of the attributes in *e1* and *e2* (with the latter taking precedence over the former in case of equally named attributes). | 9 |
|
||||
| Less Than | *e1* `<` *e2*, | none | Arithmetic comparison. | 10 |
|
||||
| Less Than or Equal To | *e1* `<=` *e2* | none | Arithmetic comparison. | 10 |
|
||||
| Greater Than | *e1* `>` *e2* | none | Arithmetic comparison. | 10 |
|
||||
| Greater Than or Equal To | *e1* `>=` *e2* | none | Arithmetic comparison. | 10 |
|
||||
| Less Than | *e1* `<` *e2*, | none | Arithmetic/lexicographic comparison. | 10 |
|
||||
| Less Than or Equal To | *e1* `<=` *e2* | none | Arithmetic/lexicographic comparison. | 10 |
|
||||
| Greater Than | *e1* `>` *e2* | none | Arithmetic/lexicographic comparison. | 10 |
|
||||
| Greater Than or Equal To | *e1* `>=` *e2* | none | Arithmetic/lexicographic comparison. | 10 |
|
||||
| Equality | *e1* `==` *e2* | none | Equality. | 11 |
|
||||
| Inequality | *e1* `!=` *e2* | none | Inequality. | 11 |
|
||||
| Logical AND | *e1* `&&` *e2* | left | Logical AND. | 12 |
|
||||
| Logical OR | *e1* `\|\|` *e2* | left | Logical OR. | 13 |
|
||||
| Logical Implication | *e1* `->` *e2* | none | Logical implication (equivalent to `!e1 \|\| e2`). | 14 |
|
||||
| Logical OR | *e1* <code>||</code> *e2* | left | Logical OR. | 13 |
|
||||
| Logical Implication | *e1* `->` *e2* | none | Logical implication (equivalent to <code>!e1 || e2</code>). | 14 |
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# Building and Testing
|
||||
|
||||
You can now try to build Hello. Of course, you could do `nix-env -i
|
||||
You can now try to build Hello. Of course, you could do `nix-env -f . -iA
|
||||
hello`, but you may not want to install a possibly broken package just
|
||||
yet. The best way to test the package is by using the command
|
||||
`nix-build`, which builds a Nix expression and creates a symlink named
|
||||
|
||||
@@ -47,7 +47,7 @@
|
||||
the store object at `P` contains the path `Q` somewhere. The
|
||||
*references* of a store path are the set of store paths to which it
|
||||
has a reference.
|
||||
|
||||
|
||||
A derivation can reference other derivations and sources (but not
|
||||
output paths), whereas an output path only references other output
|
||||
paths.
|
||||
@@ -66,7 +66,7 @@
|
||||
is necessary to deploy whole closures, since otherwise at runtime
|
||||
files could be missing. The command `nix-store -qR` prints out
|
||||
closures of store paths.
|
||||
|
||||
|
||||
As an example, if the store object at path `P` contains a reference
|
||||
to path `Q`, then `Q` is in the closure of `P`. Further, if `Q`
|
||||
references `R` then `R` is also in the closure of `P`.
|
||||
@@ -98,3 +98,7 @@
|
||||
store. It can contain regular files, directories and symbolic
|
||||
links. NARs are generated and unpacked using `nix-store --dump`
|
||||
and `nix-store --restore`.
|
||||
- `∅` \
|
||||
The empty set symbol. In the context of profile history, this denotes a package is not present in a particular version of the profile.
|
||||
- `ε` \
|
||||
The epsilon symbol. In the context of a package, this means the version is empty. More precisely, the derivation does not have a version attribute.
|
||||
|
||||
@@ -119,6 +119,30 @@ this to run the installer, but it may help if you run into trouble:
|
||||
- update `/etc/synthetic.conf` to direct macOS to create a "synthetic"
|
||||
empty root directory to mount your volume
|
||||
- specify mount options for the volume in `/etc/fstab`
|
||||
- `rw`: read-write
|
||||
- `noauto`: prevent the system from auto-mounting the volume (so the
|
||||
LaunchDaemon mentioned below can control mounting it, and to avoid
|
||||
masking problems with that mounting service).
|
||||
- `nobrowse`: prevent the Nix Store volume from showing up on your
|
||||
desktop; also keeps Spotlight from spending resources to index
|
||||
this volume
|
||||
<!-- TODO:
|
||||
- `suid`: honor setuid? surely not? ...
|
||||
- `owners`: honor file ownership on the volume
|
||||
|
||||
For now I'll avoid pretending to understand suid/owners more
|
||||
than I do. There've been some vague reports of file-ownership
|
||||
and permission issues, particularly in cloud/VM/headless setups.
|
||||
My pet theory is that this has something to do with these setups
|
||||
not having a token that gets delegated to initial/admin accounts
|
||||
on macOS. See scripts/create-darwin-volume.sh for a little more.
|
||||
|
||||
In any case, by Dec 4 2021, it _seems_ like some combination of
|
||||
suid, owners, and calling diskutil enableOwnership have stopped
|
||||
new reports from coming in. But I hesitate to celebrate because we
|
||||
haven't really named and catalogued the behavior, understood what
|
||||
we're fixing, and validated that all 3 components are essential.
|
||||
-->
|
||||
- if you have FileVault enabled
|
||||
- generate an encryption password
|
||||
- put it in your system Keychain
|
||||
|
||||
59
doc/manual/src/installation/installing-docker.md
Normal file
59
doc/manual/src/installation/installing-docker.md
Normal file
@@ -0,0 +1,59 @@
|
||||
# Using Nix within Docker
|
||||
|
||||
To run the latest stable release of Nix with Docker run the following command:
|
||||
|
||||
```console
|
||||
$ docker run -ti nixos/nix
|
||||
Unable to find image 'nixos/nix:latest' locally
|
||||
latest: Pulling from nixos/nix
|
||||
5843afab3874: Pull complete
|
||||
b52bf13f109c: Pull complete
|
||||
1e2415612aa3: Pull complete
|
||||
Digest: sha256:27f6e7f60227e959ee7ece361f75d4844a40e1cc6878b6868fe30140420031ff
|
||||
Status: Downloaded newer image for nixos/nix:latest
|
||||
35ca4ada6e96:/# nix --version
|
||||
nix (Nix) 2.3.12
|
||||
35ca4ada6e96:/# exit
|
||||
```
|
||||
|
||||
# What is included in Nix's Docker image?
|
||||
|
||||
The official Docker image is created using `pkgs.dockerTools.buildLayeredImage`
|
||||
(and not with `Dockerfile` as it is usual with Docker images). You can still
|
||||
base your custom Docker image on it as you would do with any other Docker
|
||||
image.
|
||||
|
||||
The Docker image is also not based on any other image and includes minimal set
|
||||
of runtime dependencies that are required to use Nix:
|
||||
|
||||
- pkgs.nix
|
||||
- pkgs.bashInteractive
|
||||
- pkgs.coreutils-full
|
||||
- pkgs.gnutar
|
||||
- pkgs.gzip
|
||||
- pkgs.gnugrep
|
||||
- pkgs.which
|
||||
- pkgs.curl
|
||||
- pkgs.less
|
||||
- pkgs.wget
|
||||
- pkgs.man
|
||||
- pkgs.cacert.out
|
||||
- pkgs.findutils
|
||||
|
||||
# Docker image with the latest development version of Nix
|
||||
|
||||
To get the latest image that was built by [Hydra](https://hydra.nixos.org) run
|
||||
the following command:
|
||||
|
||||
```console
|
||||
$ curl -L https://hydra.nixos.org/job/nix/master/dockerImage.x86_64-linux/latest/download/1 | docker load
|
||||
$ docker run -ti nix:2.5pre20211105
|
||||
```
|
||||
|
||||
You can also build a Docker image from source yourself:
|
||||
|
||||
```console
|
||||
$ nix build ./\#hydraJobs.dockerImage.x86_64-linux
|
||||
$ docker load -i ./result/image.tar.gz
|
||||
$ docker run -ti nix:2.5pre20211105
|
||||
```
|
||||
@@ -44,6 +44,11 @@
|
||||
obtained from the its repository
|
||||
<https://github.com/troglobit/editline>.
|
||||
|
||||
- The `libsodium` library for verifying cryptographic signatures
|
||||
of contents fetched from binary caches.
|
||||
It can be obtained from the official web site
|
||||
<https://libsodium.org>.
|
||||
|
||||
- Recent versions of Bison and Flex to build the parser. (This is
|
||||
because Nix needs GLR support in Bison and reentrancy support in
|
||||
Flex.) For Bison, you need version 2.6, which can be obtained from
|
||||
@@ -58,3 +63,11 @@
|
||||
`--disable-seccomp-sandboxing` option to the `configure` script (Not
|
||||
recommended unless your system doesn't support `libseccomp`). To get
|
||||
the library, visit <https://github.com/seccomp/libseccomp>.
|
||||
|
||||
- On 64-bit x86 machines only, `libcpuid` library
|
||||
is used to determine which microarchitecture levels are supported
|
||||
(e.g., as whether to have `x86_64-v2-linux` among additional system types).
|
||||
The library is available from its homepage
|
||||
<http://libcpuid.sourceforge.net>.
|
||||
This is an optional dependency and can be disabled
|
||||
by providing a `--disable-cpuid` to the `configure` script.
|
||||
|
||||
@@ -4,4 +4,4 @@ Nix is currently supported on the following platforms:
|
||||
|
||||
- Linux (i686, x86\_64, aarch64).
|
||||
|
||||
- macOS (x86\_64).
|
||||
- macOS (x86\_64, aarch64).
|
||||
|
||||
@@ -76,7 +76,7 @@ there after an upgrade. This means that you can _roll back_ to the
|
||||
old version:
|
||||
|
||||
```console
|
||||
$ nix-env --upgrade some-packages
|
||||
$ nix-env --upgrade -A nixpkgs.some-package
|
||||
$ nix-env --rollback
|
||||
```
|
||||
|
||||
@@ -122,7 +122,7 @@ Nix expressions generally describe how to build a package from
|
||||
source, so an installation action like
|
||||
|
||||
```console
|
||||
$ nix-env --install firefox
|
||||
$ nix-env --install -A nixpkgs.firefox
|
||||
```
|
||||
|
||||
_could_ cause quite a bit of build activity, as not only Firefox but
|
||||
|
||||
@@ -24,7 +24,7 @@ collection; you could write your own Nix expressions based on Nixpkgs,
|
||||
or completely new ones.)
|
||||
|
||||
You can manually download the latest version of Nixpkgs from
|
||||
<http://nixos.org/nixpkgs/download.html>. However, it’s much more
|
||||
<https://github.com/NixOS/nixpkgs>. However, it’s much more
|
||||
convenient to use the Nixpkgs [*channel*](channels.md), since it makes
|
||||
it easy to stay up to date with new versions of Nixpkgs. Nixpkgs is
|
||||
automatically added to your list of “subscribed” channels when you
|
||||
@@ -40,48 +40,52 @@ $ nix-channel --update
|
||||
>
|
||||
> On NixOS, you’re automatically subscribed to a NixOS channel
|
||||
> corresponding to your NixOS major release (e.g.
|
||||
> <http://nixos.org/channels/nixos-14.12>). A NixOS channel is identical
|
||||
> <http://nixos.org/channels/nixos-21.11>). A NixOS channel is identical
|
||||
> to the Nixpkgs channel, except that it contains only Linux binaries
|
||||
> and is updated only if a set of regression tests succeed.
|
||||
|
||||
You can view the set of available packages in Nixpkgs:
|
||||
|
||||
```console
|
||||
$ nix-env -qa
|
||||
aterm-2.2
|
||||
bash-3.0
|
||||
binutils-2.15
|
||||
bison-1.875d
|
||||
blackdown-1.4.2
|
||||
bzip2-1.0.2
|
||||
$ nix-env -qaP
|
||||
nixpkgs.aterm aterm-2.2
|
||||
nixpkgs.bash bash-3.0
|
||||
nixpkgs.binutils binutils-2.15
|
||||
nixpkgs.bison bison-1.875d
|
||||
nixpkgs.blackdown blackdown-1.4.2
|
||||
nixpkgs.bzip2 bzip2-1.0.2
|
||||
…
|
||||
```
|
||||
|
||||
The flag `-q` specifies a query operation, and `-a` means that you want
|
||||
The flag `-q` specifies a query operation, `-a` means that you want
|
||||
to show the “available” (i.e., installable) packages, as opposed to the
|
||||
installed packages. If you downloaded Nixpkgs yourself, or if you
|
||||
checked it out from GitHub, then you need to pass the path to your
|
||||
Nixpkgs tree using the `-f` flag:
|
||||
installed packages, and `-P` prints the attribute paths that can be used
|
||||
to unambiguously select a package for installation (listed in the first column).
|
||||
If you downloaded Nixpkgs yourself, or if you checked it out from GitHub,
|
||||
then you need to pass the path to your Nixpkgs tree using the `-f` flag:
|
||||
|
||||
```console
|
||||
$ nix-env -qaf /path/to/nixpkgs
|
||||
$ nix-env -qaPf /path/to/nixpkgs
|
||||
aterm aterm-2.2
|
||||
bash bash-3.0
|
||||
…
|
||||
```
|
||||
|
||||
where */path/to/nixpkgs* is where you’ve unpacked or checked out
|
||||
Nixpkgs.
|
||||
|
||||
You can select specific packages by name:
|
||||
You can filter the packages by name:
|
||||
|
||||
```console
|
||||
$ nix-env -qa firefox
|
||||
firefox-34.0.5
|
||||
firefox-with-plugins-34.0.5
|
||||
$ nix-env -qaP firefox
|
||||
nixpkgs.firefox-esr firefox-91.3.0esr
|
||||
nixpkgs.firefox firefox-94.0.1
|
||||
```
|
||||
|
||||
and using regular expressions:
|
||||
|
||||
```console
|
||||
$ nix-env -qa 'firefox.*'
|
||||
$ nix-env -qaP 'firefox.*'
|
||||
```
|
||||
|
||||
It is also possible to see the *status* of available packages, i.e.,
|
||||
@@ -89,11 +93,11 @@ whether they are installed into the user environment and/or present in
|
||||
the system:
|
||||
|
||||
```console
|
||||
$ nix-env -qas
|
||||
$ nix-env -qaPs
|
||||
…
|
||||
-PS bash-3.0
|
||||
--S binutils-2.15
|
||||
IPS bison-1.875d
|
||||
-PS nixpkgs.bash bash-3.0
|
||||
--S nixpkgs.binutils binutils-2.15
|
||||
IPS nixpkgs.bison bison-1.875d
|
||||
…
|
||||
```
|
||||
|
||||
@@ -106,13 +110,13 @@ which is Nix’s mechanism for doing binary deployment. It just means that
|
||||
Nix knows that it can fetch a pre-built package from somewhere
|
||||
(typically a network server) instead of building it locally.
|
||||
|
||||
You can install a package using `nix-env -i`. For instance,
|
||||
You can install a package using `nix-env -iA`. For instance,
|
||||
|
||||
```console
|
||||
$ nix-env -i subversion
|
||||
$ nix-env -iA nixpkgs.subversion
|
||||
```
|
||||
|
||||
will install the package called `subversion` (which is, of course, the
|
||||
will install the package called `subversion` from `nixpkgs` channel (which is, of course, the
|
||||
[Subversion version management system](http://subversion.tigris.org/)).
|
||||
|
||||
> **Note**
|
||||
@@ -122,7 +126,7 @@ will install the package called `subversion` (which is, of course, the
|
||||
> binary cache <https://cache.nixos.org>; it contains binaries for most
|
||||
> packages in Nixpkgs. Only if no binary is available in the binary
|
||||
> cache, Nix will build the package from source. So if `nix-env
|
||||
> -i subversion` results in Nix building stuff from source, then either
|
||||
> -iA nixpkgs.subversion` results in Nix building stuff from source, then either
|
||||
> the package is not built for your platform by the Nixpkgs build
|
||||
> servers, or your version of Nixpkgs is too old or too new. For
|
||||
> instance, if you have a very recent checkout of Nixpkgs, then the
|
||||
@@ -133,7 +137,10 @@ will install the package called `subversion` (which is, of course, the
|
||||
> using a Git checkout of the Nixpkgs tree), you will get binaries for
|
||||
> most packages.
|
||||
|
||||
Naturally, packages can also be uninstalled:
|
||||
Naturally, packages can also be uninstalled. Unlike when installing, you will
|
||||
need to use the derivation name (though the version part can be omitted),
|
||||
instead of the attribute path, as `nix-env` does not record which attribute
|
||||
was used for installing:
|
||||
|
||||
```console
|
||||
$ nix-env -e subversion
|
||||
@@ -143,7 +150,7 @@ Upgrading to a new version is just as easy. If you have a new release of
|
||||
Nix Packages, you can do:
|
||||
|
||||
```console
|
||||
$ nix-env -u subversion
|
||||
$ nix-env -uA nixpkgs.subversion
|
||||
```
|
||||
|
||||
This will *only* upgrade Subversion if there is a “newer” version in the
|
||||
|
||||
@@ -9,7 +9,7 @@ The daemon that handles binary cache requests via HTTP, `nix-serve`, is
|
||||
not part of the Nix distribution, but you can install it from Nixpkgs:
|
||||
|
||||
```console
|
||||
$ nix-env -i nix-serve
|
||||
$ nix-env -iA nixpkgs.nix-serve
|
||||
```
|
||||
|
||||
You can then start the server, listening for HTTP connections on
|
||||
@@ -35,7 +35,7 @@ On the client side, you can tell Nix to use your binary cache using
|
||||
`--option extra-binary-caches`, e.g.:
|
||||
|
||||
```console
|
||||
$ nix-env -i firefox --option extra-binary-caches http://avalon:8080/
|
||||
$ nix-env -iA nixpkgs.firefox --option extra-binary-caches http://avalon:8080/
|
||||
```
|
||||
|
||||
The option `extra-binary-caches` tells Nix to use this binary cache in
|
||||
|
||||
@@ -44,7 +44,7 @@ collector as follows:
|
||||
$ nix-store --gc
|
||||
```
|
||||
|
||||
The behaviour of the gargage collector is affected by the
|
||||
The behaviour of the garbage collector is affected by the
|
||||
`keep-derivations` (default: true) and `keep-outputs` (default: false)
|
||||
options in the Nix configuration file. The defaults will ensure that all
|
||||
derivations that are build-time dependencies of garbage collector roots
|
||||
|
||||
@@ -39,7 +39,7 @@ just Subversion 1.1.2 (arrows in the figure indicate symlinks). This
|
||||
would be what we would obtain if we had done
|
||||
|
||||
```console
|
||||
$ nix-env -i subversion
|
||||
$ nix-env -iA nixpkgs.subversion
|
||||
```
|
||||
|
||||
on a set of Nix expressions that contained Subversion 1.1.2.
|
||||
@@ -54,7 +54,7 @@ environment is generated based on the current one. For instance,
|
||||
generation 43 was created from generation 42 when we did
|
||||
|
||||
```console
|
||||
$ nix-env -i subversion firefox
|
||||
$ nix-env -iA nixpkgs.subversion nixpkgs.firefox
|
||||
```
|
||||
|
||||
on a set of Nix expressions that contained Firefox and a new version of
|
||||
@@ -127,7 +127,7 @@ All `nix-env` operations work on the profile pointed to by
|
||||
(abbreviation `-p`):
|
||||
|
||||
```console
|
||||
$ nix-env -p /nix/var/nix/profiles/other-profile -i subversion
|
||||
$ nix-env -p /nix/var/nix/profiles/other-profile -iA nixpkgs.subversion
|
||||
```
|
||||
|
||||
This will *not* change the `~/.nix-profile` symlink.
|
||||
|
||||
@@ -6,7 +6,7 @@ automatically fetching any store paths in Firefox’s closure if they are
|
||||
available on the server `avalon`:
|
||||
|
||||
```console
|
||||
$ nix-env -i firefox --substituters ssh://alice@avalon
|
||||
$ nix-env -iA nixpkgs.firefox --substituters ssh://alice@avalon
|
||||
```
|
||||
|
||||
This works similar to the binary cache substituter that Nix usually
|
||||
|
||||
@@ -19,19 +19,19 @@ to subsequent chapters.
|
||||
channel:
|
||||
|
||||
```console
|
||||
$ nix-env -qa
|
||||
docbook-xml-4.3
|
||||
docbook-xml-4.5
|
||||
firefox-33.0.2
|
||||
hello-2.9
|
||||
libxslt-1.1.28
|
||||
$ nix-env -qaP
|
||||
nixpkgs.docbook_xml_dtd_43 docbook-xml-4.3
|
||||
nixpkgs.docbook_xml_dtd_45 docbook-xml-4.5
|
||||
nixpkgs.firefox firefox-33.0.2
|
||||
nixpkgs.hello hello-2.9
|
||||
nixpkgs.libxslt libxslt-1.1.28
|
||||
…
|
||||
```
|
||||
|
||||
1. Install some packages from the channel:
|
||||
|
||||
```console
|
||||
$ nix-env -i hello
|
||||
$ nix-env -iA nixpkgs.hello
|
||||
```
|
||||
|
||||
This should download pre-built packages; it should not build them
|
||||
|
||||
@@ -276,6 +276,9 @@ more than 2800 commits from 195 contributors since release 2.3.
|
||||
|
||||
* Plugins can now register `nix` subcommands.
|
||||
|
||||
* The `--indirect` flag to `nix-store --add-root` has become a no-op.
|
||||
`--add-root` will always generate indirect GC roots from now on.
|
||||
|
||||
## Incompatible changes
|
||||
|
||||
* The `nix` command is now marked as an experimental feature. This
|
||||
@@ -395,6 +398,7 @@ dramforever,
|
||||
Dustin DeWeese,
|
||||
edef,
|
||||
Eelco Dolstra,
|
||||
Ellie Hermaszewska,
|
||||
Emilio Karakey,
|
||||
Emily,
|
||||
Eric Culp,
|
||||
@@ -405,7 +409,7 @@ Federico Pellegrin,
|
||||
Finn Behrens,
|
||||
Florian Franzen,
|
||||
Félix Baylac-Jacqué,
|
||||
Gabriel Gonzalez,
|
||||
Gabriella Gonzalez,
|
||||
Geoff Reedy,
|
||||
Georges Dubus,
|
||||
Graham Christensen,
|
||||
@@ -428,7 +432,6 @@ Jaroslavas Pocepko,
|
||||
Jarrett Keifer,
|
||||
Jeremy Schlatter,
|
||||
Joachim Breitner,
|
||||
Joe Hermaszewski,
|
||||
Joe Pea,
|
||||
John Ericson,
|
||||
Jonathan Ringer,
|
||||
|
||||
16
doc/manual/src/release-notes/rl-2.5.md
Normal file
16
doc/manual/src/release-notes/rl-2.5.md
Normal file
@@ -0,0 +1,16 @@
|
||||
# Release 2.5 (2021-12-13)
|
||||
|
||||
* The garbage collector no longer blocks new builds, so the message
|
||||
`waiting for the big garbage collector lock...` is a thing of the
|
||||
past.
|
||||
|
||||
* Binary cache stores now have a setting `compression-level`.
|
||||
|
||||
* `nix develop` now has a flag `--unpack` to run `unpackPhase`.
|
||||
|
||||
* Lists can now be compared lexicographically using the `<` operator.
|
||||
|
||||
* New built-in function: `builtins.groupBy`, with the same functionality as
|
||||
Nixpkgs' `lib.groupBy`, but faster.
|
||||
|
||||
* `nix repl` now has a `:log` command.
|
||||
21
doc/manual/src/release-notes/rl-2.6.md
Normal file
21
doc/manual/src/release-notes/rl-2.6.md
Normal file
@@ -0,0 +1,21 @@
|
||||
# Release 2.6 (2022-01-24)
|
||||
|
||||
* The Nix CLI now searches for a `flake.nix` up until the root of the current
|
||||
Git repository or a filesystem boundary rather than just in the current
|
||||
directory.
|
||||
* The TOML parser used by `builtins.fromTOML` has been replaced by [a
|
||||
more compliant one](https://github.com/ToruNiina/toml11).
|
||||
* Added `:st`/`:show-trace` commands to `nix repl`, which are used to
|
||||
set or toggle display of error traces.
|
||||
* New builtin function `builtins.zipAttrsWith` with the same
|
||||
functionality as `lib.zipAttrsWith` from Nixpkgs, but much more
|
||||
efficient.
|
||||
* New command `nix store copy-log` to copy build logs from one store
|
||||
to another.
|
||||
* The `commit-lockfile-summary` option can be set to a non-empty
|
||||
string to override the commit summary used when commiting an updated
|
||||
lockfile. This may be used in conjunction with the `nixConfig`
|
||||
attribute in `flake.nix` to better conform to repository
|
||||
conventions.
|
||||
* `docker run -ti nixos/nix:master` will place you in the Docker
|
||||
container with the latest version of Nix from the `master` branch.
|
||||
@@ -1,5 +1,2 @@
|
||||
# Release 2.5 (2021-XX-XX)
|
||||
# Release X.Y (202?-??-??)
|
||||
|
||||
* Binary cache stores now have a setting `compression-level`.
|
||||
|
||||
* `nix develop` now has a flag `--unpack` to run `unpackPhase`.
|
||||
|
||||
264
docker.nix
Normal file
264
docker.nix
Normal file
@@ -0,0 +1,264 @@
|
||||
{ pkgs ? import <nixpkgs> { }
|
||||
, lib ? pkgs.lib
|
||||
, name ? "nix"
|
||||
, tag ? "latest"
|
||||
, channelName ? "nixpkgs"
|
||||
, channelURL ? "https://nixos.org/channels/nixpkgs-unstable"
|
||||
}:
|
||||
let
|
||||
defaultPkgs = with pkgs; [
|
||||
nix
|
||||
bashInteractive
|
||||
coreutils-full
|
||||
gnutar
|
||||
gzip
|
||||
gnugrep
|
||||
which
|
||||
curl
|
||||
less
|
||||
wget
|
||||
man
|
||||
cacert.out
|
||||
findutils
|
||||
iana-etc
|
||||
git
|
||||
];
|
||||
|
||||
users = {
|
||||
|
||||
root = {
|
||||
uid = 0;
|
||||
shell = "/bin/bash";
|
||||
home = "/root";
|
||||
gid = 0;
|
||||
};
|
||||
|
||||
} // lib.listToAttrs (
|
||||
map
|
||||
(
|
||||
n: {
|
||||
name = "nixbld${toString n}";
|
||||
value = {
|
||||
uid = 30000 + n;
|
||||
gid = 30000;
|
||||
groups = [ "nixbld" ];
|
||||
description = "Nix build user ${toString n}";
|
||||
};
|
||||
}
|
||||
)
|
||||
(lib.lists.range 1 32)
|
||||
);
|
||||
|
||||
groups = {
|
||||
root.gid = 0;
|
||||
nixbld.gid = 30000;
|
||||
};
|
||||
|
||||
userToPasswd = (
|
||||
k:
|
||||
{ uid
|
||||
, gid ? 65534
|
||||
, home ? "/var/empty"
|
||||
, description ? ""
|
||||
, shell ? "/bin/false"
|
||||
, groups ? [ ]
|
||||
}: "${k}:x:${toString uid}:${toString gid}:${description}:${home}:${shell}"
|
||||
);
|
||||
passwdContents = (
|
||||
lib.concatStringsSep "\n"
|
||||
(lib.attrValues (lib.mapAttrs userToPasswd users))
|
||||
);
|
||||
|
||||
userToShadow = k: { ... }: "${k}:!:1::::::";
|
||||
shadowContents = (
|
||||
lib.concatStringsSep "\n"
|
||||
(lib.attrValues (lib.mapAttrs userToShadow users))
|
||||
);
|
||||
|
||||
# Map groups to members
|
||||
# {
|
||||
# group = [ "user1" "user2" ];
|
||||
# }
|
||||
groupMemberMap = (
|
||||
let
|
||||
# Create a flat list of user/group mappings
|
||||
mappings = (
|
||||
builtins.foldl'
|
||||
(
|
||||
acc: user:
|
||||
let
|
||||
groups = users.${user}.groups or [ ];
|
||||
in
|
||||
acc ++ map
|
||||
(group: {
|
||||
inherit user group;
|
||||
})
|
||||
groups
|
||||
)
|
||||
[ ]
|
||||
(lib.attrNames users)
|
||||
);
|
||||
in
|
||||
(
|
||||
builtins.foldl'
|
||||
(
|
||||
acc: v: acc // {
|
||||
${v.group} = acc.${v.group} or [ ] ++ [ v.user ];
|
||||
}
|
||||
)
|
||||
{ }
|
||||
mappings)
|
||||
);
|
||||
|
||||
groupToGroup = k: { gid }:
|
||||
let
|
||||
members = groupMemberMap.${k} or [ ];
|
||||
in
|
||||
"${k}:x:${toString gid}:${lib.concatStringsSep "," members}";
|
||||
groupContents = (
|
||||
lib.concatStringsSep "\n"
|
||||
(lib.attrValues (lib.mapAttrs groupToGroup groups))
|
||||
);
|
||||
|
||||
nixConf = {
|
||||
sandbox = "false";
|
||||
build-users-group = "nixbld";
|
||||
trusted-public-keys = "cache.nixos.org-1:6NCHdD59X431o0gWypbMrAURkbJ16ZPMQFGspcDShjY=";
|
||||
};
|
||||
nixConfContents = (lib.concatStringsSep "\n" (lib.mapAttrsFlatten (n: v: "${n} = ${v}") nixConf)) + "\n";
|
||||
|
||||
baseSystem =
|
||||
let
|
||||
nixpkgs = pkgs.path;
|
||||
channel = pkgs.runCommand "channel-nixos" { } ''
|
||||
mkdir $out
|
||||
ln -s ${nixpkgs} $out/nixpkgs
|
||||
echo "[]" > $out/manifest.nix
|
||||
'';
|
||||
rootEnv = pkgs.buildPackages.buildEnv {
|
||||
name = "root-profile-env";
|
||||
paths = defaultPkgs;
|
||||
};
|
||||
manifest = pkgs.buildPackages.runCommand "manifest.nix" { } ''
|
||||
cat > $out <<EOF
|
||||
[
|
||||
${lib.concatStringsSep "\n" (builtins.map (drv: let
|
||||
outputs = drv.outputsToInstall or [ "out" ];
|
||||
in ''
|
||||
{
|
||||
${lib.concatStringsSep "\n" (builtins.map (output: ''
|
||||
${output} = { outPath = "${lib.getOutput output drv}"; };
|
||||
'') outputs)}
|
||||
outputs = [ ${lib.concatStringsSep " " (builtins.map (x: "\"${x}\"") outputs)} ];
|
||||
name = "${drv.name}";
|
||||
outPath = "${drv}";
|
||||
system = "${drv.system}";
|
||||
type = "derivation";
|
||||
meta = { };
|
||||
}
|
||||
'') defaultPkgs)}
|
||||
]
|
||||
EOF
|
||||
'';
|
||||
profile = pkgs.buildPackages.runCommand "user-environment" { } ''
|
||||
mkdir $out
|
||||
cp -a ${rootEnv}/* $out/
|
||||
ln -s ${manifest} $out/manifest.nix
|
||||
'';
|
||||
in
|
||||
pkgs.runCommand "base-system"
|
||||
{
|
||||
inherit passwdContents groupContents shadowContents nixConfContents;
|
||||
passAsFile = [
|
||||
"passwdContents"
|
||||
"groupContents"
|
||||
"shadowContents"
|
||||
"nixConfContents"
|
||||
];
|
||||
allowSubstitutes = false;
|
||||
preferLocalBuild = true;
|
||||
} ''
|
||||
env
|
||||
set -x
|
||||
mkdir -p $out/etc
|
||||
|
||||
mkdir -p $out/etc/ssl/certs
|
||||
ln -s /nix/var/nix/profiles/default/etc/ssl/certs/ca-bundle.crt $out/etc/ssl/certs
|
||||
|
||||
cat $passwdContentsPath > $out/etc/passwd
|
||||
echo "" >> $out/etc/passwd
|
||||
|
||||
cat $groupContentsPath > $out/etc/group
|
||||
echo "" >> $out/etc/group
|
||||
|
||||
cat $shadowContentsPath > $out/etc/shadow
|
||||
echo "" >> $out/etc/shadow
|
||||
|
||||
mkdir -p $out/usr
|
||||
ln -s /nix/var/nix/profiles/share $out/usr/
|
||||
|
||||
mkdir -p $out/nix/var/nix/gcroots
|
||||
|
||||
mkdir $out/tmp
|
||||
|
||||
mkdir -p $out/var/tmp
|
||||
|
||||
mkdir -p $out/etc/nix
|
||||
cat $nixConfContentsPath > $out/etc/nix/nix.conf
|
||||
|
||||
mkdir -p $out/root
|
||||
mkdir -p $out/nix/var/nix/profiles/per-user/root
|
||||
|
||||
ln -s ${profile} $out/nix/var/nix/profiles/default-1-link
|
||||
ln -s $out/nix/var/nix/profiles/default-1-link $out/nix/var/nix/profiles/default
|
||||
ln -s /nix/var/nix/profiles/default $out/root/.nix-profile
|
||||
|
||||
ln -s ${channel} $out/nix/var/nix/profiles/per-user/root/channels-1-link
|
||||
ln -s $out/nix/var/nix/profiles/per-user/root/channels-1-link $out/nix/var/nix/profiles/per-user/root/channels
|
||||
|
||||
mkdir -p $out/root/.nix-defexpr
|
||||
ln -s $out/nix/var/nix/profiles/per-user/root/channels $out/root/.nix-defexpr/channels
|
||||
echo "${channelURL} ${channelName}" > $out/root/.nix-channels
|
||||
|
||||
mkdir -p $out/bin $out/usr/bin
|
||||
ln -s ${pkgs.coreutils}/bin/env $out/usr/bin/env
|
||||
ln -s ${pkgs.bashInteractive}/bin/bash $out/bin/sh
|
||||
'';
|
||||
|
||||
in
|
||||
pkgs.dockerTools.buildLayeredImageWithNixDb {
|
||||
|
||||
inherit name tag;
|
||||
|
||||
contents = [ baseSystem ];
|
||||
|
||||
extraCommands = ''
|
||||
rm -rf nix-support
|
||||
ln -s /nix/var/nix/profiles nix/var/nix/gcroots/profiles
|
||||
'';
|
||||
fakeRootCommands = ''
|
||||
chmod 1777 tmp
|
||||
chmod 1777 var/tmp
|
||||
'';
|
||||
|
||||
config = {
|
||||
Cmd = [ "/root/.nix-profile/bin/bash" ];
|
||||
Env = [
|
||||
"USER=root"
|
||||
"PATH=${lib.concatStringsSep ":" [
|
||||
"/root/.nix-profile/bin"
|
||||
"/nix/var/nix/profiles/default/bin"
|
||||
"/nix/var/nix/profiles/default/sbin"
|
||||
]}"
|
||||
"MANPATH=${lib.concatStringsSep ":" [
|
||||
"/root/.nix-profile/share/man"
|
||||
"/nix/var/nix/profiles/default/share/man"
|
||||
]}"
|
||||
"SSL_CERT_FILE=/nix/var/nix/profiles/default/etc/ssl/certs/ca-bundle.crt"
|
||||
"GIT_SSL_CAINFO=/nix/var/nix/profiles/default/etc/ssl/certs/ca-bundle.crt"
|
||||
"NIX_SSL_CERT_FILE=/nix/var/nix/profiles/default/etc/ssl/certs/ca-bundle.crt"
|
||||
"NIX_PATH=/nix/var/nix/profiles/per-user/root/channels:/root/.nix-defexpr/channels"
|
||||
];
|
||||
};
|
||||
|
||||
}
|
||||
18
flake.lock
generated
18
flake.lock
generated
@@ -31,10 +31,26 @@
|
||||
"type": "indirect"
|
||||
}
|
||||
},
|
||||
"nixpkgs-regression": {
|
||||
"locked": {
|
||||
"lastModified": 1643052045,
|
||||
"narHash": "sha256-uGJ0VXIhWKGXxkeNnq4TvV3CIOkUJ3PAoLZ3HMzNVMw=",
|
||||
"owner": "NixOS",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "215d4d0fd80ca5163643b03a33fde804a29cc1e2",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"id": "nixpkgs",
|
||||
"rev": "215d4d0fd80ca5163643b03a33fde804a29cc1e2",
|
||||
"type": "indirect"
|
||||
}
|
||||
},
|
||||
"root": {
|
||||
"inputs": {
|
||||
"lowdown-src": "lowdown-src",
|
||||
"nixpkgs": "nixpkgs"
|
||||
"nixpkgs": "nixpkgs",
|
||||
"nixpkgs-regression": "nixpkgs-regression"
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
156
flake.nix
156
flake.nix
@@ -2,9 +2,10 @@
|
||||
description = "The purely functional package manager";
|
||||
|
||||
inputs.nixpkgs.url = "nixpkgs/nixos-21.05-small";
|
||||
inputs.nixpkgs-regression.url = "nixpkgs/215d4d0fd80ca5163643b03a33fde804a29cc1e2";
|
||||
inputs.lowdown-src = { url = "github:kristapsdz/lowdown"; flake = false; };
|
||||
|
||||
outputs = { self, nixpkgs, lowdown-src }:
|
||||
outputs = { self, nixpkgs, nixpkgs-regression, lowdown-src }:
|
||||
|
||||
let
|
||||
|
||||
@@ -14,7 +15,7 @@
|
||||
then ""
|
||||
else "pre${builtins.substring 0 8 (self.lastModifiedDate or self.lastModified or "19700101")}_${self.shortRev or "dirty"}";
|
||||
|
||||
officialRelease = false;
|
||||
officialRelease = true;
|
||||
|
||||
linux64BitSystems = [ "x86_64-linux" "aarch64-linux" ];
|
||||
linuxSystems = linux64BitSystems ++ [ "i686-linux" ];
|
||||
@@ -22,15 +23,36 @@
|
||||
|
||||
crossSystems = [ "armv6l-linux" "armv7l-linux" ];
|
||||
|
||||
stdenvs = [ "gccStdenv" "clangStdenv" "clang11Stdenv" "stdenv" ];
|
||||
|
||||
forAllSystems = f: nixpkgs.lib.genAttrs systems (system: f system);
|
||||
forAllSystemsAndStdenvs = f: forAllSystems (system:
|
||||
nixpkgs.lib.listToAttrs
|
||||
(map
|
||||
(n:
|
||||
nixpkgs.lib.nameValuePair "${n}Packages" (
|
||||
f system n
|
||||
)) stdenvs
|
||||
)
|
||||
);
|
||||
|
||||
forAllStdenvs = stdenvs: f: nixpkgs.lib.genAttrs stdenvs (stdenv: f stdenv);
|
||||
|
||||
# Memoize nixpkgs for different platforms for efficiency.
|
||||
nixpkgsFor = forAllSystems (system:
|
||||
import nixpkgs {
|
||||
inherit system;
|
||||
overlays = [ self.overlay ];
|
||||
}
|
||||
);
|
||||
nixpkgsFor =
|
||||
let stdenvsPackages = forAllSystemsAndStdenvs
|
||||
(system: stdenv:
|
||||
import nixpkgs {
|
||||
inherit system;
|
||||
overlays = [
|
||||
(overlayFor (p: p.${stdenv}))
|
||||
];
|
||||
}
|
||||
);
|
||||
in
|
||||
# Add the `stdenvPackages` at toplevel, both because these are the ones
|
||||
# we want most of the time and for backwards compatibility
|
||||
forAllSystems (system: stdenvsPackages.${system} // stdenvsPackages.${system}.stdenvPackages);
|
||||
|
||||
commonDeps = pkgs: with pkgs; rec {
|
||||
# Use "busybox-sandbox-shell" if present,
|
||||
@@ -75,7 +97,7 @@
|
||||
buildPackages.mdbook
|
||||
buildPackages.autoconf-archive
|
||||
buildPackages.autoreconfHook
|
||||
buildPackages.pkgconfig
|
||||
buildPackages.pkg-config
|
||||
|
||||
# Tests
|
||||
buildPackages.git
|
||||
@@ -91,7 +113,7 @@
|
||||
libarchive
|
||||
boost
|
||||
lowdown-nix
|
||||
gmock
|
||||
gtest
|
||||
]
|
||||
++ lib.optionals stdenv.isLinux [libseccomp]
|
||||
++ lib.optional (stdenv.isLinux || stdenv.isDarwin) libsodium
|
||||
@@ -255,18 +277,15 @@
|
||||
$(cat ${installerClosureInfo}/store-paths)
|
||||
'';
|
||||
|
||||
in {
|
||||
|
||||
# A Nixpkgs overlay that overrides the 'nix' and
|
||||
# 'nix.perl-bindings' packages.
|
||||
overlay = final: prev: {
|
||||
|
||||
overlayFor = getStdenv: final: prev:
|
||||
let currentStdenv = getStdenv final; in
|
||||
{
|
||||
nixStable = prev.nix;
|
||||
|
||||
# Forward from the previous stage as we don’t want it to pick the lowdown override
|
||||
nixUnstable = prev.nixUnstable;
|
||||
|
||||
nix = with final; with commonDeps pkgs; stdenv.mkDerivation {
|
||||
nix = with final; with commonDeps pkgs; currentStdenv.mkDerivation {
|
||||
name = "nix-${version}";
|
||||
inherit version;
|
||||
|
||||
@@ -281,6 +300,8 @@
|
||||
|
||||
propagatedBuildInputs = propagatedDeps;
|
||||
|
||||
disallowedReferences = [ boost ];
|
||||
|
||||
preConfigure =
|
||||
''
|
||||
# Copy libboost_context so we don't get all of Boost in our closure.
|
||||
@@ -288,9 +309,16 @@
|
||||
mkdir -p $out/lib
|
||||
cp -pd ${boost}/lib/{libboost_context*,libboost_thread*,libboost_system*} $out/lib
|
||||
rm -f $out/lib/*.a
|
||||
${lib.optionalString stdenv.isLinux ''
|
||||
${lib.optionalString currentStdenv.isLinux ''
|
||||
chmod u+w $out/lib/*.so.*
|
||||
patchelf --set-rpath $out/lib:${stdenv.cc.cc.lib}/lib $out/lib/libboost_thread.so.*
|
||||
patchelf --set-rpath $out/lib:${currentStdenv.cc.cc.lib}/lib $out/lib/libboost_thread.so.*
|
||||
''}
|
||||
${lib.optionalString currentStdenv.isDarwin ''
|
||||
for LIB in $out/lib/*.dylib; do
|
||||
chmod u+w $LIB
|
||||
install_name_tool -id $LIB $LIB
|
||||
done
|
||||
install_name_tool -change ${boost}/lib/libboost_system.dylib $out/lib/libboost_system.dylib $out/lib/libboost_thread.dylib
|
||||
''}
|
||||
'';
|
||||
|
||||
@@ -308,6 +336,12 @@
|
||||
postInstall = ''
|
||||
mkdir -p $doc/nix-support
|
||||
echo "doc manual $doc/share/doc/nix/manual" >> $doc/nix-support/hydra-build-products
|
||||
${lib.optionalString currentStdenv.isDarwin ''
|
||||
install_name_tool \
|
||||
-change ${boost}/lib/libboost_context.dylib \
|
||||
$out/lib/libboost_context.dylib \
|
||||
$out/lib/libnixutil.dylib
|
||||
''}
|
||||
'';
|
||||
|
||||
doInstallCheck = true;
|
||||
@@ -317,7 +351,7 @@
|
||||
|
||||
strictDeps = true;
|
||||
|
||||
passthru.perl-bindings = with final; stdenv.mkDerivation {
|
||||
passthru.perl-bindings = with final; currentStdenv.mkDerivation {
|
||||
name = "nix-perl-${version}";
|
||||
|
||||
src = self;
|
||||
@@ -325,7 +359,7 @@
|
||||
nativeBuildInputs =
|
||||
[ buildPackages.autoconf-archive
|
||||
buildPackages.autoreconfHook
|
||||
buildPackages.pkgconfig
|
||||
buildPackages.pkg-config
|
||||
];
|
||||
|
||||
buildInputs =
|
||||
@@ -336,8 +370,8 @@
|
||||
pkgs.perl
|
||||
boost
|
||||
]
|
||||
++ lib.optional (stdenv.isLinux || stdenv.isDarwin) libsodium
|
||||
++ lib.optional stdenv.isDarwin darwin.apple_sdk.frameworks.Security;
|
||||
++ lib.optional (currentStdenv.isLinux || currentStdenv.isDarwin) libsodium
|
||||
++ lib.optional currentStdenv.isDarwin darwin.apple_sdk.frameworks.Security;
|
||||
|
||||
configureFlags = ''
|
||||
--with-dbi=${perlPackages.DBI}/${pkgs.perl.libPrefix}
|
||||
@@ -351,7 +385,7 @@
|
||||
|
||||
};
|
||||
|
||||
lowdown-nix = with final; stdenv.mkDerivation rec {
|
||||
lowdown-nix = with final; currentStdenv.mkDerivation rec {
|
||||
name = "lowdown-0.9.0";
|
||||
|
||||
src = lowdown-src;
|
||||
@@ -361,15 +395,20 @@
|
||||
nativeBuildInputs = [ buildPackages.which ];
|
||||
|
||||
configurePhase = ''
|
||||
${if (stdenv.isDarwin && stdenv.isAarch64) then "echo \"HAVE_SANDBOX_INIT=false\" > configure.local" else ""}
|
||||
${if (currentStdenv.isDarwin && currentStdenv.isAarch64) then "echo \"HAVE_SANDBOX_INIT=false\" > configure.local" else ""}
|
||||
./configure \
|
||||
PREFIX=${placeholder "dev"} \
|
||||
BINDIR=${placeholder "bin"}/bin
|
||||
'';
|
||||
'';
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
in {
|
||||
|
||||
# A Nixpkgs overlay that overrides the 'nix' and
|
||||
# 'nix.perl-bindings' packages.
|
||||
overlay = overlayFor (p: p.stdenv);
|
||||
|
||||
hydraJobs = {
|
||||
|
||||
# Binary package for various platforms.
|
||||
@@ -405,6 +444,21 @@
|
||||
installerScript = installScriptFor [ "x86_64-linux" "i686-linux" "aarch64-linux" "x86_64-darwin" "aarch64-darwin" "armv6l-linux" "armv7l-linux" ];
|
||||
installerScriptForGHA = installScriptFor [ "x86_64-linux" "x86_64-darwin" "armv6l-linux" "armv7l-linux"];
|
||||
|
||||
# docker image with Nix inside
|
||||
dockerImage = nixpkgs.lib.genAttrs linux64BitSystems (system:
|
||||
let
|
||||
pkgs = nixpkgsFor.${system};
|
||||
image = import ./docker.nix { inherit pkgs; tag = version; };
|
||||
in pkgs.runCommand "docker-image-tarball-${version}"
|
||||
{ meta.description = "Docker image with Nix for ${system}";
|
||||
}
|
||||
''
|
||||
mkdir -p $out/nix-support
|
||||
image=$out/image.tar.gz
|
||||
ln -s ${image} $image
|
||||
echo "file binary-dist $image" >> $out/nix-support/hydra-build-products
|
||||
'');
|
||||
|
||||
# Line coverage analysis.
|
||||
coverage =
|
||||
with nixpkgsFor.x86_64-linux;
|
||||
@@ -465,29 +519,23 @@
|
||||
inherit (self) overlay;
|
||||
});
|
||||
|
||||
/*
|
||||
# Check whether we can still evaluate all of Nixpkgs.
|
||||
# Make sure that nix-env still produces the exact same result
|
||||
# on a particular version of Nixpkgs.
|
||||
tests.evalNixpkgs =
|
||||
import (nixpkgs + "/pkgs/top-level/make-tarball.nix") {
|
||||
# FIXME: fix pkgs/top-level/make-tarball.nix in NixOS to not require a revCount.
|
||||
inherit nixpkgs;
|
||||
pkgs = nixpkgsFor.x86_64-linux;
|
||||
officialRelease = false;
|
||||
};
|
||||
|
||||
# Check whether we can still evaluate NixOS.
|
||||
tests.evalNixOS =
|
||||
with nixpkgsFor.x86_64-linux;
|
||||
runCommand "eval-nixos" { buildInputs = [ nix ]; }
|
||||
''
|
||||
export NIX_STATE_DIR=$TMPDIR
|
||||
|
||||
nix-instantiate ${nixpkgs}/nixos/release-combined.nix -A tested --dry-run \
|
||||
--arg nixpkgs '{ outPath = ${nixpkgs}; revCount = 123; shortRev = "abcdefgh"; }'
|
||||
|
||||
touch $out
|
||||
type -p nix-env
|
||||
# Note: we're filtering out nixos-install-tools because https://github.com/NixOS/nixpkgs/pull/153594#issuecomment-1020530593.
|
||||
time nix-env --store dummy:// -f ${nixpkgs-regression} -qaP --drv-path | sort | grep -v nixos-install-tools > packages
|
||||
[[ $(sha1sum < packages | cut -c1-40) = ff451c521e61e4fe72bdbe2d0ca5d1809affa733 ]]
|
||||
mkdir $out
|
||||
'';
|
||||
*/
|
||||
|
||||
metrics.nixpkgs = import "${nixpkgs-regression}/pkgs/top-level/metrics.nix" {
|
||||
pkgs = nixpkgsFor.x86_64-linux;
|
||||
nixpkgs = nixpkgs-regression;
|
||||
};
|
||||
|
||||
installTests = forAllSystems (system:
|
||||
let pkgs = nixpkgsFor.${system}; in
|
||||
@@ -509,6 +557,8 @@
|
||||
binaryTarball = self.hydraJobs.binaryTarball.${system};
|
||||
perlBindings = self.hydraJobs.perlBindings.${system};
|
||||
installTests = self.hydraJobs.installTests.${system};
|
||||
} // (nixpkgs.lib.optionalAttrs (builtins.elem system linux64BitSystems)) {
|
||||
dockerImage = self.hydraJobs.dockerImage.${system};
|
||||
});
|
||||
|
||||
packages = forAllSystems (system: {
|
||||
@@ -593,15 +643,22 @@
|
||||
doInstallCheck = true;
|
||||
installCheckFlags = "sysconfdir=$(out)/etc";
|
||||
};
|
||||
}) crossSystems)));
|
||||
}) crossSystems)) // (builtins.listToAttrs (map (stdenvName:
|
||||
nixpkgsFor.${system}.lib.nameValuePair
|
||||
"nix-${stdenvName}"
|
||||
nixpkgsFor.${system}."${stdenvName}Packages".nix
|
||||
) stdenvs))
|
||||
);
|
||||
|
||||
defaultPackage = forAllSystems (system: self.packages.${system}.nix);
|
||||
|
||||
devShell = forAllSystems (system:
|
||||
devShell = forAllSystems (system: self.devShells.${system}.stdenvPackages);
|
||||
|
||||
devShells = forAllSystemsAndStdenvs (system: stdenv:
|
||||
with nixpkgsFor.${system};
|
||||
with commonDeps pkgs;
|
||||
|
||||
stdenv.mkDerivation {
|
||||
nixpkgsFor.${system}.${stdenv}.mkDerivation {
|
||||
name = "nix";
|
||||
|
||||
outputs = [ "out" "dev" "doc" ];
|
||||
@@ -620,6 +677,9 @@
|
||||
PATH=$prefix/bin:$PATH
|
||||
unset PYTHONPATH
|
||||
export MANPATH=$out/share/man:$MANPATH
|
||||
|
||||
# Make bash completion work.
|
||||
XDG_DATA_DIRS+=:$out/share
|
||||
'';
|
||||
});
|
||||
|
||||
|
||||
@@ -7,13 +7,15 @@ function _complete_nix {
|
||||
local completion=${line%% *}
|
||||
if [[ -z $have_type ]]; then
|
||||
have_type=1
|
||||
if [[ $completion = filenames ]]; then
|
||||
if [[ $completion == filenames ]]; then
|
||||
compopt -o filenames
|
||||
elif [[ $completion == attrs ]]; then
|
||||
compopt -o nospace
|
||||
fi
|
||||
else
|
||||
COMPREPLY+=("$completion")
|
||||
fi
|
||||
done < <(NIX_GET_COMPLETIONS=$cword "${words[@]}")
|
||||
done < <(NIX_GET_COMPLETIONS=$cword "${words[@]/#\~/$HOME}")
|
||||
__ltrim_colon_completions "$cur"
|
||||
}
|
||||
|
||||
|
||||
@@ -19,7 +19,6 @@ end
|
||||
|
||||
function _nix_accepts_files
|
||||
set -l response (_nix_complete)
|
||||
# First line is either filenames or no-filenames.
|
||||
test $response[1] = 'filenames'
|
||||
end
|
||||
|
||||
|
||||
@@ -25,5 +25,10 @@
|
||||
<string>/var/log/nix-daemon.log</string>
|
||||
<key>StandardOutPath</key>
|
||||
<string>/dev/null</string>
|
||||
<key>SoftResourceLimits</key>
|
||||
<dict>
|
||||
<key>NumberOfFiles</key>
|
||||
<integer>4096</integer>
|
||||
</dict>
|
||||
</dict>
|
||||
</plist>
|
||||
|
||||
@@ -41,7 +41,7 @@ perlarchname=$($perl -e 'use Config; print $Config{archname};')
|
||||
AC_SUBST(perllibdir, [${libdir}/perl5/site_perl/$perlversion/$perlarchname])
|
||||
AC_MSG_RESULT($perllibdir)
|
||||
|
||||
# Look for libsodium, an optional dependency.
|
||||
# Look for libsodium.
|
||||
PKG_CHECK_MODULES([SODIUM], [libsodium], [CXXFLAGS="$SODIUM_CFLAGS $CXXFLAGS"])
|
||||
|
||||
# Check for the required Perl dependencies (DBI and DBD::SQLite).
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package Nix::Config;
|
||||
|
||||
use MIME::Base64;
|
||||
use Nix::Store;
|
||||
|
||||
$version = "@PACKAGE_VERSION@";
|
||||
|
||||
|
||||
28
scripts/check-hydra-status.sh
Normal file
28
scripts/check-hydra-status.sh
Normal file
@@ -0,0 +1,28 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
set -euo pipefail
|
||||
# set -x
|
||||
|
||||
|
||||
# mapfile BUILDS_FOR_LATEST_EVAL < <(
|
||||
# curl -H 'Accept: application/json' https://hydra.nixos.org/jobset/nix/master/evals | \
|
||||
# jq -r '.evals[0].builds[] | @sh')
|
||||
BUILDS_FOR_LATEST_EVAL=$(
|
||||
curl -sS -H 'Accept: application/json' https://hydra.nixos.org/jobset/nix/master/evals | \
|
||||
jq -r '.evals[0].builds[]')
|
||||
|
||||
someBuildFailed=0
|
||||
|
||||
for buildId in $BUILDS_FOR_LATEST_EVAL; do
|
||||
buildInfo=$(curl -sS -H 'Accept: application/json' "https://hydra.nixos.org/build/$buildId")
|
||||
|
||||
buildStatus=$(echo "$buildInfo" | \
|
||||
jq -r '.buildstatus')
|
||||
|
||||
if [[ "$buildStatus" -ne 0 ]]; then
|
||||
someBuildFailed=1
|
||||
echo "Job “$(echo "$buildInfo" | jq -r '.job')” failed on hydra"
|
||||
fi
|
||||
done
|
||||
|
||||
exit "$someBuildFailed"
|
||||
@@ -440,7 +440,22 @@ add_nix_vol_fstab_line() {
|
||||
# shellcheck disable=SC1003,SC2026
|
||||
local escaped_mountpoint="${NIX_ROOT/ /'\\\'040}"
|
||||
shift
|
||||
EDITOR="/usr/bin/ex" _sudo "to add nix to fstab" "$@" <<EOF
|
||||
|
||||
# wrap `ex` to work around a problem with vim plugins breaking exit codes;
|
||||
# (see https://github.com/NixOS/nix/issues/5468)
|
||||
# we'd prefer EDITOR="/usr/bin/ex --noplugin" but vifs doesn't word-split
|
||||
# the EDITOR env.
|
||||
#
|
||||
# TODO: at some point we should switch to `--clean`, but it wasn't added
|
||||
# until https://github.com/vim/vim/releases/tag/v8.0.1554 while the macOS
|
||||
# minver 10.12.6 seems to have released with vim 7.4
|
||||
cat > "$SCRATCH/ex_cleanroom_wrapper" <<EOF
|
||||
#!/bin/sh
|
||||
/usr/bin/ex --noplugin "\$@"
|
||||
EOF
|
||||
chmod 755 "$SCRATCH/ex_cleanroom_wrapper"
|
||||
|
||||
EDITOR="$SCRATCH/ex_cleanroom_wrapper" _sudo "to add nix to fstab" "$@" <<EOF
|
||||
:a
|
||||
UUID=$uuid $escaped_mountpoint apfs rw,noauto,nobrowse,suid,owners
|
||||
.
|
||||
@@ -631,7 +646,7 @@ EOF
|
||||
# technically /etc/synthetic.d/nix is supported in Big Sur+
|
||||
# but handling both takes even more code...
|
||||
_sudo "to add Nix to /etc/synthetic.conf" \
|
||||
/usr/bin/ex /etc/synthetic.conf <<EOF
|
||||
/usr/bin/ex --noplugin /etc/synthetic.conf <<EOF
|
||||
:a
|
||||
${NIX_ROOT:1}
|
||||
.
|
||||
@@ -742,6 +757,9 @@ setup_volume() {
|
||||
|
||||
use_special="${NIX_VOLUME_USE_SPECIAL:-$(create_volume)}"
|
||||
|
||||
_sudo "to ensure the Nix volume is not mounted" \
|
||||
/usr/sbin/diskutil unmount force "$use_special" || true # might not be mounted
|
||||
|
||||
use_uuid=${NIX_VOLUME_USE_UUID:-$(volume_uuid_from_special "$use_special")}
|
||||
|
||||
setup_fstab "$use_uuid"
|
||||
@@ -791,7 +809,7 @@ setup_volume_daemon() {
|
||||
local volume_uuid="$2"
|
||||
if ! test_voldaemon; then
|
||||
task "Configuring LaunchDaemon to mount '$NIX_VOLUME_LABEL'" >&2
|
||||
_sudo "to install the Nix volume mounter" /usr/bin/ex "$NIX_VOLUME_MOUNTD_DEST" <<EOF
|
||||
_sudo "to install the Nix volume mounter" /usr/bin/ex --noplugin "$NIX_VOLUME_MOUNTD_DEST" <<EOF
|
||||
:a
|
||||
$(generate_mount_daemon "$cmd_type" "$volume_uuid")
|
||||
.
|
||||
|
||||
@@ -218,7 +218,7 @@ EOF
|
||||
setup_darwin_volume
|
||||
fi
|
||||
|
||||
if [ "$(diskutil info -plist /nix | xmllint --xpath "(/plist/dict/key[text()='GlobalPermissionsEnabled'])/following-sibling::*[1]" -)" = "<false/>" ]; then
|
||||
failure "This script needs a /nix volume with global permissions! This may require running sudo diskutil enableOwnership /nix."
|
||||
if [ "$(/usr/sbin/diskutil info -plist /nix | xmllint --xpath "(/plist/dict/key[text()='GlobalPermissionsEnabled'])/following-sibling::*[1]" -)" = "<false/>" ]; then
|
||||
failure "This script needs a /nix volume with global permissions! This may require running sudo /usr/sbin/diskutil enableOwnership /nix."
|
||||
fi
|
||||
}
|
||||
|
||||
@@ -377,6 +377,11 @@ cure_artifacts() {
|
||||
}
|
||||
|
||||
validate_starting_assumptions() {
|
||||
task "Checking for artifacts of previous installs"
|
||||
cat <<EOF
|
||||
Before I try to install, I'll check for signs Nix already is or has
|
||||
been installed on this system.
|
||||
EOF
|
||||
if type nix-env 2> /dev/null >&2; then
|
||||
warning <<EOF
|
||||
Nix already appears to be installed. This installer may run into issues.
|
||||
@@ -386,20 +391,34 @@ $(uninstall_directions)
|
||||
EOF
|
||||
fi
|
||||
|
||||
# TODO: I think it would be good for this step to accumulate more
|
||||
# knowledge of older obsolete artifacts, if there are any.
|
||||
# We could issue a "reminder" here that the user might want
|
||||
# to clean them up?
|
||||
|
||||
for profile_target in "${PROFILE_TARGETS[@]}"; do
|
||||
# TODO: I think it would be good to accumulate a list of all
|
||||
# of the copies so that people don't hit this 2 or 3x in
|
||||
# a row for different files.
|
||||
if [ -e "$profile_target$PROFILE_BACKUP_SUFFIX" ]; then
|
||||
# this backup process first released in Nix 2.1
|
||||
failure <<EOF
|
||||
When this script runs, it backs up the current $profile_target to
|
||||
$profile_target$PROFILE_BACKUP_SUFFIX. This backup file already exists, though.
|
||||
I back up shell profile/rc scripts before I add Nix to them.
|
||||
I need to back up $profile_target to $profile_target$PROFILE_BACKUP_SUFFIX,
|
||||
but the latter already exists.
|
||||
|
||||
Please follow these instructions to clean up the old backup file:
|
||||
Here's how to clean up the old backup file:
|
||||
|
||||
1. Copy $profile_target and $profile_target$PROFILE_BACKUP_SUFFIX to another place, just
|
||||
in case.
|
||||
1. Back up (copy) $profile_target and $profile_target$PROFILE_BACKUP_SUFFIX
|
||||
to another location, just in case.
|
||||
|
||||
2. Take care to make sure that $profile_target$PROFILE_BACKUP_SUFFIX doesn't look like
|
||||
it has anything nix-related in it. If it does, something is probably
|
||||
quite wrong. Please open an issue or get in touch immediately.
|
||||
2. Ensure $profile_target$PROFILE_BACKUP_SUFFIX does not have anything
|
||||
Nix-related in it. If it does, something is probably quite
|
||||
wrong. Please open an issue or get in touch immediately.
|
||||
|
||||
3. Once you confirm $profile_target is backed up and
|
||||
$profile_target$PROFILE_BACKUP_SUFFIX doesn't mention Nix, run:
|
||||
mv $profile_target$PROFILE_BACKUP_SUFFIX $profile_target
|
||||
EOF
|
||||
fi
|
||||
done
|
||||
@@ -571,7 +590,7 @@ create_directories() {
|
||||
"$get_chr_own" -R "root:$NIX_BUILD_GROUP_NAME" "$NIX_ROOT" || true
|
||||
fi
|
||||
_sudo "to make the basic directory structure of Nix (part 1)" \
|
||||
install -dv -m 0755 /nix /nix/var /nix/var/log /nix/var/log/nix /nix/var/log/nix/drvs /nix/var/nix{,/db,/gcroots,/profiles,/temproots,/userpool} /nix/var/nix/{gcroots,profiles}/per-user
|
||||
install -dv -m 0755 /nix /nix/var /nix/var/log /nix/var/log/nix /nix/var/log/nix/drvs /nix/var/nix{,/db,/gcroots,/profiles,/temproots,/userpool,/daemon-socket} /nix/var/nix/{gcroots,profiles}/per-user
|
||||
|
||||
_sudo "to make the basic directory structure of Nix (part 2)" \
|
||||
install -dv -g "$NIX_BUILD_GROUP_NAME" -m 1775 /nix/store
|
||||
@@ -809,7 +828,7 @@ main() {
|
||||
# can fail faster in this case. Sourcing install-darwin... now runs
|
||||
# `touch /` to detect Read-only root, but it could update times on
|
||||
# pre-Catalina macOS if run as root user.
|
||||
if [ $EUID -eq 0 ]; then
|
||||
if [ "$EUID" -eq 0 ]; then
|
||||
failure <<EOF
|
||||
Please do not run this script with root privileges. I will call sudo
|
||||
when I need to.
|
||||
|
||||
@@ -38,7 +38,7 @@ fi
|
||||
|
||||
# Determine if we could use the multi-user installer or not
|
||||
if [ "$(uname -s)" = "Linux" ]; then
|
||||
echo "Note: a multi-user installation is possible. See https://nixos.org/nix/manual/#sect-multi-user-installation" >&2
|
||||
echo "Note: a multi-user installation is possible. See https://nixos.org/manual/nix/stable/installation/installing-binary.html#multi-user-installation" >&2
|
||||
fi
|
||||
|
||||
case "$(uname -s)" in
|
||||
@@ -98,7 +98,7 @@ while [ $# -gt 0 ]; do
|
||||
echo " providing multi-user support and better isolation for local builds."
|
||||
echo " Both for security and reproducibility, this method is recommended if"
|
||||
echo " supported on your platform."
|
||||
echo " See https://nixos.org/nix/manual/#sect-multi-user-installation"
|
||||
echo " See https://nixos.org/manual/nix/stable/installation/installing-binary.html#multi-user-installation"
|
||||
echo ""
|
||||
echo " --no-daemon: Simple, single-user installation that does not require root and is"
|
||||
echo " trivial to uninstall."
|
||||
@@ -134,7 +134,7 @@ fi
|
||||
|
||||
echo "performing a single-user installation of Nix..." >&2
|
||||
|
||||
if ! [ -e $dest ]; then
|
||||
if ! [ -e "$dest" ]; then
|
||||
cmd="mkdir -m 0755 $dest && chown $USER $dest"
|
||||
echo "directory $dest does not exist; creating it by running '$cmd' using sudo" >&2
|
||||
if ! sudo sh -c "$cmd"; then
|
||||
@@ -143,12 +143,12 @@ if ! [ -e $dest ]; then
|
||||
fi
|
||||
fi
|
||||
|
||||
if ! [ -w $dest ]; then
|
||||
echo "$0: directory $dest exists, but is not writable by you. This could indicate that another user has already performed a single-user installation of Nix on this system. If you wish to enable multi-user support see https://nixos.org/nix/manual/#ssec-multi-user. If you wish to continue with a single-user install for $USER please run 'chown -R $USER $dest' as root." >&2
|
||||
if ! [ -w "$dest" ]; then
|
||||
echo "$0: directory $dest exists, but is not writable by you. This could indicate that another user has already performed a single-user installation of Nix on this system. If you wish to enable multi-user support see https://nixos.org/manual/nix/stable/installation/multi-user.html. If you wish to continue with a single-user install for $USER please run 'chown -R $USER $dest' as root." >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
mkdir -p $dest/store
|
||||
mkdir -p "$dest/store"
|
||||
|
||||
printf "copying Nix to %s..." "${dest}/store" >&2
|
||||
# Insert a newline if no progress is shown.
|
||||
@@ -189,17 +189,17 @@ fi
|
||||
|
||||
# Install an SSL certificate bundle.
|
||||
if [ -z "$NIX_SSL_CERT_FILE" ] || ! [ -f "$NIX_SSL_CERT_FILE" ]; then
|
||||
$nix/bin/nix-env -i "$cacert"
|
||||
"$nix/bin/nix-env" -i "$cacert"
|
||||
export NIX_SSL_CERT_FILE="$HOME/.nix-profile/etc/ssl/certs/ca-bundle.crt"
|
||||
fi
|
||||
|
||||
# Subscribe the user to the Nixpkgs channel and fetch it.
|
||||
if [ -z "$NIX_INSTALLER_NO_CHANNEL_ADD" ]; then
|
||||
if ! $nix/bin/nix-channel --list | grep -q "^nixpkgs "; then
|
||||
$nix/bin/nix-channel --add https://nixos.org/channels/nixpkgs-unstable
|
||||
if ! "$nix/bin/nix-channel" --list | grep -q "^nixpkgs "; then
|
||||
"$nix/bin/nix-channel" --add https://nixos.org/channels/nixpkgs-unstable
|
||||
fi
|
||||
if [ -z "$_NIX_INSTALLER_TEST" ]; then
|
||||
if ! $nix/bin/nix-channel --update nixpkgs; then
|
||||
if ! "$nix/bin/nix-channel" --update nixpkgs; then
|
||||
echo "Fetching the nixpkgs channel failed. (Are you offline?)"
|
||||
echo "To try again later, run \"nix-channel --update nixpkgs\"."
|
||||
fi
|
||||
|
||||
@@ -15,7 +15,7 @@ readonly SERVICE_OVERRIDE=${SERVICE_DEST}.d/override.conf
|
||||
|
||||
create_systemd_override() {
|
||||
header "Configuring proxy for the nix-daemon service"
|
||||
_sudo "create directory for systemd unit override" mkdir -p "$(dirname $SERVICE_OVERRIDE)"
|
||||
_sudo "create directory for systemd unit override" mkdir -p "$(dirname "$SERVICE_OVERRIDE")"
|
||||
cat <<EOF | _sudo "create systemd unit override" tee "$SERVICE_OVERRIDE"
|
||||
[Service]
|
||||
$1
|
||||
|
||||
@@ -81,10 +81,10 @@ if [ "$(uname -s)" != "Darwin" ]; then
|
||||
require_util xz "unpack the binary tarball"
|
||||
fi
|
||||
|
||||
if command -v wget > /dev/null 2>&1; then
|
||||
fetch() { wget "$1" -O "$2"; }
|
||||
elif command -v curl > /dev/null 2>&1; then
|
||||
if command -v curl > /dev/null 2>&1; then
|
||||
fetch() { curl -L "$1" -o "$2"; }
|
||||
elif command -v wget > /dev/null 2>&1; then
|
||||
fetch() { wget "$1" -O "$2"; }
|
||||
else
|
||||
oops "you don't have wget or curl installed, which I need to download the binary tarball"
|
||||
fi
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
nix_noinst_scripts := \
|
||||
$(d)/nix-http-export.cgi \
|
||||
$(d)/nix-profile.sh \
|
||||
$(d)/nix-reduce-build
|
||||
$(d)/nix-profile.sh
|
||||
|
||||
noinst-scripts += $(nix_noinst_scripts)
|
||||
|
||||
|
||||
@@ -1,51 +0,0 @@
|
||||
#! /bin/sh
|
||||
|
||||
export HOME=/tmp
|
||||
export NIX_REMOTE=daemon
|
||||
|
||||
TMP_DIR="${TMP_DIR:-/tmp/nix-export}"
|
||||
|
||||
@coreutils@/mkdir -p "$TMP_DIR" || true
|
||||
@coreutils@/chmod a+r "$TMP_DIR"
|
||||
|
||||
needed_path="?$QUERY_STRING"
|
||||
needed_path="${needed_path#*[?&]needed_path=}"
|
||||
needed_path="${needed_path%%&*}"
|
||||
#needed_path="$(echo $needed_path | ./unhttp)"
|
||||
needed_path="${needed_path//%2B/+}"
|
||||
needed_path="${needed_path//%3D/=}"
|
||||
|
||||
echo needed_path: "$needed_path" >&2
|
||||
|
||||
NIX_STORE="${NIX_STORE_DIR:-/nix/store}"
|
||||
|
||||
echo NIX_STORE: "${NIX_STORE}" >&2
|
||||
|
||||
full_path="${NIX_STORE}"/"$needed_path"
|
||||
|
||||
if [ "$needed_path" != "${needed_path%.drv}" ]; then
|
||||
echo "Status: 403 You should create the derivation file yourself"
|
||||
echo "Content-Type: text/plain"
|
||||
echo
|
||||
echo "Refusing to disclose derivation contents"
|
||||
exit
|
||||
fi
|
||||
|
||||
if @bindir@/nix-store --check-validity "$full_path"; then
|
||||
if ! [ -e nix-export/"$needed_path".nar.gz ]; then
|
||||
@bindir@/nix-store --export "$full_path" | @gzip@ > "$TMP_DIR"/"$needed_path".nar.gz
|
||||
@coreutils@/ln -fs "$TMP_DIR"/"$needed_path".nar.gz nix-export/"$needed_path".nar.gz
|
||||
fi;
|
||||
echo "Status: 301 Moved"
|
||||
echo "Location: nix-export/"$needed_path".nar.gz"
|
||||
echo
|
||||
else
|
||||
echo "Status: 404 No such path found"
|
||||
echo "Content-Type: text/plain"
|
||||
echo
|
||||
echo "Path not found:"
|
||||
echo "$needed_path"
|
||||
echo "checked:"
|
||||
echo "$full_path"
|
||||
fi
|
||||
|
||||
@@ -5,7 +5,7 @@ __ETC_PROFILE_NIX_SOURCED=1
|
||||
export NIX_PROFILES="@localstatedir@/nix/profiles/default $HOME/.nix-profile"
|
||||
|
||||
# Set $NIX_SSL_CERT_FILE so that Nixpkgs applications like curl work.
|
||||
if [ ! -z "${NIX_SSL_CERT_FILE:-}" ]; then
|
||||
if [ -n "${NIX_SSL_CERT_FILE:-}" ]; then
|
||||
: # Allow users to override the NIX_SSL_CERT_FILE
|
||||
elif [ -e /etc/ssl/certs/ca-certificates.crt ]; then # NixOS, Ubuntu, Debian, Gentoo, Arch
|
||||
export NIX_SSL_CERT_FILE=/etc/ssl/certs/ca-certificates.crt
|
||||
@@ -18,14 +18,14 @@ elif [ -e /etc/pki/tls/certs/ca-bundle.crt ]; then # Fedora, CentOS
|
||||
else
|
||||
# Fall back to what is in the nix profiles, favouring whatever is defined last.
|
||||
check_nix_profiles() {
|
||||
if [ "$ZSH_VERSION" ]; then
|
||||
if [ -n "$ZSH_VERSION" ]; then
|
||||
# Zsh by default doesn't split words in unquoted parameter expansion.
|
||||
# Set local_options for these options to be reverted at the end of the function
|
||||
# and shwordsplit to force splitting words in $NIX_PROFILES below.
|
||||
setopt local_options shwordsplit
|
||||
fi
|
||||
for i in $NIX_PROFILES; do
|
||||
if [ -e $i/etc/ssl/certs/ca-bundle.crt ]; then
|
||||
if [ -e "$i/etc/ssl/certs/ca-bundle.crt" ]; then
|
||||
export NIX_SSL_CERT_FILE=$i/etc/ssl/certs/ca-bundle.crt
|
||||
fi
|
||||
done
|
||||
|
||||
@@ -1,171 +0,0 @@
|
||||
#! @bash@
|
||||
|
||||
WORKING_DIRECTORY=$(mktemp -d "${TMPDIR:-/tmp}"/nix-reduce-build-XXXXXX);
|
||||
cd "$WORKING_DIRECTORY";
|
||||
|
||||
if test -z "$1" || test "a--help" = "a$1" ; then
|
||||
echo 'nix-reduce-build (paths or Nix expressions) -- (package sources)' >&2
|
||||
echo As in: >&2
|
||||
echo nix-reduce-build /etc/nixos/nixos -- ssh://user@somewhere.nowhere.example.org >&2
|
||||
echo nix-reduce-build /etc/nixos/nixos -- \\
|
||||
echo " " \''http://somewhere.nowhere.example.org/nix/nix-http-export.cgi?needed_path='\' >&2
|
||||
echo " store path name will be added into the end of the URL" >&2
|
||||
echo nix-reduce-build /etc/nixos/nixos -- file://home/user/nar/ >&2
|
||||
echo " that should be a directory where gzipped 'nix-store --export' ">&2
|
||||
echo " files are located (they should have .nar.gz extension)" >&2
|
||||
echo " Or all together: " >&2
|
||||
echo -e nix-reduce-build /expr.nix /e2.nix -- \\\\\\\n\
|
||||
" ssh://a@b.example.com http://n.example.com/get-nar?q= file://nar/" >&2
|
||||
echo " Also supports best-effort local builds of failing expression set:" >&2
|
||||
echo "nix-reduce-build /e.nix -- nix-daemon:// nix-self://" >&2
|
||||
echo " nix-daemon:// builds using daemon"
|
||||
echo " nix-self:// builds directly using nix-store from current installation" >&2
|
||||
echo " nix-daemon-fixed:// and nix-self-fixed:// do the same, but only for" >&2;
|
||||
echo "derivations with specified output hash (sha256, sha1 or md5)." >&2
|
||||
echo " nix-daemon-substitute:// and nix-self-substitute:// try to substitute" >&2;
|
||||
echo "maximum amount of paths" >&2;
|
||||
echo " nix-daemon-build:// and nix-self-build:// try to build (not substitute)" >&2;
|
||||
echo "maximum amount of paths" >&2;
|
||||
echo " If no package sources are specified, required paths are listed." >&2;
|
||||
exit;
|
||||
fi;
|
||||
|
||||
while ! test "$1" = "--" || test "$1" = "" ; do
|
||||
echo "$1" >> initial; >&2
|
||||
shift;
|
||||
done
|
||||
shift;
|
||||
echo Will work on $(cat initial | wc -l) targets. >&2
|
||||
|
||||
while read ; do
|
||||
case "$REPLY" in
|
||||
${NIX_STORE_DIR:-/nix/store}/*)
|
||||
echo "$REPLY" >> paths; >&2
|
||||
;;
|
||||
*)
|
||||
(
|
||||
IFS=: ;
|
||||
nix-instantiate $REPLY >> paths;
|
||||
);
|
||||
;;
|
||||
esac;
|
||||
done < initial;
|
||||
echo Proceeding $(cat paths | wc -l) paths. >&2
|
||||
|
||||
while read; do
|
||||
case "$REPLY" in
|
||||
*.drv)
|
||||
echo "$REPLY" >> derivers; >&2
|
||||
;;
|
||||
*)
|
||||
nix-store --query --deriver "$REPLY" >>derivers;
|
||||
;;
|
||||
esac;
|
||||
done < paths;
|
||||
echo Found $(cat derivers | wc -l) derivers. >&2
|
||||
|
||||
cat derivers | xargs nix-store --query -R > derivers-closure;
|
||||
echo Proceeding at most $(cat derivers-closure | wc -l) derivers. >&2
|
||||
|
||||
cat derivers-closure | egrep '[.]drv$' | xargs nix-store --query --outputs > wanted-paths;
|
||||
cat derivers-closure | egrep -v '[.]drv$' >> wanted-paths;
|
||||
echo Prepared $(cat wanted-paths | wc -l) paths to get. >&2
|
||||
|
||||
cat wanted-paths | xargs nix-store --check-validity --print-invalid > needed-paths;
|
||||
echo We need $(cat needed-paths | wc -l) paths. >&2
|
||||
|
||||
egrep '[.]drv$' derivers-closure > critical-derivers;
|
||||
|
||||
if test -z "$1" ; then
|
||||
cat needed-paths;
|
||||
fi;
|
||||
|
||||
refresh_critical_derivers() {
|
||||
echo "Finding needed derivers..." >&2;
|
||||
cat critical-derivers | while read; do
|
||||
if ! (nix-store --query --outputs "$REPLY" | xargs nix-store --check-validity &> /dev/null;); then
|
||||
echo "$REPLY";
|
||||
fi;
|
||||
done > new-critical-derivers;
|
||||
mv new-critical-derivers critical-derivers;
|
||||
echo The needed paths are realized by $(cat critical-derivers | wc -l) derivers. >&2
|
||||
}
|
||||
|
||||
build_here() {
|
||||
cat critical-derivers | while read; do
|
||||
echo "Realising $REPLY using nix-daemon" >&2
|
||||
@bindir@/nix-store -r "${REPLY}"
|
||||
done;
|
||||
}
|
||||
|
||||
try_to_substitute(){
|
||||
cat needed-paths | while read ; do
|
||||
echo "Building $REPLY using nix-daemon" >&2
|
||||
@bindir@/nix-store -r "${NIX_STORE_DIR:-/nix/store}/${REPLY##*/}"
|
||||
done;
|
||||
}
|
||||
|
||||
for i in "$@"; do
|
||||
sshHost="${i#ssh://}";
|
||||
httpHost="${i#http://}";
|
||||
httpsHost="${i#https://}";
|
||||
filePath="${i#file:/}";
|
||||
if [ "$i" != "$sshHost" ]; then
|
||||
cat needed-paths | while read; do
|
||||
echo "Getting $REPLY and its closure over ssh" >&2
|
||||
nix-copy-closure --from "$sshHost" --gzip "$REPLY" </dev/null || true;
|
||||
done;
|
||||
elif [ "$i" != "$httpHost" ] || [ "$i" != "$httpsHost" ]; then
|
||||
cat needed-paths | while read; do
|
||||
echo "Getting $REPLY over http/https" >&2
|
||||
curl ${BAD_CERTIFICATE:+-k} -L "$i${REPLY##*/}" | gunzip | nix-store --import;
|
||||
done;
|
||||
elif [ "$i" != "$filePath" ] ; then
|
||||
cat needed-paths | while read; do
|
||||
echo "Installing $REPLY from file" >&2
|
||||
gunzip < "$filePath/${REPLY##*/}".nar.gz | nix-store --import;
|
||||
done;
|
||||
elif [ "$i" = "nix-daemon://" ] ; then
|
||||
NIX_REMOTE=daemon try_to_substitute;
|
||||
refresh_critical_derivers;
|
||||
NIX_REMOTE=daemon build_here;
|
||||
elif [ "$i" = "nix-self://" ] ; then
|
||||
NIX_REMOTE= try_to_substitute;
|
||||
refresh_critical_derivers;
|
||||
NIX_REMOTE= build_here;
|
||||
elif [ "$i" = "nix-daemon-fixed://" ] ; then
|
||||
refresh_critical_derivers;
|
||||
|
||||
cat critical-derivers | while read; do
|
||||
if egrep '"(md5|sha1|sha256)"' "$REPLY" &>/dev/null; then
|
||||
echo "Realising $REPLY using nix-daemon" >&2
|
||||
NIX_REMOTE=daemon @bindir@/nix-store -r "${REPLY}"
|
||||
fi;
|
||||
done;
|
||||
elif [ "$i" = "nix-self-fixed://" ] ; then
|
||||
refresh_critical_derivers;
|
||||
|
||||
cat critical-derivers | while read; do
|
||||
if egrep '"(md5|sha1|sha256)"' "$REPLY" &>/dev/null; then
|
||||
echo "Realising $REPLY using direct Nix build" >&2
|
||||
NIX_REMOTE= @bindir@/nix-store -r "${REPLY}"
|
||||
fi;
|
||||
done;
|
||||
elif [ "$i" = "nix-daemon-substitute://" ] ; then
|
||||
NIX_REMOTE=daemon try_to_substitute;
|
||||
elif [ "$i" = "nix-self-substitute://" ] ; then
|
||||
NIX_REMOTE= try_to_substitute;
|
||||
elif [ "$i" = "nix-daemon-build://" ] ; then
|
||||
refresh_critical_derivers;
|
||||
NIX_REMOTE=daemon build_here;
|
||||
elif [ "$i" = "nix-self-build://" ] ; then
|
||||
refresh_critical_derivers;
|
||||
NIX_REMOTE= build_here;
|
||||
fi;
|
||||
mv needed-paths wanted-paths;
|
||||
cat wanted-paths | xargs nix-store --check-validity --print-invalid > needed-paths;
|
||||
echo We still need $(cat needed-paths | wc -l) paths. >&2
|
||||
done;
|
||||
|
||||
cd /
|
||||
rm -r "$WORKING_DIRECTORY"
|
||||
@@ -3,7 +3,7 @@
|
||||
set -e
|
||||
|
||||
script=$(nix-build -A outputs.hydraJobs.installerScriptForGHA --no-out-link)
|
||||
installerHash=$(echo $script | cut -b12-43 -)
|
||||
installerHash=$(echo "$script" | cut -b12-43 -)
|
||||
|
||||
installerURL=https://$CACHIX_NAME.cachix.org/serve/$installerHash/install
|
||||
|
||||
|
||||
@@ -1,18 +0,0 @@
|
||||
Copyright (c) 2014 Chase Geigle
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
this software and associated documentation files (the "Software"), to deal in
|
||||
the Software without restriction, including without limitation the rights to
|
||||
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
||||
the Software, and to permit persons to whom the Software is furnished to do so,
|
||||
subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
File diff suppressed because it is too large
Load Diff
@@ -54,6 +54,36 @@ void StoreCommand::run()
|
||||
run(getStore());
|
||||
}
|
||||
|
||||
CopyCommand::CopyCommand()
|
||||
{
|
||||
addFlag({
|
||||
.longName = "from",
|
||||
.description = "URL of the source Nix store.",
|
||||
.labels = {"store-uri"},
|
||||
.handler = {&srcUri},
|
||||
});
|
||||
|
||||
addFlag({
|
||||
.longName = "to",
|
||||
.description = "URL of the destination Nix store.",
|
||||
.labels = {"store-uri"},
|
||||
.handler = {&dstUri},
|
||||
});
|
||||
}
|
||||
|
||||
ref<Store> CopyCommand::createStore()
|
||||
{
|
||||
return srcUri.empty() ? StoreCommand::createStore() : openStore(srcUri);
|
||||
}
|
||||
|
||||
ref<Store> CopyCommand::getDstStore()
|
||||
{
|
||||
if (srcUri.empty() && dstUri.empty())
|
||||
throw UsageError("you must pass '--from' and/or '--to'");
|
||||
|
||||
return dstUri.empty() ? openStore() : openStore(dstUri);
|
||||
}
|
||||
|
||||
EvalCommand::EvalCommand()
|
||||
{
|
||||
}
|
||||
@@ -74,7 +104,15 @@ ref<Store> EvalCommand::getEvalStore()
|
||||
ref<EvalState> EvalCommand::getEvalState()
|
||||
{
|
||||
if (!evalState)
|
||||
evalState = std::make_shared<EvalState>(searchPath, getEvalStore(), getStore());
|
||||
evalState =
|
||||
#if HAVE_BOEHMGC
|
||||
std::allocate_shared<EvalState>(traceable_allocator<EvalState>(),
|
||||
searchPath, getEvalStore(), getStore())
|
||||
#else
|
||||
std::make_shared<EvalState>(
|
||||
searchPath, getEvalStore(), getStore())
|
||||
#endif
|
||||
;
|
||||
return ref<EvalState>(evalState);
|
||||
}
|
||||
|
||||
|
||||
@@ -43,6 +43,19 @@ private:
|
||||
std::shared_ptr<Store> _store;
|
||||
};
|
||||
|
||||
/* A command that copies something between `--from` and `--to`
|
||||
stores. */
|
||||
struct CopyCommand : virtual StoreCommand
|
||||
{
|
||||
std::string srcUri, dstUri;
|
||||
|
||||
CopyCommand();
|
||||
|
||||
ref<Store> createStore() override;
|
||||
|
||||
ref<Store> getDstStore();
|
||||
};
|
||||
|
||||
struct EvalCommand : virtual StoreCommand, MixEvalArgs
|
||||
{
|
||||
EvalCommand();
|
||||
|
||||
@@ -191,7 +191,7 @@ void SourceExprCommand::completeInstallable(std::string_view prefix)
|
||||
auto sep = prefix_.rfind('.');
|
||||
std::string searchWord;
|
||||
if (sep != std::string::npos) {
|
||||
searchWord = prefix_.substr(sep, std::string::npos);
|
||||
searchWord = prefix_.substr(sep + 1, std::string::npos);
|
||||
prefix_ = prefix_.substr(0, sep);
|
||||
} else {
|
||||
searchWord = prefix_;
|
||||
@@ -203,6 +203,8 @@ void SourceExprCommand::completeInstallable(std::string_view prefix)
|
||||
Value v2;
|
||||
state->autoCallFunction(*autoArgs, v1, v2);
|
||||
|
||||
completionType = ctAttrs;
|
||||
|
||||
if (v2.type() == nAttrs) {
|
||||
for (auto & i : *v2.attrs) {
|
||||
std::string name = i.name;
|
||||
@@ -232,7 +234,9 @@ void completeFlakeRefWithFragment(
|
||||
prefix. */
|
||||
try {
|
||||
auto hash = prefix.find('#');
|
||||
if (hash != std::string::npos) {
|
||||
if (hash == std::string::npos) {
|
||||
completeFlakeRef(evalState->store, prefix);
|
||||
} else {
|
||||
auto fragment = prefix.substr(hash + 1);
|
||||
auto flakeRefS = std::string(prefix.substr(0, hash));
|
||||
// FIXME: do tilde expansion.
|
||||
@@ -248,6 +252,8 @@ void completeFlakeRefWithFragment(
|
||||
flake. */
|
||||
attrPathPrefixes.push_back("");
|
||||
|
||||
completionType = ctAttrs;
|
||||
|
||||
for (auto & attrPathPrefixS : attrPathPrefixes) {
|
||||
auto attrPathPrefix = parseAttrPath(*evalState, attrPathPrefixS);
|
||||
auto attrPathS = attrPathPrefixS + std::string(fragment);
|
||||
@@ -285,12 +291,13 @@ void completeFlakeRefWithFragment(
|
||||
} catch (Error & e) {
|
||||
warn(e.msg());
|
||||
}
|
||||
|
||||
completeFlakeRef(evalState->store, prefix);
|
||||
}
|
||||
|
||||
void completeFlakeRef(ref<Store> store, std::string_view prefix)
|
||||
{
|
||||
if (!settings.isExperimentalFeatureEnabled(Xp::Flakes))
|
||||
return;
|
||||
|
||||
if (prefix == "")
|
||||
completions->add(".");
|
||||
|
||||
@@ -338,6 +345,18 @@ Installable::getCursor(EvalState & state)
|
||||
return cursors[0];
|
||||
}
|
||||
|
||||
static StorePath getDeriver(
|
||||
ref<Store> store,
|
||||
const Installable & i,
|
||||
const StorePath & drvPath)
|
||||
{
|
||||
auto derivers = store->queryValidDerivers(drvPath);
|
||||
if (derivers.empty())
|
||||
throw Error("'%s' does not have a known deriver", i.what());
|
||||
// FIXME: use all derivers?
|
||||
return *derivers.begin();
|
||||
}
|
||||
|
||||
struct InstallableStorePath : Installable
|
||||
{
|
||||
ref<Store> store;
|
||||
@@ -346,7 +365,7 @@ struct InstallableStorePath : Installable
|
||||
InstallableStorePath(ref<Store> store, StorePath && storePath)
|
||||
: store(store), storePath(std::move(storePath)) { }
|
||||
|
||||
std::string what() override { return store->printStorePath(storePath); }
|
||||
std::string what() const override { return store->printStorePath(storePath); }
|
||||
|
||||
DerivedPaths toDerivedPaths() override
|
||||
{
|
||||
@@ -367,6 +386,15 @@ struct InstallableStorePath : Installable
|
||||
}
|
||||
}
|
||||
|
||||
StorePathSet toDrvPaths(ref<Store> store) override
|
||||
{
|
||||
if (storePath.isDerivation()) {
|
||||
return {storePath};
|
||||
} else {
|
||||
return {getDeriver(store, *this, storePath)};
|
||||
}
|
||||
}
|
||||
|
||||
std::optional<StorePath> getStorePath() override
|
||||
{
|
||||
return storePath;
|
||||
@@ -395,6 +423,14 @@ DerivedPaths InstallableValue::toDerivedPaths()
|
||||
return res;
|
||||
}
|
||||
|
||||
StorePathSet InstallableValue::toDrvPaths(ref<Store> store)
|
||||
{
|
||||
StorePathSet res;
|
||||
for (auto & drv : toDerivations())
|
||||
res.insert(drv.drvPath);
|
||||
return res;
|
||||
}
|
||||
|
||||
struct InstallableAttrPath : InstallableValue
|
||||
{
|
||||
SourceExprCommand & cmd;
|
||||
@@ -405,7 +441,7 @@ struct InstallableAttrPath : InstallableValue
|
||||
: InstallableValue(state), cmd(cmd), v(allocRootValue(v)), attrPath(attrPath)
|
||||
{ }
|
||||
|
||||
std::string what() override { return attrPath; }
|
||||
std::string what() const override { return attrPath; }
|
||||
|
||||
std::pair<Value *, Pos> toValue(EvalState & state) override
|
||||
{
|
||||
@@ -829,11 +865,7 @@ StorePathSet toDerivations(
|
||||
[&](const DerivedPath::Opaque & bo) {
|
||||
if (!useDeriver)
|
||||
throw Error("argument '%s' did not evaluate to a derivation", i->what());
|
||||
auto derivers = store->queryValidDerivers(bo.path);
|
||||
if (derivers.empty())
|
||||
throw Error("'%s' does not have a known deriver", i->what());
|
||||
// FIXME: use all derivers?
|
||||
drvPaths.insert(*derivers.begin());
|
||||
drvPaths.insert(getDeriver(store, *i, bo.path));
|
||||
},
|
||||
[&](const DerivedPath::Built & bfd) {
|
||||
drvPaths.insert(bfd.drvPath);
|
||||
|
||||
@@ -33,10 +33,15 @@ struct Installable
|
||||
{
|
||||
virtual ~Installable() { }
|
||||
|
||||
virtual std::string what() = 0;
|
||||
virtual std::string what() const = 0;
|
||||
|
||||
virtual DerivedPaths toDerivedPaths() = 0;
|
||||
|
||||
virtual StorePathSet toDrvPaths(ref<Store> store)
|
||||
{
|
||||
throw Error("'%s' cannot be converted to a derivation path", what());
|
||||
}
|
||||
|
||||
DerivedPath toDerivedPath();
|
||||
|
||||
UnresolvedApp toApp(EvalState & state);
|
||||
@@ -81,6 +86,8 @@ struct InstallableValue : Installable
|
||||
virtual std::vector<DerivationInfo> toDerivations() = 0;
|
||||
|
||||
DerivedPaths toDerivedPaths() override;
|
||||
|
||||
StorePathSet toDrvPaths(ref<Store> store) override;
|
||||
};
|
||||
|
||||
struct InstallableFlake : InstallableValue
|
||||
@@ -99,7 +106,7 @@ struct InstallableFlake : InstallableValue
|
||||
Strings && prefixes,
|
||||
const flake::LockFlags & lockFlags);
|
||||
|
||||
std::string what() override { return flakeRef.to_string() + "#" + *attrPaths.begin(); }
|
||||
std::string what() const override { return flakeRef.to_string() + "#" + *attrPaths.begin(); }
|
||||
|
||||
std::vector<std::string> getActualAttrPaths();
|
||||
|
||||
|
||||
@@ -7,26 +7,19 @@
|
||||
namespace nix {
|
||||
|
||||
|
||||
|
||||
/* Allocate a new array of attributes for an attribute set with a specific
|
||||
capacity. The space is implicitly reserved after the Bindings
|
||||
structure. */
|
||||
Bindings * EvalState::allocBindings(size_t capacity)
|
||||
{
|
||||
if (capacity == 0)
|
||||
return &emptyBindings;
|
||||
if (capacity > std::numeric_limits<Bindings::size_t>::max())
|
||||
throw Error("attribute set of size %d is too big", capacity);
|
||||
return new (allocBytes(sizeof(Bindings) + sizeof(Attr) * capacity)) Bindings((Bindings::size_t) capacity);
|
||||
}
|
||||
|
||||
|
||||
void EvalState::mkAttrs(Value & v, size_t capacity)
|
||||
{
|
||||
if (capacity == 0) {
|
||||
v = vEmptySet;
|
||||
return;
|
||||
}
|
||||
v.mkAttrs(allocBindings(capacity));
|
||||
nrAttrsets++;
|
||||
nrAttrsInAttrsets += capacity;
|
||||
return new (allocBytes(sizeof(Bindings) + sizeof(Attr) * capacity)) Bindings((Bindings::size_t) capacity);
|
||||
}
|
||||
|
||||
|
||||
@@ -41,15 +34,36 @@ Value * EvalState::allocAttr(Value & vAttrs, const Symbol & name)
|
||||
}
|
||||
|
||||
|
||||
Value * EvalState::allocAttr(Value & vAttrs, const std::string & name)
|
||||
Value * EvalState::allocAttr(Value & vAttrs, std::string_view name)
|
||||
{
|
||||
return allocAttr(vAttrs, symbols.create(name));
|
||||
}
|
||||
|
||||
|
||||
Value & BindingsBuilder::alloc(const Symbol & name, ptr<Pos> pos)
|
||||
{
|
||||
auto value = state.allocValue();
|
||||
bindings->push_back(Attr(name, value, pos));
|
||||
return *value;
|
||||
}
|
||||
|
||||
|
||||
Value & BindingsBuilder::alloc(std::string_view name, ptr<Pos> pos)
|
||||
{
|
||||
return alloc(state.symbols.create(name), pos);
|
||||
}
|
||||
|
||||
|
||||
void Bindings::sort()
|
||||
{
|
||||
std::sort(begin(), end());
|
||||
if (size_) std::sort(begin(), end());
|
||||
}
|
||||
|
||||
|
||||
Value & Value::mkAttrs(BindingsBuilder & bindings)
|
||||
{
|
||||
mkAttrs(bindings.finish());
|
||||
return *this;
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -113,5 +113,52 @@ public:
|
||||
friend class EvalState;
|
||||
};
|
||||
|
||||
/* A wrapper around Bindings that ensures that its always in sorted
|
||||
order at the end. The only way to consume a BindingsBuilder is to
|
||||
call finish(), which sorts the bindings. */
|
||||
class BindingsBuilder
|
||||
{
|
||||
Bindings * bindings;
|
||||
|
||||
public:
|
||||
// needed by std::back_inserter
|
||||
using value_type = Attr;
|
||||
|
||||
EvalState & state;
|
||||
|
||||
BindingsBuilder(EvalState & state, Bindings * bindings)
|
||||
: bindings(bindings), state(state)
|
||||
{ }
|
||||
|
||||
void insert(Symbol name, Value * value, ptr<Pos> pos = ptr(&noPos))
|
||||
{
|
||||
insert(Attr(name, value, pos));
|
||||
}
|
||||
|
||||
void insert(const Attr & attr)
|
||||
{
|
||||
push_back(attr);
|
||||
}
|
||||
|
||||
void push_back(const Attr & attr)
|
||||
{
|
||||
bindings->push_back(attr);
|
||||
}
|
||||
|
||||
Value & alloc(const Symbol & name, ptr<Pos> pos = ptr(&noPos));
|
||||
|
||||
Value & alloc(std::string_view name, ptr<Pos> pos = ptr(&noPos));
|
||||
|
||||
Bindings * finish()
|
||||
{
|
||||
bindings->sort();
|
||||
return bindings;
|
||||
}
|
||||
|
||||
Bindings * alreadySorted()
|
||||
{
|
||||
return bindings;
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@@ -73,17 +73,16 @@ MixEvalArgs::MixEvalArgs()
|
||||
|
||||
Bindings * MixEvalArgs::getAutoArgs(EvalState & state)
|
||||
{
|
||||
Bindings * res = state.allocBindings(autoArgs.size());
|
||||
auto res = state.buildBindings(autoArgs.size());
|
||||
for (auto & i : autoArgs) {
|
||||
Value * v = state.allocValue();
|
||||
auto v = state.allocValue();
|
||||
if (i.second[0] == 'E')
|
||||
state.mkThunk_(*v, state.parseExprFromString(string(i.second, 1), absPath(".")));
|
||||
else
|
||||
mkString(*v, string(i.second, 1));
|
||||
res->push_back(Attr(state.symbols.create(i.first), v));
|
||||
v->mkString(((std::string_view) i.second).substr(1));
|
||||
res.insert(state.symbols.create(i.first), v);
|
||||
}
|
||||
res->sort();
|
||||
return res;
|
||||
return res.finish();
|
||||
}
|
||||
|
||||
Path lookupFileArg(EvalState & state, string s)
|
||||
|
||||
@@ -36,6 +36,19 @@
|
||||
namespace nix {
|
||||
|
||||
|
||||
static char * allocString(size_t size)
|
||||
{
|
||||
char * t;
|
||||
#if HAVE_BOEHMGC
|
||||
t = (char *) GC_MALLOC_ATOMIC(size);
|
||||
#else
|
||||
t = malloc(size);
|
||||
#endif
|
||||
if (!t) throw std::bad_alloc();
|
||||
return t;
|
||||
}
|
||||
|
||||
|
||||
static char * dupString(const char * s)
|
||||
{
|
||||
char * t;
|
||||
@@ -119,15 +132,14 @@ void printValue(std::ostream & str, std::set<const Value *> & active, const Valu
|
||||
case tList2:
|
||||
case tListN:
|
||||
str << "[ ";
|
||||
for (unsigned int n = 0; n < v.listSize(); ++n) {
|
||||
printValue(str, active, *v.listElems()[n]);
|
||||
for (auto v2 : v.listItems()) {
|
||||
printValue(str, active, *v2);
|
||||
str << " ";
|
||||
}
|
||||
str << "]";
|
||||
break;
|
||||
case tThunk:
|
||||
case tApp:
|
||||
case tPartialApp:
|
||||
str << "<CODE>";
|
||||
break;
|
||||
case tLambda:
|
||||
@@ -146,7 +158,7 @@ void printValue(std::ostream & str, std::set<const Value *> & active, const Valu
|
||||
str << v.fpoint;
|
||||
break;
|
||||
default:
|
||||
throw Error("invalid value");
|
||||
abort();
|
||||
}
|
||||
|
||||
active.erase(&v);
|
||||
@@ -413,10 +425,21 @@ EvalState::EvalState(
|
||||
, sDescription(symbols.create("description"))
|
||||
, sSelf(symbols.create("self"))
|
||||
, sEpsilon(symbols.create(""))
|
||||
, sStartSet(symbols.create("startSet"))
|
||||
, sOperator(symbols.create("operator"))
|
||||
, sKey(symbols.create("key"))
|
||||
, sPath(symbols.create("path"))
|
||||
, sPrefix(symbols.create("prefix"))
|
||||
, repair(NoRepair)
|
||||
, emptyBindings(0)
|
||||
, store(store)
|
||||
, buildStore(buildStore ? buildStore : store)
|
||||
, regexCache(makeRegexCache())
|
||||
#if HAVE_BOEHMGC
|
||||
, valueAllocCache(std::allocate_shared<void *>(traceable_allocator<void *>(), nullptr))
|
||||
#else
|
||||
, valueAllocCache(std::make_shared<void *>(nullptr))
|
||||
#endif
|
||||
, baseEnv(allocEnv(128))
|
||||
, staticBaseEnv(false, 0)
|
||||
{
|
||||
@@ -455,8 +478,6 @@ EvalState::EvalState(
|
||||
}
|
||||
}
|
||||
|
||||
vEmptySet.mkAttrs(allocBindings(0));
|
||||
|
||||
createBaseEnv();
|
||||
}
|
||||
|
||||
@@ -520,8 +541,12 @@ Path EvalState::checkSourcePath(const Path & path_)
|
||||
}
|
||||
}
|
||||
|
||||
if (!found)
|
||||
throw RestrictedPathError("access to path '%1%' is forbidden in restricted mode", abspath);
|
||||
if (!found) {
|
||||
auto modeInformation = evalSettings.pureEval
|
||||
? "in pure eval mode (use '--impure' to override)"
|
||||
: "in restricted mode";
|
||||
throw RestrictedPathError("access to absolute path '%1%' is forbidden %2%", abspath, modeInformation);
|
||||
}
|
||||
|
||||
/* Resolve symlinks. */
|
||||
debug(format("checking access to '%s'") % abspath);
|
||||
@@ -534,7 +559,7 @@ Path EvalState::checkSourcePath(const Path & path_)
|
||||
}
|
||||
}
|
||||
|
||||
throw RestrictedPathError("access to path '%1%' is forbidden in restricted mode", path);
|
||||
throw RestrictedPathError("access to canonical path '%1%' is forbidden in restricted mode", path);
|
||||
}
|
||||
|
||||
|
||||
@@ -610,7 +635,7 @@ Value * EvalState::addPrimOp(const string & name,
|
||||
auto vPrimOp = allocValue();
|
||||
vPrimOp->mkPrimOp(new PrimOp { .fun = primOp, .arity = 1, .name = sym });
|
||||
Value v;
|
||||
mkApp(v, *vPrimOp, *vPrimOp);
|
||||
v.mkApp(vPrimOp, vPrimOp);
|
||||
return addConstant(name, v);
|
||||
}
|
||||
|
||||
@@ -632,7 +657,7 @@ Value * EvalState::addPrimOp(PrimOp && primOp)
|
||||
auto vPrimOp = allocValue();
|
||||
vPrimOp->mkPrimOp(new PrimOp(std::move(primOp)));
|
||||
Value v;
|
||||
mkApp(v, *vPrimOp, *vPrimOp);
|
||||
v.mkApp(vPrimOp, vPrimOp);
|
||||
return addConstant(primOp.name, v);
|
||||
}
|
||||
|
||||
@@ -763,15 +788,14 @@ LocalNoInline(void addErrorTrace(Error & e, const Pos & pos, const char * s, con
|
||||
}
|
||||
|
||||
|
||||
void mkString(Value & v, const char * s)
|
||||
void Value::mkString(std::string_view s)
|
||||
{
|
||||
v.mkString(dupString(s));
|
||||
mkString(dupStringWithLen(s.data(), s.size()));
|
||||
}
|
||||
|
||||
|
||||
Value & mkString(Value & v, std::string_view s, const PathSet & context)
|
||||
static void copyContextToValue(Value & v, const PathSet & context)
|
||||
{
|
||||
v.mkString(dupStringWithLen(s.data(), s.size()));
|
||||
if (!context.empty()) {
|
||||
size_t n = 0;
|
||||
v.string.context = (const char * *)
|
||||
@@ -780,13 +804,24 @@ Value & mkString(Value & v, std::string_view s, const PathSet & context)
|
||||
v.string.context[n++] = dupString(i.c_str());
|
||||
v.string.context[n] = 0;
|
||||
}
|
||||
return v;
|
||||
}
|
||||
|
||||
void Value::mkString(std::string_view s, const PathSet & context)
|
||||
{
|
||||
mkString(s);
|
||||
copyContextToValue(*this, context);
|
||||
}
|
||||
|
||||
void Value::mkStringMove(const char * s, const PathSet & context)
|
||||
{
|
||||
mkString(s);
|
||||
copyContextToValue(*this, context);
|
||||
}
|
||||
|
||||
|
||||
void mkPath(Value & v, const char * s)
|
||||
void Value::mkPath(std::string_view s)
|
||||
{
|
||||
v.mkPath(dupString(s));
|
||||
mkPath(dupStringWithLen(s.data(), s.size()));
|
||||
}
|
||||
|
||||
|
||||
@@ -818,8 +853,23 @@ inline Value * EvalState::lookupVar(Env * env, const ExprVar & var, bool noEval)
|
||||
|
||||
Value * EvalState::allocValue()
|
||||
{
|
||||
/* We use the boehm batch allocator to speed up allocations of Values (of which there are many).
|
||||
GC_malloc_many returns a linked list of objects of the given size, where the first word
|
||||
of each object is also the pointer to the next object in the list. This also means that we
|
||||
have to explicitly clear the first word of every object we take. */
|
||||
if (!*valueAllocCache) {
|
||||
*valueAllocCache = GC_malloc_many(sizeof(Value));
|
||||
if (!*valueAllocCache) throw std::bad_alloc();
|
||||
}
|
||||
|
||||
/* GC_NEXT is a convenience macro for accessing the first word of an object.
|
||||
Take the first list item, advance the list to the next item, and clear the next pointer. */
|
||||
void * p = *valueAllocCache;
|
||||
GC_PTR_STORE_AND_DIRTY(&*valueAllocCache, GC_NEXT(p));
|
||||
GC_NEXT(p) = nullptr;
|
||||
|
||||
nrValues++;
|
||||
auto v = (Value *) allocBytes(sizeof(Value));
|
||||
auto v = (Value *) p;
|
||||
return v;
|
||||
}
|
||||
|
||||
@@ -864,13 +914,13 @@ void EvalState::mkThunk_(Value & v, Expr * expr)
|
||||
void EvalState::mkPos(Value & v, ptr<Pos> pos)
|
||||
{
|
||||
if (pos->file.set()) {
|
||||
mkAttrs(v, 3);
|
||||
mkString(*allocAttr(v, sFile), pos->file);
|
||||
mkInt(*allocAttr(v, sLine), pos->line);
|
||||
mkInt(*allocAttr(v, sColumn), pos->column);
|
||||
v.attrs->sort();
|
||||
auto attrs = buildBindings(3);
|
||||
attrs.alloc(sFile).mkString(pos->file);
|
||||
attrs.alloc(sLine).mkInt(pos->line);
|
||||
attrs.alloc(sColumn).mkInt(pos->column);
|
||||
v.mkAttrs(attrs);
|
||||
} else
|
||||
mkNull(v);
|
||||
v.mkNull();
|
||||
}
|
||||
|
||||
|
||||
@@ -1049,8 +1099,8 @@ void ExprPath::eval(EvalState & state, Env & env, Value & v)
|
||||
|
||||
void ExprAttrs::eval(EvalState & state, Env & env, Value & v)
|
||||
{
|
||||
state.mkAttrs(v, attrs.size() + dynamicAttrs.size());
|
||||
Env *dynamicEnv = &env;
|
||||
v.mkAttrs(state.buildBindings(attrs.size() + dynamicAttrs.size()).finish());
|
||||
auto dynamicEnv = &env;
|
||||
|
||||
if (recursive) {
|
||||
/* Create a new environment that contains the attributes in
|
||||
@@ -1152,8 +1202,8 @@ void ExprLet::eval(EvalState & state, Env & env, Value & v)
|
||||
void ExprList::eval(EvalState & state, Env & env, Value & v)
|
||||
{
|
||||
state.mkList(v, elems.size());
|
||||
for (size_t n = 0; n < elems.size(); ++n)
|
||||
v.listElems()[n] = elems[n]->maybeThunk(state, env);
|
||||
for (auto [n, v2] : enumerate(v.listItems()))
|
||||
const_cast<Value * &>(v2) = elems[n]->maybeThunk(state, env);
|
||||
}
|
||||
|
||||
|
||||
@@ -1241,14 +1291,14 @@ void ExprOpHasAttr::eval(EvalState & state, Env & env, Value & v)
|
||||
if (vAttrs->type() != nAttrs ||
|
||||
(j = vAttrs->attrs->find(name)) == vAttrs->attrs->end())
|
||||
{
|
||||
mkBool(v, false);
|
||||
v.mkBool(false);
|
||||
return;
|
||||
} else {
|
||||
vAttrs = j->value;
|
||||
}
|
||||
}
|
||||
|
||||
mkBool(v, true);
|
||||
v.mkBool(true);
|
||||
}
|
||||
|
||||
|
||||
@@ -1276,28 +1326,37 @@ void EvalState::callFunction(Value & fun, size_t nrArgs, Value * * args, Value &
|
||||
}
|
||||
};
|
||||
|
||||
auto callLambda = [&](Env * env, ExprLambda & lambda, Value * * args)
|
||||
{
|
||||
Env & env2(allocEnv(lambda.envSize));
|
||||
env2.up = env;
|
||||
Attr * functor;
|
||||
|
||||
Displacement displ = 0;
|
||||
while (nrArgs > 0) {
|
||||
|
||||
for (auto & arg : lambda.args) {
|
||||
auto vArg = *args++;
|
||||
if (vCur.isLambda()) {
|
||||
|
||||
if (arg.arg != sEpsilon)
|
||||
env2.values[displ++] = vArg;
|
||||
ExprLambda & lambda(*vCur.lambda.fun);
|
||||
|
||||
if (arg.formals) {
|
||||
forceAttrs(*vArg, pos);
|
||||
auto size =
|
||||
(lambda.arg.empty() ? 0 : 1) +
|
||||
(lambda.hasFormals() ? lambda.formals->formals.size() : 0);
|
||||
Env & env2(allocEnv(size));
|
||||
env2.up = vCur.lambda.env;
|
||||
|
||||
Displacement displ = 0;
|
||||
|
||||
if (!lambda.hasFormals())
|
||||
env2.values[displ++] = args[0];
|
||||
|
||||
else {
|
||||
forceAttrs(*args[0], pos);
|
||||
|
||||
if (!lambda.arg.empty())
|
||||
env2.values[displ++] = args[0];
|
||||
|
||||
/* For each formal argument, get the actual argument. If
|
||||
there is no matching actual argument but the formal
|
||||
argument has a default, use the default. */
|
||||
size_t attrsUsed = 0;
|
||||
for (auto & i : arg.formals->formals) {
|
||||
auto j = vArg->attrs->get(i.name);
|
||||
for (auto & i : lambda.formals->formals) {
|
||||
auto j = args[0]->attrs->get(i.name);
|
||||
if (!j) {
|
||||
if (!i.def) throwTypeError(pos, "%1% called without required argument '%2%'",
|
||||
lambda, i.name);
|
||||
@@ -1310,96 +1369,35 @@ void EvalState::callFunction(Value & fun, size_t nrArgs, Value * * args, Value &
|
||||
|
||||
/* Check that each actual argument is listed as a formal
|
||||
argument (unless the attribute match specifies a `...'). */
|
||||
if (!arg.formals->ellipsis && attrsUsed != vArg->attrs->size()) {
|
||||
if (!lambda.formals->ellipsis && attrsUsed != args[0]->attrs->size()) {
|
||||
/* Nope, so show the first unexpected argument to the
|
||||
user. */
|
||||
for (auto & i : *vArg->attrs)
|
||||
if (arg.formals->argNames.find(i.name) == arg.formals->argNames.end())
|
||||
for (auto & i : *args[0]->attrs)
|
||||
if (!lambda.formals->argNames.count(i.name))
|
||||
throwTypeError(pos, "%1% called with unexpected argument '%2%'", lambda, i.name);
|
||||
abort(); // can't happen
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
assert(displ == lambda.envSize);
|
||||
nrFunctionCalls++;
|
||||
if (countCalls) incrFunctionCall(&lambda);
|
||||
|
||||
nrFunctionCalls++;
|
||||
if (countCalls) incrFunctionCall(&lambda);
|
||||
|
||||
/* Evaluate the body. */
|
||||
try {
|
||||
lambda.body->eval(*this, env2, vCur);
|
||||
} catch (Error & e) {
|
||||
if (loggerSettings.showTrace) {
|
||||
addErrorTrace(e, lambda.pos, "while evaluating %s",
|
||||
(lambda.name.set()
|
||||
? "'" + (string) lambda.name + "'"
|
||||
: "anonymous lambda"));
|
||||
addErrorTrace(e, pos, "from call site%s", "");
|
||||
}
|
||||
throw;
|
||||
}
|
||||
};
|
||||
|
||||
while (nrArgs > 0) {
|
||||
|
||||
if (vCur.isLambda()) {
|
||||
|
||||
ExprLambda & lambda(*vCur.lambda.fun);
|
||||
|
||||
if (nrArgs < lambda.args.size()) {
|
||||
vRes = vCur;
|
||||
for (size_t i = 0; i < nrArgs; ++i) {
|
||||
auto fun2 = allocValue();
|
||||
*fun2 = vRes;
|
||||
vRes.mkPartialApp(fun2, args[i]);
|
||||
/* Evaluate the body. */
|
||||
try {
|
||||
lambda.body->eval(*this, env2, vCur);
|
||||
} catch (Error & e) {
|
||||
if (loggerSettings.showTrace.get()) {
|
||||
addErrorTrace(e, lambda.pos, "while evaluating %s",
|
||||
(lambda.name.set()
|
||||
? "'" + (string) lambda.name + "'"
|
||||
: "anonymous lambda"));
|
||||
addErrorTrace(e, pos, "from call site%s", "");
|
||||
}
|
||||
return;
|
||||
} else {
|
||||
callLambda(vCur.lambda.env, lambda, args);
|
||||
nrArgs -= lambda.args.size();
|
||||
args += lambda.args.size();
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
else if (vCur.isPartialApp()) {
|
||||
/* Figure out the number of arguments still needed. */
|
||||
size_t argsDone = 0;
|
||||
Value * lambda = &vCur;
|
||||
while (lambda->isPartialApp()) {
|
||||
argsDone++;
|
||||
lambda = lambda->app.left;
|
||||
}
|
||||
assert(lambda->isLambda());
|
||||
auto arity = lambda->lambda.fun->args.size();
|
||||
auto argsLeft = arity - argsDone;
|
||||
|
||||
if (nrArgs < argsLeft) {
|
||||
/* We still don't have enough arguments, so extend the tPartialApp chain. */
|
||||
vRes = vCur;
|
||||
for (size_t i = 0; i < nrArgs; ++i) {
|
||||
auto fun2 = allocValue();
|
||||
*fun2 = vRes;
|
||||
vRes.mkPartialApp(fun2, args[i]);
|
||||
}
|
||||
return;
|
||||
} else {
|
||||
/* We have all the arguments, so call the function
|
||||
with the previous and new arguments. */
|
||||
|
||||
Value * vArgs[arity];
|
||||
auto n = argsDone;
|
||||
for (Value * arg = &vCur; arg->isPartialApp(); arg = arg->app.left)
|
||||
vArgs[--n] = arg->app.right;
|
||||
|
||||
for (size_t i = 0; i < argsLeft; ++i)
|
||||
vArgs[argsDone + i] = args[i];
|
||||
|
||||
nrArgs -= argsLeft;
|
||||
args += argsLeft;
|
||||
|
||||
callLambda(lambda->lambda.env, *lambda->lambda.fun, vArgs);
|
||||
}
|
||||
nrArgs--;
|
||||
args += 1;
|
||||
}
|
||||
|
||||
else if (vCur.isPrimOp()) {
|
||||
@@ -1458,16 +1456,16 @@ void EvalState::callFunction(Value & fun, size_t nrArgs, Value * * args, Value &
|
||||
}
|
||||
}
|
||||
|
||||
else if (vCur.type() == nAttrs) {
|
||||
if (auto functor = vCur.attrs->get(sFunctor)) {
|
||||
/* 'vCur" may be allocated on the stack of the calling
|
||||
function, but for functors we may keep a reference,
|
||||
so heap-allocate a copy and use that instead. */
|
||||
Value * args2[] = {allocValue()};
|
||||
*args2[0] = vCur;
|
||||
/* !!! Should we use the attr pos here? */
|
||||
callFunction(*functor->value, 1, args2, vCur, pos);
|
||||
}
|
||||
else if (vCur.type() == nAttrs && (functor = vCur.attrs->get(sFunctor))) {
|
||||
/* 'vCur' may be allocated on the stack of the calling
|
||||
function, but for functors we may keep a reference, so
|
||||
heap-allocate a copy and use that instead. */
|
||||
Value * args2[] = {allocValue(), args[0]};
|
||||
*args2[0] = vCur;
|
||||
/* !!! Should we use the attr pos here? */
|
||||
callFunction(*functor->value, 2, args2, vCur, pos);
|
||||
nrArgs--;
|
||||
args++;
|
||||
}
|
||||
|
||||
else
|
||||
@@ -1513,48 +1511,38 @@ void EvalState::autoCallFunction(Bindings & args, Value & fun, Value & res)
|
||||
}
|
||||
}
|
||||
|
||||
if (!fun.isLambda()) {
|
||||
if (!fun.isLambda() || !fun.lambda.fun->hasFormals()) {
|
||||
res = fun;
|
||||
return;
|
||||
}
|
||||
|
||||
Value * actualArgs[fun.lambda.fun->args.size()];
|
||||
auto attrs = buildBindings(std::max(static_cast<uint32_t>(fun.lambda.fun->formals->formals.size()), args.size()));
|
||||
|
||||
for (const auto & [i, arg] : enumerate(fun.lambda.fun->args)) {
|
||||
if (!arg.formals) {
|
||||
res = fun;
|
||||
return;
|
||||
}
|
||||
|
||||
actualArgs[i] = allocValue();
|
||||
mkAttrs(*actualArgs[i], std::max(arg.formals->formals.size(), static_cast<size_t>(args.size())));
|
||||
|
||||
if (arg.formals->ellipsis) {
|
||||
/* If the formals have an ellipsis (i.e. the function
|
||||
accepts extra args), pass all available automatic
|
||||
arguments. */
|
||||
for (auto & v : args)
|
||||
actualArgs[i]->attrs->push_back(v);
|
||||
} else {
|
||||
/* Otherwise, only pass the arguments that the function
|
||||
accepts. */
|
||||
for (auto & j : arg.formals->formals) {
|
||||
if (auto attr = args.get(j.name))
|
||||
actualArgs[i]->attrs->push_back(*attr);
|
||||
else if (!j.def)
|
||||
throwMissingArgumentError(j.pos, R"(cannot evaluate a function that has an argument without a value ('%1%')
|
||||
if (fun.lambda.fun->formals->ellipsis) {
|
||||
// If the formals have an ellipsis (eg the function accepts extra args) pass
|
||||
// all available automatic arguments (which includes arguments specified on
|
||||
// the command line via --arg/--argstr)
|
||||
for (auto & v : args)
|
||||
attrs.insert(v);
|
||||
} else {
|
||||
// Otherwise, only pass the arguments that the function accepts
|
||||
for (auto & i : fun.lambda.fun->formals->formals) {
|
||||
Bindings::iterator j = args.find(i.name);
|
||||
if (j != args.end()) {
|
||||
attrs.insert(*j);
|
||||
} else if (!i.def) {
|
||||
throwMissingArgumentError(i.pos, R"(cannot evaluate a function that has an argument without a value ('%1%')
|
||||
|
||||
Nix attempted to evaluate a function as a top level expression; in
|
||||
this case it must have its arguments supplied either by default
|
||||
values, or passed explicitly with '--arg' or '--argstr'. See
|
||||
https://nixos.org/manual/nix/stable/#ss-functions.)", j.name);
|
||||
https://nixos.org/manual/nix/stable/#ss-functions.)", i.name);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
actualArgs[i]->attrs->sort();
|
||||
}
|
||||
|
||||
callFunction(fun, fun.lambda.fun->args.size(), actualArgs, res, noPos);
|
||||
callFunction(fun, allocValue()->mkAttrs(attrs), res, noPos);
|
||||
}
|
||||
|
||||
|
||||
@@ -1589,7 +1577,7 @@ void ExprAssert::eval(EvalState & state, Env & env, Value & v)
|
||||
|
||||
void ExprOpNot::eval(EvalState & state, Env & env, Value & v)
|
||||
{
|
||||
mkBool(v, !state.evalBool(env, e));
|
||||
v.mkBool(!state.evalBool(env, e));
|
||||
}
|
||||
|
||||
|
||||
@@ -1597,7 +1585,7 @@ void ExprOpEq::eval(EvalState & state, Env & env, Value & v)
|
||||
{
|
||||
Value v1; e1->eval(state, env, v1);
|
||||
Value v2; e2->eval(state, env, v2);
|
||||
mkBool(v, state.eqValues(v1, v2));
|
||||
v.mkBool(state.eqValues(v1, v2));
|
||||
}
|
||||
|
||||
|
||||
@@ -1605,25 +1593,25 @@ void ExprOpNEq::eval(EvalState & state, Env & env, Value & v)
|
||||
{
|
||||
Value v1; e1->eval(state, env, v1);
|
||||
Value v2; e2->eval(state, env, v2);
|
||||
mkBool(v, !state.eqValues(v1, v2));
|
||||
v.mkBool(!state.eqValues(v1, v2));
|
||||
}
|
||||
|
||||
|
||||
void ExprOpAnd::eval(EvalState & state, Env & env, Value & v)
|
||||
{
|
||||
mkBool(v, state.evalBool(env, e1, pos) && state.evalBool(env, e2, pos));
|
||||
v.mkBool(state.evalBool(env, e1, pos) && state.evalBool(env, e2, pos));
|
||||
}
|
||||
|
||||
|
||||
void ExprOpOr::eval(EvalState & state, Env & env, Value & v)
|
||||
{
|
||||
mkBool(v, state.evalBool(env, e1, pos) || state.evalBool(env, e2, pos));
|
||||
v.mkBool(state.evalBool(env, e1, pos) || state.evalBool(env, e2, pos));
|
||||
}
|
||||
|
||||
|
||||
void ExprOpImpl::eval(EvalState & state, Env & env, Value & v)
|
||||
{
|
||||
mkBool(v, !state.evalBool(env, e1, pos) || state.evalBool(env, e2, pos));
|
||||
v.mkBool(!state.evalBool(env, e1, pos) || state.evalBool(env, e2, pos));
|
||||
}
|
||||
|
||||
|
||||
@@ -1638,7 +1626,7 @@ void ExprOpUpdate::eval(EvalState & state, Env & env, Value & v)
|
||||
if (v1.attrs->size() == 0) { v = v2; return; }
|
||||
if (v2.attrs->size() == 0) { v = v1; return; }
|
||||
|
||||
state.mkAttrs(v, v1.attrs->size() + v2.attrs->size());
|
||||
auto attrs = state.buildBindings(v1.attrs->size() + v2.attrs->size());
|
||||
|
||||
/* Merge the sets, preferring values from the second set. Make
|
||||
sure to keep the resulting vector in sorted order. */
|
||||
@@ -1647,17 +1635,19 @@ void ExprOpUpdate::eval(EvalState & state, Env & env, Value & v)
|
||||
|
||||
while (i != v1.attrs->end() && j != v2.attrs->end()) {
|
||||
if (i->name == j->name) {
|
||||
v.attrs->push_back(*j);
|
||||
attrs.insert(*j);
|
||||
++i; ++j;
|
||||
}
|
||||
else if (i->name < j->name)
|
||||
v.attrs->push_back(*i++);
|
||||
attrs.insert(*i++);
|
||||
else
|
||||
v.attrs->push_back(*j++);
|
||||
attrs.insert(*j++);
|
||||
}
|
||||
|
||||
while (i != v1.attrs->end()) v.attrs->push_back(*i++);
|
||||
while (j != v2.attrs->end()) v.attrs->push_back(*j++);
|
||||
while (i != v1.attrs->end()) attrs.insert(*i++);
|
||||
while (j != v2.attrs->end()) attrs.insert(*j++);
|
||||
|
||||
v.mkAttrs(attrs.alreadySorted());
|
||||
|
||||
state.nrOpUpdateValuesCopied += v.attrs->size();
|
||||
}
|
||||
@@ -1704,14 +1694,35 @@ void EvalState::concatLists(Value & v, size_t nrLists, Value * * lists, const Po
|
||||
void ExprConcatStrings::eval(EvalState & state, Env & env, Value & v)
|
||||
{
|
||||
PathSet context;
|
||||
std::ostringstream s;
|
||||
std::vector<std::string> s;
|
||||
size_t sSize = 0;
|
||||
NixInt n = 0;
|
||||
NixFloat nf = 0;
|
||||
|
||||
bool first = !forceString;
|
||||
ValueType firstType = nString;
|
||||
|
||||
for (auto & i : *es) {
|
||||
const auto str = [&] {
|
||||
std::string result;
|
||||
result.reserve(sSize);
|
||||
for (const auto & part : s) result += part;
|
||||
return result;
|
||||
};
|
||||
/* c_str() is not str().c_str() because we want to create a string
|
||||
Value. allocating a GC'd string directly and moving it into a
|
||||
Value lets us avoid an allocation and copy. */
|
||||
const auto c_str = [&] {
|
||||
char * result = allocString(sSize + 1);
|
||||
char * tmp = result;
|
||||
for (const auto & part : s) {
|
||||
memcpy(tmp, part.c_str(), part.size());
|
||||
tmp += part.size();
|
||||
}
|
||||
*tmp = 0;
|
||||
return result;
|
||||
};
|
||||
|
||||
for (auto & [i_pos, i] : *es) {
|
||||
Value vTmp;
|
||||
i->eval(state, env, vTmp);
|
||||
|
||||
@@ -1732,34 +1743,37 @@ void ExprConcatStrings::eval(EvalState & state, Env & env, Value & v)
|
||||
nf = n;
|
||||
nf += vTmp.fpoint;
|
||||
} else
|
||||
throwEvalError(pos, "cannot add %1% to an integer", showType(vTmp));
|
||||
throwEvalError(i_pos, "cannot add %1% to an integer", showType(vTmp));
|
||||
} else if (firstType == nFloat) {
|
||||
if (vTmp.type() == nInt) {
|
||||
nf += vTmp.integer;
|
||||
} else if (vTmp.type() == nFloat) {
|
||||
nf += vTmp.fpoint;
|
||||
} else
|
||||
throwEvalError(pos, "cannot add %1% to a float", showType(vTmp));
|
||||
} else
|
||||
throwEvalError(i_pos, "cannot add %1% to a float", showType(vTmp));
|
||||
} else {
|
||||
if (s.empty()) s.reserve(es->size());
|
||||
/* skip canonization of first path, which would only be not
|
||||
canonized in the first place if it's coming from a ./${foo} type
|
||||
path */
|
||||
s << state.coerceToString(pos, vTmp, context, false, firstType == nString, !first);
|
||||
s.emplace_back(
|
||||
state.coerceToString(i_pos, vTmp, context, false, firstType == nString, !first));
|
||||
sSize += s.back().size();
|
||||
}
|
||||
|
||||
first = false;
|
||||
}
|
||||
|
||||
if (firstType == nInt)
|
||||
mkInt(v, n);
|
||||
v.mkInt(n);
|
||||
else if (firstType == nFloat)
|
||||
mkFloat(v, nf);
|
||||
v.mkFloat(nf);
|
||||
else if (firstType == nPath) {
|
||||
if (!context.empty())
|
||||
throwEvalError(pos, "a string that refers to a store path cannot be appended to a path");
|
||||
auto path = canonPath(s.str());
|
||||
mkPath(v, path.c_str());
|
||||
v.mkPath(canonPath(str()));
|
||||
} else
|
||||
mkString(v, s.str(), context);
|
||||
v.mkStringMove(c_str(), context);
|
||||
}
|
||||
|
||||
|
||||
@@ -1791,8 +1805,8 @@ void EvalState::forceValueDeep(Value & v)
|
||||
}
|
||||
|
||||
else if (v.isList()) {
|
||||
for (size_t n = 0; n < v.listSize(); ++n)
|
||||
recurse(*v.listElems()[n]);
|
||||
for (auto v2 : v.listItems())
|
||||
recurse(*v2);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -1976,12 +1990,12 @@ string EvalState::coerceToString(const Pos & pos, Value & v, PathSet & context,
|
||||
|
||||
if (v.isList()) {
|
||||
string result;
|
||||
for (size_t n = 0; n < v.listSize(); ++n) {
|
||||
result += coerceToString(pos, *v.listElems()[n],
|
||||
for (auto [n, v2] : enumerate(v.listItems())) {
|
||||
result += coerceToString(pos, *v2,
|
||||
context, coerceMore, copyToStore);
|
||||
if (n < v.listSize() - 1
|
||||
/* !!! not quite correct */
|
||||
&& (!v.listElems()[n]->isList() || v.listElems()[n]->listSize() != 0))
|
||||
&& (!v2->isList() || v2->listSize() != 0))
|
||||
result += " ";
|
||||
}
|
||||
return result;
|
||||
|
||||
@@ -44,8 +44,6 @@ struct Env
|
||||
};
|
||||
|
||||
|
||||
Value & mkString(Value & v, std::string_view s, const PathSet & context = PathSet());
|
||||
|
||||
void copyContext(const Value & v, PathSet & context);
|
||||
|
||||
|
||||
@@ -82,7 +80,8 @@ public:
|
||||
sContentAddressed,
|
||||
sOutputHash, sOutputHashAlgo, sOutputHashMode,
|
||||
sRecurseForDerivations,
|
||||
sDescription, sSelf, sEpsilon;
|
||||
sDescription, sSelf, sEpsilon, sStartSet, sOperator, sKey, sPath,
|
||||
sPrefix;
|
||||
Symbol sDerivationNix;
|
||||
|
||||
/* If set, force copying files to the Nix store even if they
|
||||
@@ -93,7 +92,7 @@ public:
|
||||
mode. */
|
||||
std::optional<PathSet> allowedPaths;
|
||||
|
||||
Value vEmptySet;
|
||||
Bindings emptyBindings;
|
||||
|
||||
/* Store used to materialise .drv files. */
|
||||
const ref<Store> store;
|
||||
@@ -133,6 +132,9 @@ private:
|
||||
/* Cache used by prim_match(). */
|
||||
std::shared_ptr<RegexCache> regexCache;
|
||||
|
||||
/* Allocation cache for GC'd Value objects. */
|
||||
std::shared_ptr<void *> valueAllocCache;
|
||||
|
||||
public:
|
||||
|
||||
EvalState(
|
||||
@@ -178,8 +180,8 @@ public:
|
||||
Expr * parseExprFromFile(const Path & path, StaticEnv & staticEnv);
|
||||
|
||||
/* Parse a Nix expression from the specified string. */
|
||||
Expr * parseExprFromString(std::string_view s, const Path & basePath, StaticEnv & staticEnv);
|
||||
Expr * parseExprFromString(std::string_view s, const Path & basePath);
|
||||
Expr * parseExprFromString(std::string s, const Path & basePath, StaticEnv & staticEnv);
|
||||
Expr * parseExprFromString(std::string s, const Path & basePath);
|
||||
|
||||
Expr * parseStdin();
|
||||
|
||||
@@ -307,7 +309,7 @@ private:
|
||||
friend struct ExprAttrs;
|
||||
friend struct ExprLet;
|
||||
|
||||
Expr * parse(const char * text, FileOrigin origin, const Path & path,
|
||||
Expr * parse(char * text, size_t length, FileOrigin origin, const Path & path,
|
||||
const Path & basePath, StaticEnv & staticEnv);
|
||||
|
||||
public:
|
||||
@@ -336,12 +338,16 @@ public:
|
||||
Env & allocEnv(size_t size);
|
||||
|
||||
Value * allocAttr(Value & vAttrs, const Symbol & name);
|
||||
Value * allocAttr(Value & vAttrs, const std::string & name);
|
||||
Value * allocAttr(Value & vAttrs, std::string_view name);
|
||||
|
||||
Bindings * allocBindings(size_t capacity);
|
||||
|
||||
BindingsBuilder buildBindings(size_t capacity)
|
||||
{
|
||||
return BindingsBuilder(*this, allocBindings(capacity));
|
||||
}
|
||||
|
||||
void mkList(Value & v, size_t length);
|
||||
void mkAttrs(Value & v, size_t capacity);
|
||||
void mkThunk_(Value & v, Expr * expr);
|
||||
void mkPos(Value & v, ptr<Pos> pos);
|
||||
|
||||
@@ -350,7 +356,10 @@ public:
|
||||
/* Print statistics. */
|
||||
void printStats();
|
||||
|
||||
void realiseContext(const PathSet & context);
|
||||
/* Realise the given context, and return a mapping from the placeholders
|
||||
* used to construct the associated value to their final store path
|
||||
*/
|
||||
[[nodiscard]] StringMap realiseContext(const PathSet & context);
|
||||
|
||||
private:
|
||||
|
||||
@@ -391,6 +400,9 @@ private:
|
||||
friend struct ExprSelect;
|
||||
friend void prim_getAttr(EvalState & state, const Pos & pos, Value * * args, Value & v);
|
||||
friend void prim_match(EvalState & state, const Pos & pos, Value * * args, Value & v);
|
||||
friend void prim_split(EvalState & state, const Pos & pos, Value * * args, Value & v);
|
||||
|
||||
friend struct Value;
|
||||
};
|
||||
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
#include "flake.hh"
|
||||
#include "globals.hh"
|
||||
|
||||
#include <nlohmann/json.hpp>
|
||||
|
||||
@@ -37,11 +38,11 @@ void ConfigFile::apply()
|
||||
|
||||
// FIXME: Move into libutil/config.cc.
|
||||
std::string valueS;
|
||||
if (auto s = std::get_if<std::string>(&value))
|
||||
if (auto* s = std::get_if<std::string>(&value))
|
||||
valueS = *s;
|
||||
else if (auto n = std::get_if<int64_t>(&value))
|
||||
valueS = fmt("%d", n);
|
||||
else if (auto b = std::get_if<Explicit<bool>>(&value))
|
||||
else if (auto* n = std::get_if<int64_t>(&value))
|
||||
valueS = fmt("%d", *n);
|
||||
else if (auto* b = std::get_if<Explicit<bool>>(&value))
|
||||
valueS = b->t ? "true" : "false";
|
||||
else if (auto ss = std::get_if<std::vector<std::string>>(&value))
|
||||
valueS = concatStringsSep(" ", *ss); // FIXME: evil
|
||||
@@ -52,21 +53,19 @@ void ConfigFile::apply()
|
||||
auto trustedList = readTrustedList();
|
||||
|
||||
bool trusted = false;
|
||||
|
||||
if (auto saved = get(get(trustedList, name).value_or(std::map<std::string, bool>()), valueS)) {
|
||||
if (nix::settings.acceptFlakeConfig){
|
||||
trusted = true;
|
||||
} else if (auto saved = get(get(trustedList, name).value_or(std::map<std::string, bool>()), valueS)) {
|
||||
trusted = *saved;
|
||||
warn("Using saved setting for '%s = %s' from ~/.local/share/nix/trusted-settings.json.", name,valueS);
|
||||
} else {
|
||||
// FIXME: filter ANSI escapes, newlines, \r, etc.
|
||||
if (std::tolower(logger->ask(fmt("do you want to allow configuration setting '%s' to be set to '" ANSI_RED "%s" ANSI_NORMAL "' (y/N)?", name, valueS)).value_or('n')) != 'y') {
|
||||
if (std::tolower(logger->ask("do you want to permanently mark this value as untrusted (y/N)?").value_or('n')) == 'y') {
|
||||
trustedList[name][valueS] = false;
|
||||
writeTrustedList(trustedList);
|
||||
}
|
||||
} else {
|
||||
if (std::tolower(logger->ask("do you want to permanently mark this value as trusted (y/N)?").value_or('n')) == 'y') {
|
||||
trustedList[name][valueS] = trusted = true;
|
||||
writeTrustedList(trustedList);
|
||||
}
|
||||
if (std::tolower(logger->ask(fmt("do you want to allow configuration setting '%s' to be set to '" ANSI_RED "%s" ANSI_NORMAL "' (y/N)?", name, valueS)).value_or('n')) == 'y') {
|
||||
trusted = true;
|
||||
}
|
||||
if (std::tolower(logger->ask(fmt("do you want to permanently mark this value as %s (y/N)?", trusted ? "trusted": "untrusted" )).value_or('n')) == 'y') {
|
||||
trustedList[name][valueS] = trusted;
|
||||
writeTrustedList(trustedList);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -155,7 +155,7 @@ static FlakeInput parseFlakeInput(EvalState & state,
|
||||
if (!attrs.empty())
|
||||
throw Error("unexpected flake input attribute '%s', at %s", attrs.begin()->first, pos);
|
||||
if (url)
|
||||
input.ref = parseFlakeRef(*url, baseDir, true);
|
||||
input.ref = parseFlakeRef(*url, baseDir, true, input.isFlake);
|
||||
}
|
||||
|
||||
if (!input.follows && !input.ref)
|
||||
@@ -194,8 +194,8 @@ static Flake getFlake(
|
||||
state, originalRef, allowLookup, flakeCache);
|
||||
|
||||
// Guard against symlink attacks.
|
||||
auto flakeDir = canonPath(sourceInfo.actualPath + "/" + lockedRef.subdir);
|
||||
auto flakeFile = canonPath(flakeDir + "/flake.nix");
|
||||
auto flakeDir = canonPath(sourceInfo.actualPath + "/" + lockedRef.subdir, true);
|
||||
auto flakeFile = canonPath(flakeDir + "/flake.nix", true);
|
||||
if (!isInDir(flakeFile, sourceInfo.actualPath))
|
||||
throw Error("'flake.nix' file of flake '%s' escapes from '%s'",
|
||||
lockedRef, state.store->printStorePath(sourceInfo.storePath));
|
||||
@@ -230,13 +230,8 @@ static Flake getFlake(
|
||||
if (auto outputs = vInfo.attrs->get(sOutputs)) {
|
||||
expectType(state, nFunction, *outputs->value, *outputs->pos);
|
||||
|
||||
if (outputs->value->lambda.fun->args.size() != 1)
|
||||
throw Error("the 'outputs' attribute of flake '%s' is not a unary function", lockedRef);
|
||||
|
||||
auto & arg = outputs->value->lambda.fun->args[0];
|
||||
|
||||
if (arg.formals) {
|
||||
for (auto & formal : arg.formals->formals) {
|
||||
if (outputs->value->isLambda() && outputs->value->lambda.fun->hasFormals()) {
|
||||
for (auto & formal : outputs->value->lambda.fun->formals->formals) {
|
||||
if (formal.name != state.sSelf)
|
||||
flake.inputs.emplace(formal.name, FlakeInput {
|
||||
.ref = parseFlakeRef(formal.name)
|
||||
@@ -256,14 +251,17 @@ static Flake getFlake(
|
||||
forceTrivialValue(state, *setting.value, *setting.pos);
|
||||
if (setting.value->type() == nString)
|
||||
flake.config.settings.insert({setting.name, state.forceStringNoCtx(*setting.value, *setting.pos)});
|
||||
else if (setting.value->type() == nPath) {
|
||||
PathSet emptyContext = {};
|
||||
flake.config.settings.insert({setting.name, state.coerceToString(*setting.pos, *setting.value, emptyContext, false, true, true)});
|
||||
}
|
||||
else if (setting.value->type() == nInt)
|
||||
flake.config.settings.insert({setting.name, state.forceInt(*setting.value, *setting.pos)});
|
||||
else if (setting.value->type() == nBool)
|
||||
flake.config.settings.insert({setting.name, state.forceBool(*setting.value, *setting.pos)});
|
||||
flake.config.settings.insert({setting.name, Explicit<bool> { state.forceBool(*setting.value, *setting.pos) }});
|
||||
else if (setting.value->type() == nList) {
|
||||
std::vector<std::string> ss;
|
||||
for (unsigned int n = 0; n < setting.value->listSize(); ++n) {
|
||||
auto elem = setting.value->listElems()[n];
|
||||
for (auto elem : setting.value->listItems()) {
|
||||
if (elem->type() != nString)
|
||||
throw TypeError("list element in flake configuration setting '%s' is %s while a string is expected",
|
||||
setting.name, showType(*setting.value));
|
||||
@@ -312,7 +310,7 @@ LockedFlake lockFlake(
|
||||
|
||||
if (lockFlags.applyNixConfig) {
|
||||
flake.config.apply();
|
||||
// FIXME: send new config to the daemon.
|
||||
state.store->setOptions();
|
||||
}
|
||||
|
||||
try {
|
||||
@@ -350,7 +348,8 @@ LockedFlake lockFlake(
|
||||
const InputPath & inputPathPrefix,
|
||||
std::shared_ptr<const Node> oldNode,
|
||||
const LockParent & parent,
|
||||
const Path & parentPath)>
|
||||
const Path & parentPath,
|
||||
bool trustLock)>
|
||||
computeLocks;
|
||||
|
||||
computeLocks = [&](
|
||||
@@ -359,7 +358,8 @@ LockedFlake lockFlake(
|
||||
const InputPath & inputPathPrefix,
|
||||
std::shared_ptr<const Node> oldNode,
|
||||
const LockParent & parent,
|
||||
const Path & parentPath)
|
||||
const Path & parentPath,
|
||||
bool trustLock)
|
||||
{
|
||||
debug("computing lock file node '%s'", printInputPath(inputPathPrefix));
|
||||
|
||||
@@ -451,22 +451,18 @@ LockedFlake lockFlake(
|
||||
update it. */
|
||||
auto lb = lockFlags.inputUpdates.lower_bound(inputPath);
|
||||
|
||||
auto hasChildUpdate =
|
||||
auto mustRefetch =
|
||||
lb != lockFlags.inputUpdates.end()
|
||||
&& lb->size() > inputPath.size()
|
||||
&& std::equal(inputPath.begin(), inputPath.end(), lb->begin());
|
||||
|
||||
if (hasChildUpdate) {
|
||||
auto inputFlake = getFlake(
|
||||
state, oldLock->lockedRef, false, flakeCache);
|
||||
computeLocks(inputFlake.inputs, childNode, inputPath, oldLock, parent, parentPath);
|
||||
} else {
|
||||
FlakeInputs fakeInputs;
|
||||
|
||||
if (!mustRefetch) {
|
||||
/* No need to fetch this flake, we can be
|
||||
lazy. However there may be new overrides on the
|
||||
inputs of this flake, so we need to check
|
||||
those. */
|
||||
FlakeInputs fakeInputs;
|
||||
|
||||
for (auto & i : oldLock->inputs) {
|
||||
if (auto lockedNode = std::get_if<0>(&i.second)) {
|
||||
fakeInputs.emplace(i.first, FlakeInput {
|
||||
@@ -474,15 +470,39 @@ LockedFlake lockFlake(
|
||||
.isFlake = (*lockedNode)->isFlake,
|
||||
});
|
||||
} else if (auto follows = std::get_if<1>(&i.second)) {
|
||||
if (! trustLock) {
|
||||
// It is possible that the flake has changed,
|
||||
// so we must confirm all the follows that are in the lockfile are also in the flake.
|
||||
auto overridePath(inputPath);
|
||||
overridePath.push_back(i.first);
|
||||
auto o = overrides.find(overridePath);
|
||||
// If the override disappeared, we have to refetch the flake,
|
||||
// since some of the inputs may not be present in the lockfile.
|
||||
if (o == overrides.end()) {
|
||||
mustRefetch = true;
|
||||
// There's no point populating the rest of the fake inputs,
|
||||
// since we'll refetch the flake anyways.
|
||||
break;
|
||||
}
|
||||
}
|
||||
fakeInputs.emplace(i.first, FlakeInput {
|
||||
.follows = *follows,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
computeLocks(fakeInputs, childNode, inputPath, oldLock, parent, parentPath);
|
||||
}
|
||||
|
||||
LockParent newParent {
|
||||
.path = inputPath,
|
||||
.absolute = true
|
||||
};
|
||||
|
||||
computeLocks(
|
||||
mustRefetch
|
||||
? getFlake(state, oldLock->lockedRef, false, flakeCache).inputs
|
||||
: fakeInputs,
|
||||
childNode, inputPath, oldLock, newParent, parentPath, !mustRefetch);
|
||||
|
||||
} else {
|
||||
/* We need to create a new lock file entry. So fetch
|
||||
this input. */
|
||||
@@ -538,7 +558,7 @@ LockedFlake lockFlake(
|
||||
? std::dynamic_pointer_cast<const Node>(oldLock)
|
||||
: LockFile::read(
|
||||
inputFlake.sourceInfo->actualPath + "/" + inputFlake.lockedRef.subdir + "/flake.lock").root,
|
||||
newParent, localPath);
|
||||
newParent, localPath, false);
|
||||
}
|
||||
|
||||
else {
|
||||
@@ -562,11 +582,11 @@ LockedFlake lockFlake(
|
||||
};
|
||||
|
||||
// Bring in the current ref for relative path resolution if we have it
|
||||
auto parentPath = canonPath(flake.sourceInfo->actualPath + "/" + flake.lockedRef.subdir);
|
||||
auto parentPath = canonPath(flake.sourceInfo->actualPath + "/" + flake.lockedRef.subdir, true);
|
||||
|
||||
computeLocks(
|
||||
flake.inputs, newLockFile.root, {},
|
||||
lockFlags.recreateLockFile ? nullptr : oldLockFile.root, parent, parentPath);
|
||||
lockFlags.recreateLockFile ? nullptr : oldLockFile.root, parent, parentPath, false);
|
||||
|
||||
for (auto & i : lockFlags.inputOverrides)
|
||||
if (!overridesUsed.count(i.first))
|
||||
@@ -613,12 +633,24 @@ LockedFlake lockFlake(
|
||||
|
||||
newLockFile.write(path);
|
||||
|
||||
std::optional<std::string> commitMessage = std::nullopt;
|
||||
if (lockFlags.commitLockFile) {
|
||||
std::string cm;
|
||||
|
||||
cm = settings.commitLockFileSummary.get();
|
||||
|
||||
if (cm == "") {
|
||||
cm = fmt("%s: %s", relPath, lockFileExists ? "Update" : "Add");
|
||||
}
|
||||
|
||||
cm += "\n\nFlake lock file updates:\n\n";
|
||||
cm += filterANSIEscapes(diff, true);
|
||||
commitMessage = cm;
|
||||
}
|
||||
|
||||
topRef.input.markChangedFile(
|
||||
(topRef.subdir == "" ? "" : topRef.subdir + "/") + "flake.lock",
|
||||
lockFlags.commitLockFile
|
||||
? std::optional<std::string>(fmt("%s: %s\n\nFlake lock file changes:\n\n%s",
|
||||
relPath, lockFileExists ? "Update" : "Add", filterANSIEscapes(diff, true)))
|
||||
: std::nullopt);
|
||||
commitMessage);
|
||||
|
||||
/* Rewriting the lockfile changed the top-level
|
||||
repo, so we should re-read it. FIXME: we could
|
||||
@@ -666,7 +698,7 @@ void callFlake(EvalState & state,
|
||||
auto vTmp1 = state.allocValue();
|
||||
auto vTmp2 = state.allocValue();
|
||||
|
||||
mkString(*vLocks, lockedFlake.lockFile.to_string());
|
||||
vLocks->mkString(lockedFlake.lockFile.to_string());
|
||||
|
||||
emitTreeAttrs(
|
||||
state,
|
||||
@@ -676,7 +708,7 @@ void callFlake(EvalState & state,
|
||||
false,
|
||||
lockedFlake.flake.forceDirty);
|
||||
|
||||
mkString(*vRootSubdir, lockedFlake.flake.lockedRef.subdir);
|
||||
vRootSubdir->mkString(lockedFlake.flake.lockedRef.subdir);
|
||||
|
||||
if (!state.vCallFlake) {
|
||||
state.vCallFlake = allocRootValue(state.allocValue());
|
||||
|
||||
@@ -48,9 +48,12 @@ FlakeRef FlakeRef::resolve(ref<Store> store) const
|
||||
}
|
||||
|
||||
FlakeRef parseFlakeRef(
|
||||
const std::string & url, const std::optional<Path> & baseDir, bool allowMissing)
|
||||
const std::string & url,
|
||||
const std::optional<Path> & baseDir,
|
||||
bool allowMissing,
|
||||
bool isFlake)
|
||||
{
|
||||
auto [flakeRef, fragment] = parseFlakeRefWithFragment(url, baseDir, allowMissing);
|
||||
auto [flakeRef, fragment] = parseFlakeRefWithFragment(url, baseDir, allowMissing, isFlake);
|
||||
if (fragment != "")
|
||||
throw Error("unexpected fragment '%s' in flake reference '%s'", fragment, url);
|
||||
return flakeRef;
|
||||
@@ -67,7 +70,10 @@ std::optional<FlakeRef> maybeParseFlakeRef(
|
||||
}
|
||||
|
||||
std::pair<FlakeRef, std::string> parseFlakeRefWithFragment(
|
||||
const std::string & url, const std::optional<Path> & baseDir, bool allowMissing)
|
||||
const std::string & url,
|
||||
const std::optional<Path> & baseDir,
|
||||
bool allowMissing,
|
||||
bool isFlake)
|
||||
{
|
||||
using namespace fetchers;
|
||||
|
||||
@@ -112,46 +118,71 @@ std::pair<FlakeRef, std::string> parseFlakeRefWithFragment(
|
||||
to 'baseDir'). If so, search upward to the root of the
|
||||
repo (i.e. the directory containing .git). */
|
||||
|
||||
path = absPath(path, baseDir, true);
|
||||
path = absPath(path, baseDir);
|
||||
|
||||
if (!S_ISDIR(lstat(path).st_mode))
|
||||
throw BadURL("path '%s' is not a flake (because it's not a directory)", path);
|
||||
if (isFlake) {
|
||||
|
||||
if (!allowMissing && !pathExists(path + "/flake.nix"))
|
||||
throw BadURL("path '%s' is not a flake (because it doesn't contain a 'flake.nix' file)", path);
|
||||
if (!allowMissing && !pathExists(path + "/flake.nix")){
|
||||
notice("path '%s' does not contain a 'flake.nix', searching up",path);
|
||||
|
||||
auto flakeRoot = path;
|
||||
std::string subdir;
|
||||
|
||||
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,
|
||||
.query = decodeQuery(match[2]),
|
||||
};
|
||||
|
||||
if (subdir != "") {
|
||||
if (parsedURL.query.count("dir"))
|
||||
throw Error("flake URL '%s' has an inconsistent 'dir' parameter", url);
|
||||
parsedURL.query.insert_or_assign("dir", subdir);
|
||||
// Save device to detect filesystem boundary
|
||||
dev_t device = lstat(path).st_dev;
|
||||
bool found = false;
|
||||
while (path != "/") {
|
||||
if (pathExists(path + "/flake.nix")) {
|
||||
found = true;
|
||||
break;
|
||||
} else if (pathExists(path + "/.git"))
|
||||
throw Error("path '%s' is not part of a flake (neither it nor its parent directories contain a 'flake.nix' file)", path);
|
||||
else {
|
||||
if (lstat(path).st_dev != device)
|
||||
throw Error("unable to find a flake before encountering filesystem boundary at '%s'", path);
|
||||
}
|
||||
path = dirOf(path);
|
||||
}
|
||||
|
||||
if (pathExists(flakeRoot + "/.git/shallow"))
|
||||
parsedURL.query.insert_or_assign("shallow", "1");
|
||||
|
||||
return std::make_pair(
|
||||
FlakeRef(Input::fromURL(parsedURL), get(parsedURL.query, "dir").value_or("")),
|
||||
fragment);
|
||||
if (!found)
|
||||
throw BadURL("could not find a flake.nix file");
|
||||
}
|
||||
|
||||
subdir = std::string(baseNameOf(flakeRoot)) + (subdir.empty() ? "" : "/" + subdir);
|
||||
flakeRoot = dirOf(flakeRoot);
|
||||
if (!S_ISDIR(lstat(path).st_mode))
|
||||
throw BadURL("path '%s' is not a flake (because it's not a directory)", path);
|
||||
|
||||
if (!allowMissing && !pathExists(path + "/flake.nix"))
|
||||
throw BadURL("path '%s' is not a flake (because it doesn't contain a 'flake.nix' file)", path);
|
||||
|
||||
auto flakeRoot = path;
|
||||
std::string subdir;
|
||||
|
||||
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,
|
||||
.query = decodeQuery(match[2]),
|
||||
};
|
||||
|
||||
if (subdir != "") {
|
||||
if (parsedURL.query.count("dir"))
|
||||
throw Error("flake URL '%s' has an inconsistent 'dir' parameter", url);
|
||||
parsedURL.query.insert_or_assign("dir", subdir);
|
||||
}
|
||||
|
||||
if (pathExists(flakeRoot + "/.git/shallow"))
|
||||
parsedURL.query.insert_or_assign("shallow", "1");
|
||||
|
||||
return std::make_pair(
|
||||
FlakeRef(Input::fromURL(parsedURL), get(parsedURL.query, "dir").value_or("")),
|
||||
fragment);
|
||||
}
|
||||
|
||||
subdir = std::string(baseNameOf(flakeRoot)) + (subdir.empty() ? "" : "/" + subdir);
|
||||
flakeRoot = dirOf(flakeRoot);
|
||||
}
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
@@ -62,13 +62,19 @@ struct FlakeRef
|
||||
std::ostream & operator << (std::ostream & str, const FlakeRef & flakeRef);
|
||||
|
||||
FlakeRef parseFlakeRef(
|
||||
const std::string & url, const std::optional<Path> & baseDir = {}, bool allowMissing = false);
|
||||
const std::string & url,
|
||||
const std::optional<Path> & baseDir = {},
|
||||
bool allowMissing = false,
|
||||
bool isFlake = true);
|
||||
|
||||
std::optional<FlakeRef> maybeParseFlake(
|
||||
const std::string & url, const std::optional<Path> & baseDir = {});
|
||||
|
||||
std::pair<FlakeRef, std::string> parseFlakeRefWithFragment(
|
||||
const std::string & url, const std::optional<Path> & baseDir = {}, bool allowMissing = false);
|
||||
const std::string & url,
|
||||
const std::optional<Path> & baseDir = {},
|
||||
bool allowMissing = false,
|
||||
bool isFlake = true);
|
||||
|
||||
std::optional<std::pair<FlakeRef, std::string>> maybeParseFlakeRefWithFragment(
|
||||
const std::string & url, const std::optional<Path> & baseDir = {});
|
||||
|
||||
@@ -102,9 +102,9 @@ DrvInfo::Outputs DrvInfo::queryOutputs(bool onlyOutputsToInstall)
|
||||
state->forceList(*i->value, *i->pos);
|
||||
|
||||
/* For each output... */
|
||||
for (unsigned int j = 0; j < i->value->listSize(); ++j) {
|
||||
for (auto elem : i->value->listItems()) {
|
||||
/* Evaluate the corresponding set. */
|
||||
string name = state->forceStringNoCtx(*i->value->listElems()[j], *i->pos);
|
||||
string name = state->forceStringNoCtx(*elem, *i->pos);
|
||||
Bindings::iterator out = attrs->find(state->symbols.create(name));
|
||||
if (out == attrs->end()) continue; // FIXME: throw error?
|
||||
state->forceAttrs(*out->value);
|
||||
@@ -128,9 +128,9 @@ DrvInfo::Outputs DrvInfo::queryOutputs(bool onlyOutputsToInstall)
|
||||
/* ^ this shows during `nix-env -i` right under the bad derivation */
|
||||
if (!outTI->isList()) throw errMsg;
|
||||
Outputs result;
|
||||
for (auto i = outTI->listElems(); i != outTI->listElems() + outTI->listSize(); ++i) {
|
||||
if ((*i)->type() != nString) throw errMsg;
|
||||
auto out = outputs.find((*i)->string.s);
|
||||
for (auto elem : outTI->listItems()) {
|
||||
if (elem->type() != nString) throw errMsg;
|
||||
auto out = outputs.find(elem->string.s);
|
||||
if (out == outputs.end()) throw errMsg;
|
||||
result.insert(*out);
|
||||
}
|
||||
@@ -174,8 +174,8 @@ bool DrvInfo::checkMeta(Value & v)
|
||||
{
|
||||
state->forceValue(v);
|
||||
if (v.type() == nList) {
|
||||
for (unsigned int n = 0; n < v.listSize(); ++n)
|
||||
if (!checkMeta(*v.listElems()[n])) return false;
|
||||
for (auto elem : v.listItems())
|
||||
if (!checkMeta(*elem)) return false;
|
||||
return true;
|
||||
}
|
||||
else if (v.type() == nAttrs) {
|
||||
@@ -254,15 +254,14 @@ bool DrvInfo::queryMetaBool(const string & name, bool def)
|
||||
void DrvInfo::setMeta(const string & name, Value * v)
|
||||
{
|
||||
getMeta();
|
||||
Bindings * old = meta;
|
||||
meta = state->allocBindings(1 + (old ? old->size() : 0));
|
||||
auto attrs = state->buildBindings(1 + (meta ? meta->size() : 0));
|
||||
Symbol sym = state->symbols.create(name);
|
||||
if (old)
|
||||
for (auto i : *old)
|
||||
if (meta)
|
||||
for (auto i : *meta)
|
||||
if (i.name != sym)
|
||||
meta->push_back(i);
|
||||
if (v) meta->push_back(Attr(sym, v));
|
||||
meta->sort();
|
||||
attrs.insert(i);
|
||||
if (v) attrs.insert(sym, v);
|
||||
meta = attrs.finish();
|
||||
}
|
||||
|
||||
|
||||
@@ -364,10 +363,10 @@ static void getDerivations(EvalState & state, Value & vIn,
|
||||
}
|
||||
|
||||
else if (v.type() == nList) {
|
||||
for (unsigned int n = 0; n < v.listSize(); ++n) {
|
||||
string pathPrefix2 = addToPath(pathPrefix, (format("%1%") % n).str());
|
||||
if (getDerivation(state, *v.listElems()[n], pathPrefix2, drvs, done, ignoreAssertionFailures))
|
||||
getDerivations(state, *v.listElems()[n], pathPrefix2, autoArgs, drvs, done, ignoreAssertionFailures);
|
||||
for (auto [n, elem] : enumerate(v.listItems())) {
|
||||
string pathPrefix2 = addToPath(pathPrefix, fmt("%d", n));
|
||||
if (getDerivation(state, *elem, pathPrefix2, drvs, done, ignoreAssertionFailures))
|
||||
getDerivations(state, *elem, pathPrefix2, autoArgs, drvs, done, ignoreAssertionFailures);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -37,10 +37,10 @@ class JSONSax : nlohmann::json_sax<json> {
|
||||
ValueMap attrs;
|
||||
std::unique_ptr<JSONState> resolve(EvalState & state) override
|
||||
{
|
||||
Value & v = parent->value(state);
|
||||
state.mkAttrs(v, attrs.size());
|
||||
auto attrs2 = state.buildBindings(attrs.size());
|
||||
for (auto & i : attrs)
|
||||
v.attrs->push_back(Attr(i.first, i.second));
|
||||
attrs2.insert(i.first, i.second);
|
||||
parent->value(state).mkAttrs(attrs2.alreadySorted());
|
||||
return std::move(parent);
|
||||
}
|
||||
void add() override { v = nullptr; }
|
||||
@@ -76,45 +76,51 @@ class JSONSax : nlohmann::json_sax<json> {
|
||||
EvalState & state;
|
||||
std::unique_ptr<JSONState> rs;
|
||||
|
||||
template<typename T, typename... Args> inline bool handle_value(T f, Args... args)
|
||||
{
|
||||
f(rs->value(state), args...);
|
||||
rs->add();
|
||||
return true;
|
||||
}
|
||||
|
||||
public:
|
||||
JSONSax(EvalState & state, Value & v) : state(state), rs(new JSONState(&v)) {};
|
||||
|
||||
bool null()
|
||||
{
|
||||
return handle_value(mkNull);
|
||||
rs->value(state).mkNull();
|
||||
rs->add();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool boolean(bool val)
|
||||
{
|
||||
return handle_value(mkBool, val);
|
||||
rs->value(state).mkBool(val);
|
||||
rs->add();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool number_integer(number_integer_t val)
|
||||
{
|
||||
return handle_value(mkInt, val);
|
||||
rs->value(state).mkInt(val);
|
||||
rs->add();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool number_unsigned(number_unsigned_t val)
|
||||
{
|
||||
return handle_value(mkInt, val);
|
||||
rs->value(state).mkInt(val);
|
||||
rs->add();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool number_float(number_float_t val, const string_t & s)
|
||||
{
|
||||
return handle_value(mkFloat, val);
|
||||
rs->value(state).mkFloat(val);
|
||||
rs->add();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool string(string_t & val)
|
||||
{
|
||||
return handle_value<void(Value&, const char*)>(mkString, val.c_str());
|
||||
rs->value(state).mkString(val);
|
||||
rs->add();
|
||||
return true;
|
||||
}
|
||||
|
||||
#if NLOHMANN_JSON_VERSION_MAJOR >= 3 && NLOHMANN_JSON_VERSION_MINOR >= 8
|
||||
bool binary(binary_t&)
|
||||
{
|
||||
|
||||
@@ -64,29 +64,32 @@ static void adjustLoc(YYLTYPE * loc, const char * s, size_t len)
|
||||
}
|
||||
|
||||
|
||||
// FIXME: optimize
|
||||
static Expr * unescapeStr(SymbolTable & symbols, const char * s, size_t length)
|
||||
// we make use of the fact that the parser receives a private copy of the input
|
||||
// string and can munge around in it.
|
||||
static Expr * unescapeStr(SymbolTable & symbols, char * s, size_t length)
|
||||
{
|
||||
string t;
|
||||
t.reserve(length);
|
||||
char * result = s;
|
||||
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++)) {
|
||||
if (c == '\\') {
|
||||
assert(*s);
|
||||
c = *s++;
|
||||
if (c == 'n') t += '\n';
|
||||
else if (c == 'r') t += '\r';
|
||||
else if (c == 't') t += '\t';
|
||||
else t += c;
|
||||
if (c == 'n') *t = '\n';
|
||||
else if (c == 'r') *t = '\r';
|
||||
else if (c == 't') *t = '\t';
|
||||
else *t = c;
|
||||
}
|
||||
else if (c == '\r') {
|
||||
/* Normalise CR and CR/LF into LF. */
|
||||
t += '\n';
|
||||
*t = '\n';
|
||||
if (*s == '\n') s++; /* cr/lf */
|
||||
}
|
||||
else t += c;
|
||||
else *t = c;
|
||||
t++;
|
||||
}
|
||||
return new ExprString(symbols.create(t));
|
||||
return new ExprString(symbols.create({result, size_t(t - result)}));
|
||||
}
|
||||
|
||||
|
||||
@@ -139,7 +142,7 @@ or { return OR_KW; }
|
||||
\/\/ { return UPDATE; }
|
||||
\+\+ { return CONCAT; }
|
||||
|
||||
{ID} { yylval->id = strdup(yytext); return ID; }
|
||||
{ID} { yylval->id = {yytext, (size_t) yyleng}; return ID; }
|
||||
{INT} { errno = 0;
|
||||
try {
|
||||
yylval->n = boost::lexical_cast<int64_t>(yytext);
|
||||
@@ -221,14 +224,14 @@ or { return OR_KW; }
|
||||
<PATH_START>{PATH_SEG} {
|
||||
POP_STATE();
|
||||
PUSH_STATE(INPATH_SLASH);
|
||||
yylval->path = strdup(yytext);
|
||||
yylval->path = {yytext, (size_t) yyleng};
|
||||
return PATH;
|
||||
}
|
||||
|
||||
<PATH_START>{HPATH_START} {
|
||||
POP_STATE();
|
||||
PUSH_STATE(INPATH_SLASH);
|
||||
yylval->path = strdup(yytext);
|
||||
yylval->path = {yytext, (size_t) yyleng};
|
||||
return HPATH;
|
||||
}
|
||||
|
||||
@@ -237,7 +240,7 @@ or { return OR_KW; }
|
||||
PUSH_STATE(INPATH_SLASH);
|
||||
else
|
||||
PUSH_STATE(INPATH);
|
||||
yylval->path = strdup(yytext);
|
||||
yylval->path = {yytext, (size_t) yyleng};
|
||||
return PATH;
|
||||
}
|
||||
{HPATH} {
|
||||
@@ -245,7 +248,7 @@ or { return OR_KW; }
|
||||
PUSH_STATE(INPATH_SLASH);
|
||||
else
|
||||
PUSH_STATE(INPATH);
|
||||
yylval->path = strdup(yytext);
|
||||
yylval->path = {yytext, (size_t) yyleng};
|
||||
return HPATH;
|
||||
}
|
||||
|
||||
@@ -280,8 +283,8 @@ or { return OR_KW; }
|
||||
throw ParseError("path has a trailing slash");
|
||||
}
|
||||
|
||||
{SPATH} { yylval->path = strdup(yytext); return SPATH; }
|
||||
{URI} { yylval->uri = strdup(yytext); return URI; }
|
||||
{SPATH} { yylval->path = {yytext, (size_t) yyleng}; return SPATH; }
|
||||
{URI} { yylval->uri = {yytext, (size_t) yyleng}; return URI; }
|
||||
|
||||
[ \t\r\n]+ /* eat up whitespace */
|
||||
\#[^\r\n]* /* single-line comments */
|
||||
|
||||
@@ -124,26 +124,23 @@ void ExprList::show(std::ostream & str) const
|
||||
void ExprLambda::show(std::ostream & str) const
|
||||
{
|
||||
str << "(";
|
||||
for (auto & arg : args) {
|
||||
if (arg.formals) {
|
||||
str << "{ ";
|
||||
bool first = true;
|
||||
for (auto & i : arg.formals->formals) {
|
||||
if (first) first = false; else str << ", ";
|
||||
str << i.name;
|
||||
if (i.def) str << " ? " << *i.def;
|
||||
}
|
||||
if (arg.formals->ellipsis) {
|
||||
if (!first) str << ", ";
|
||||
str << "...";
|
||||
}
|
||||
str << " }";
|
||||
if (!arg.arg.empty()) str << " @ ";
|
||||
if (hasFormals()) {
|
||||
str << "{ ";
|
||||
bool first = true;
|
||||
for (auto & i : formals->formals) {
|
||||
if (first) first = false; else str << ", ";
|
||||
str << i.name;
|
||||
if (i.def) str << " ? " << *i.def;
|
||||
}
|
||||
if (!arg.arg.empty()) str << arg.arg;
|
||||
str << ": ";
|
||||
if (formals->ellipsis) {
|
||||
if (!first) str << ", ";
|
||||
str << "...";
|
||||
}
|
||||
str << " }";
|
||||
if (!arg.empty()) str << " @ ";
|
||||
}
|
||||
str << *body << ")";
|
||||
if (!arg.empty()) str << arg;
|
||||
str << ": " << *body << ")";
|
||||
}
|
||||
|
||||
void ExprCall::show(std::ostream & str) const
|
||||
@@ -194,7 +191,7 @@ void ExprConcatStrings::show(std::ostream & str) const
|
||||
str << "(";
|
||||
for (auto & i : *es) {
|
||||
if (first) first = false; else str << " + ";
|
||||
str << *i;
|
||||
str << *i.second;
|
||||
}
|
||||
str << ")";
|
||||
}
|
||||
@@ -282,7 +279,8 @@ void ExprVar::bindVars(const StaticEnv & env)
|
||||
if (curEnv->isWith) {
|
||||
if (withLevel == -1) withLevel = level;
|
||||
} else {
|
||||
if (auto i = curEnv->get(name)) {
|
||||
auto i = curEnv->find(name);
|
||||
if (i != curEnv->vars.end()) {
|
||||
fromWith = false;
|
||||
this->level = level;
|
||||
displ = i->second;
|
||||
@@ -356,48 +354,25 @@ void ExprList::bindVars(const StaticEnv & env)
|
||||
|
||||
void ExprLambda::bindVars(const StaticEnv & env)
|
||||
{
|
||||
/* The parser adds arguments in reverse order. Let's fix that
|
||||
now. */
|
||||
std::reverse(args.begin(), args.end());
|
||||
|
||||
envSize = 0;
|
||||
|
||||
for (auto & arg :args) {
|
||||
if (!arg.arg.empty()) envSize++;
|
||||
if (arg.formals) envSize += arg.formals->formals.size();
|
||||
}
|
||||
|
||||
StaticEnv newEnv(false, &env, envSize);
|
||||
StaticEnv newEnv(
|
||||
false, &env,
|
||||
(hasFormals() ? formals->formals.size() : 0) +
|
||||
(arg.empty() ? 0 : 1));
|
||||
|
||||
Displacement displ = 0;
|
||||
|
||||
for (auto & arg : args) {
|
||||
if (!arg.arg.empty()) {
|
||||
if (auto i = const_cast<StaticEnv::Vars::value_type *>(newEnv.get(arg.arg)))
|
||||
i->second = displ++;
|
||||
else
|
||||
newEnv.vars.emplace_back(arg.arg, displ++);
|
||||
}
|
||||
if (!arg.empty()) newEnv.vars.emplace_back(arg, displ++);
|
||||
|
||||
if (arg.formals) {
|
||||
for (auto & i : arg.formals->formals) {
|
||||
if (auto j = const_cast<StaticEnv::Vars::value_type *>(newEnv.get(i.name)))
|
||||
j->second = displ++;
|
||||
else
|
||||
newEnv.vars.emplace_back(i.name, displ++);
|
||||
}
|
||||
if (hasFormals()) {
|
||||
for (auto & i : formals->formals)
|
||||
newEnv.vars.emplace_back(i.name, displ++);
|
||||
|
||||
newEnv.sort();
|
||||
newEnv.sort();
|
||||
|
||||
for (auto & i : arg.formals->formals)
|
||||
if (i.def) i.def->bindVars(newEnv);
|
||||
}
|
||||
for (auto & i : formals->formals)
|
||||
if (i.def) i.def->bindVars(newEnv);
|
||||
}
|
||||
|
||||
assert(displ == envSize);
|
||||
|
||||
newEnv.sort();
|
||||
|
||||
body->bindVars(newEnv);
|
||||
}
|
||||
|
||||
@@ -464,7 +439,7 @@ void ExprOpNot::bindVars(const StaticEnv & env)
|
||||
void ExprConcatStrings::bindVars(const StaticEnv & env)
|
||||
{
|
||||
for (auto & i : *es)
|
||||
i->bindVars(env);
|
||||
i.second->bindVars(env);
|
||||
}
|
||||
|
||||
void ExprPos::bindVars(const StaticEnv & env)
|
||||
@@ -498,7 +473,7 @@ string ExprLambda::showNamePos() const
|
||||
size_t SymbolTable::totalSize() const
|
||||
{
|
||||
size_t n = 0;
|
||||
for (auto & i : symbols)
|
||||
for (auto & i : store)
|
||||
n += i.size();
|
||||
return n;
|
||||
}
|
||||
|
||||
@@ -94,7 +94,7 @@ struct ExprInt : Expr
|
||||
{
|
||||
NixInt n;
|
||||
Value v;
|
||||
ExprInt(NixInt n) : n(n) { mkInt(v, n); };
|
||||
ExprInt(NixInt n) : n(n) { v.mkInt(n); };
|
||||
COMMON_METHODS
|
||||
Value * maybeThunk(EvalState & state, Env & env);
|
||||
};
|
||||
@@ -103,7 +103,7 @@ struct ExprFloat : Expr
|
||||
{
|
||||
NixFloat nf;
|
||||
Value v;
|
||||
ExprFloat(NixFloat nf) : nf(nf) { mkFloat(v, nf); };
|
||||
ExprFloat(NixFloat nf) : nf(nf) { v.mkFloat(nf); };
|
||||
COMMON_METHODS
|
||||
Value * maybeThunk(EvalState & state, Env & env);
|
||||
};
|
||||
@@ -112,7 +112,7 @@ struct ExprString : Expr
|
||||
{
|
||||
Symbol s;
|
||||
Value v;
|
||||
ExprString(const Symbol & s) : s(s) { mkString(v, s); };
|
||||
ExprString(const Symbol & s) : s(s) { v.mkString(s); };
|
||||
COMMON_METHODS
|
||||
Value * maybeThunk(EvalState & state, Env & env);
|
||||
};
|
||||
@@ -233,24 +233,21 @@ struct ExprLambda : Expr
|
||||
{
|
||||
Pos pos;
|
||||
Symbol name;
|
||||
|
||||
struct Arg
|
||||
{
|
||||
Symbol arg;
|
||||
Formals * formals;
|
||||
};
|
||||
|
||||
std::vector<Arg> args;
|
||||
|
||||
Symbol arg;
|
||||
Formals * formals;
|
||||
Expr * body;
|
||||
|
||||
Displacement envSize = 0; // initialized by bindVars()
|
||||
|
||||
ExprLambda(const Pos & pos, Expr * body)
|
||||
: pos(pos), body(body)
|
||||
{ };
|
||||
ExprLambda(const Pos & pos, const Symbol & arg, Formals * formals, Expr * body)
|
||||
: pos(pos), arg(arg), formals(formals), body(body)
|
||||
{
|
||||
if (!arg.empty() && formals && formals->argNames.find(arg) != formals->argNames.end())
|
||||
throw ParseError({
|
||||
.msg = hintfmt("duplicate formal function argument '%1%'", arg),
|
||||
.errPos = pos
|
||||
});
|
||||
};
|
||||
void setName(Symbol & name);
|
||||
string showNamePos() const;
|
||||
inline bool hasFormals() const { return formals != nullptr; }
|
||||
COMMON_METHODS
|
||||
};
|
||||
|
||||
@@ -335,8 +332,8 @@ struct ExprConcatStrings : Expr
|
||||
{
|
||||
Pos pos;
|
||||
bool forceString;
|
||||
vector<Expr *> * es;
|
||||
ExprConcatStrings(const Pos & pos, bool forceString, vector<Expr *> * es)
|
||||
vector<std::pair<Pos, Expr *> > * es;
|
||||
ExprConcatStrings(const Pos & pos, bool forceString, vector<std::pair<Pos, Expr *> > * es)
|
||||
: pos(pos), forceString(forceString), es(es) { };
|
||||
COMMON_METHODS
|
||||
};
|
||||
@@ -367,16 +364,27 @@ struct StaticEnv
|
||||
|
||||
void sort()
|
||||
{
|
||||
std::sort(vars.begin(), vars.end(),
|
||||
std::stable_sort(vars.begin(), vars.end(),
|
||||
[](const Vars::value_type & a, const Vars::value_type & b) { return a.first < b.first; });
|
||||
}
|
||||
|
||||
const Vars::value_type * get(const Symbol & name) const
|
||||
void deduplicate()
|
||||
{
|
||||
auto it = vars.begin(), jt = it, end = vars.end();
|
||||
while (jt != end) {
|
||||
*it = *jt++;
|
||||
while (jt != end && it->first == jt->first) *it = *jt++;
|
||||
it++;
|
||||
}
|
||||
vars.erase(it, end);
|
||||
}
|
||||
|
||||
Vars::const_iterator find(const Symbol & name) const
|
||||
{
|
||||
Vars::value_type key(name, 0);
|
||||
auto i = std::lower_bound(vars.begin(), vars.end(), key);
|
||||
if (i != vars.end() && i->first == name) return &*i;
|
||||
return {};
|
||||
if (i != vars.end() && i->first == name) return i;
|
||||
return vars.end();
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -33,11 +33,9 @@ namespace nix {
|
||||
Symbol file;
|
||||
FileOrigin origin;
|
||||
std::optional<ErrorInfo> error;
|
||||
Symbol sLetBody;
|
||||
ParseData(EvalState & state)
|
||||
: state(state)
|
||||
, symbols(state.symbols)
|
||||
, sLetBody(symbols.create("<let-body>"))
|
||||
{ };
|
||||
};
|
||||
|
||||
@@ -154,25 +152,7 @@ static void addFormal(const Pos & pos, Formals * formals, const Formal & formal)
|
||||
}
|
||||
|
||||
|
||||
static Expr * addArg(const Pos & pos, Expr * e, ExprLambda::Arg && arg)
|
||||
{
|
||||
if (!arg.arg.empty() && arg.formals && arg.formals->argNames.count(arg.arg))
|
||||
throw ParseError({
|
||||
.msg = hintfmt("duplicate formal function argument '%1%'", arg.arg),
|
||||
.errPos = pos
|
||||
});
|
||||
|
||||
auto e2 = dynamic_cast<ExprLambda *>(e); // FIXME: slow?
|
||||
if (!e2)
|
||||
e2 = new ExprLambda(pos, e);
|
||||
else
|
||||
e2->pos = pos;
|
||||
e2->args.emplace_back(std::move(arg));
|
||||
return e2;
|
||||
}
|
||||
|
||||
|
||||
static Expr * stripIndentation(const Pos & pos, SymbolTable & symbols, vector<Expr *> & es)
|
||||
static Expr * stripIndentation(const Pos & pos, SymbolTable & symbols, vector<std::pair<Pos, Expr *> > & es)
|
||||
{
|
||||
if (es.empty()) return new ExprString(symbols.create(""));
|
||||
|
||||
@@ -182,7 +162,7 @@ static Expr * stripIndentation(const Pos & pos, SymbolTable & symbols, vector<Ex
|
||||
bool atStartOfLine = true; /* = seen only whitespace in the current line */
|
||||
size_t minIndent = 1000000;
|
||||
size_t curIndent = 0;
|
||||
for (auto & i : es) {
|
||||
for (auto & [i_pos, i] : es) {
|
||||
ExprIndStr * e = dynamic_cast<ExprIndStr *>(i);
|
||||
if (!e) {
|
||||
/* Anti-quotations end the current start-of-line whitespace. */
|
||||
@@ -212,12 +192,12 @@ static Expr * stripIndentation(const Pos & pos, SymbolTable & symbols, vector<Ex
|
||||
}
|
||||
|
||||
/* Strip spaces from each line. */
|
||||
vector<Expr *> * es2 = new vector<Expr *>;
|
||||
vector<std::pair<Pos, Expr *> > * es2 = new vector<std::pair<Pos, Expr *> >;
|
||||
atStartOfLine = true;
|
||||
size_t curDropped = 0;
|
||||
size_t n = es.size();
|
||||
for (vector<Expr *>::iterator i = es.begin(); i != es.end(); ++i, --n) {
|
||||
ExprIndStr * e = dynamic_cast<ExprIndStr *>(*i);
|
||||
for (vector<std::pair<Pos, Expr *> >::iterator i = es.begin(); i != es.end(); ++i, --n) {
|
||||
ExprIndStr * e = dynamic_cast<ExprIndStr *>(i->second);
|
||||
if (!e) {
|
||||
atStartOfLine = false;
|
||||
curDropped = 0;
|
||||
@@ -254,11 +234,11 @@ static Expr * stripIndentation(const Pos & pos, SymbolTable & symbols, vector<Ex
|
||||
s2 = string(s2, 0, p + 1);
|
||||
}
|
||||
|
||||
es2->push_back(new ExprString(symbols.create(s2)));
|
||||
es2->emplace_back(i->first, new ExprString(symbols.create(s2)));
|
||||
}
|
||||
|
||||
/* If this is a single string, then don't do a concatenation. */
|
||||
return es2->size() == 1 && dynamic_cast<ExprString *>((*es2)[0]) ? (*es2)[0] : new ExprConcatStrings(pos, true, es2);
|
||||
return es2->size() == 1 && dynamic_cast<ExprString *>((*es2)[0].second) ? (*es2)[0].second : new ExprConcatStrings(pos, true, es2);
|
||||
}
|
||||
|
||||
|
||||
@@ -293,11 +273,18 @@ void yyerror(YYLTYPE * loc, yyscan_t scanner, ParseData * data, const char * err
|
||||
nix::Formal * formal;
|
||||
nix::NixInt n;
|
||||
nix::NixFloat nf;
|
||||
const char * id; // !!! -> Symbol
|
||||
char * path;
|
||||
char * uri;
|
||||
// using C a struct allows us to avoid having to define the special
|
||||
// members that using string_view here would implicitly delete.
|
||||
struct StringToken {
|
||||
const char * p;
|
||||
size_t l;
|
||||
operator std::string_view() const { return {p, l}; }
|
||||
};
|
||||
StringToken id; // !!! -> Symbol
|
||||
StringToken path;
|
||||
StringToken uri;
|
||||
std::vector<nix::AttrName> * attrNames;
|
||||
std::vector<nix::Expr *> * string_parts;
|
||||
std::vector<std::pair<nix::Pos, nix::Expr *> > * string_parts;
|
||||
}
|
||||
|
||||
%type <e> start expr expr_function expr_if expr_op
|
||||
@@ -342,13 +329,13 @@ expr: expr_function;
|
||||
|
||||
expr_function
|
||||
: ID ':' expr_function
|
||||
{ $$ = addArg(CUR_POS, $3, {data->symbols.create($1), nullptr}); }
|
||||
{ $$ = new ExprLambda(CUR_POS, data->symbols.create($1), 0, $3); }
|
||||
| '{' formals '}' ':' expr_function
|
||||
{ $$ = addArg(CUR_POS, $5, {data->state.sEpsilon, $2}); }
|
||||
{ $$ = new ExprLambda(CUR_POS, data->symbols.create(""), $2, $5); }
|
||||
| '{' formals '}' '@' ID ':' expr_function
|
||||
{ $$ = addArg(CUR_POS, $7, {data->symbols.create($5), $2}); }
|
||||
{ $$ = new ExprLambda(CUR_POS, data->symbols.create($5), $2, $7); }
|
||||
| ID '@' '{' formals '}' ':' expr_function
|
||||
{ $$ = addArg(CUR_POS, $7, {data->symbols.create($1), $4}); }
|
||||
{ $$ = new ExprLambda(CUR_POS, data->symbols.create($1), $4, $7); }
|
||||
| ASSERT expr ';' expr_function
|
||||
{ $$ = new ExprAssert(CUR_POS, $2, $4); }
|
||||
| WITH expr ';' expr_function
|
||||
@@ -384,7 +371,7 @@ expr_op
|
||||
| expr_op UPDATE expr_op { $$ = new ExprOpUpdate(CUR_POS, $1, $3); }
|
||||
| expr_op '?' attrpath { $$ = new ExprOpHasAttr($1, *$3); }
|
||||
| expr_op '+' expr_op
|
||||
{ $$ = new ExprConcatStrings(CUR_POS, false, new vector<Expr *>({$1, $3})); }
|
||||
{ $$ = new ExprConcatStrings(CUR_POS, false, new vector<std::pair<Pos, Expr *> >({{makeCurPos(@1, data), $1}, {makeCurPos(@3, data), $3}})); }
|
||||
| expr_op '-' expr_op { $$ = new ExprCall(CUR_POS, new ExprVar(data->symbols.create("__sub")), {$1, $3}); }
|
||||
| expr_op '*' expr_op { $$ = new ExprCall(CUR_POS, new ExprVar(data->symbols.create("__mul")), {$1, $3}); }
|
||||
| expr_op '/' expr_op { $$ = new ExprCall(CUR_POS, new ExprVar(data->symbols.create("__div")), {$1, $3}); }
|
||||
@@ -417,7 +404,8 @@ expr_select
|
||||
|
||||
expr_simple
|
||||
: ID {
|
||||
if (strcmp($1, "__curPos") == 0)
|
||||
std::string_view s = "__curPos";
|
||||
if ($1.l == s.size() && strncmp($1.p, s.data(), s.size()) == 0)
|
||||
$$ = new ExprPos(CUR_POS);
|
||||
else
|
||||
$$ = new ExprVar(CUR_POS, data->symbols.create($1));
|
||||
@@ -430,11 +418,11 @@ expr_simple
|
||||
}
|
||||
| path_start PATH_END { $$ = $1; }
|
||||
| path_start string_parts_interpolated PATH_END {
|
||||
$2->insert($2->begin(), $1);
|
||||
$2->insert($2->begin(), {makeCurPos(@1, data), $1});
|
||||
$$ = new ExprConcatStrings(CUR_POS, false, $2);
|
||||
}
|
||||
| SPATH {
|
||||
string path($1 + 1, strlen($1) - 2);
|
||||
string path($1.p + 1, $1.l - 2);
|
||||
$$ = new ExprCall(CUR_POS,
|
||||
new ExprVar(data->symbols.create("__findFile")),
|
||||
{new ExprVar(data->symbols.create("__nixPath")),
|
||||
@@ -464,38 +452,38 @@ expr_simple
|
||||
string_parts
|
||||
: STR
|
||||
| string_parts_interpolated { $$ = new ExprConcatStrings(CUR_POS, true, $1); }
|
||||
| { $$ = new ExprString(data->state.sEpsilon); }
|
||||
| { $$ = new ExprString(data->symbols.create("")); }
|
||||
;
|
||||
|
||||
string_parts_interpolated
|
||||
: string_parts_interpolated STR { $$ = $1; $1->push_back($2); }
|
||||
| string_parts_interpolated DOLLAR_CURLY expr '}' { $$ = $1; $1->push_back($3); }
|
||||
| DOLLAR_CURLY expr '}' { $$ = new vector<Expr *>; $$->push_back($2); }
|
||||
: string_parts_interpolated STR { $$ = $1; $1->emplace_back(makeCurPos(@2, data), $2); }
|
||||
| string_parts_interpolated DOLLAR_CURLY expr '}' { $$ = $1; $1->emplace_back(makeCurPos(@2, data), $3); }
|
||||
| DOLLAR_CURLY expr '}' { $$ = new vector<std::pair<Pos, Expr *> >; $$->emplace_back(makeCurPos(@1, data), $2); }
|
||||
| STR DOLLAR_CURLY expr '}' {
|
||||
$$ = new vector<Expr *>;
|
||||
$$->push_back($1);
|
||||
$$->push_back($3);
|
||||
$$ = new vector<std::pair<Pos, Expr *> >;
|
||||
$$->emplace_back(makeCurPos(@1, data), $1);
|
||||
$$->emplace_back(makeCurPos(@2, data), $3);
|
||||
}
|
||||
;
|
||||
|
||||
path_start
|
||||
: PATH {
|
||||
Path path(absPath($1, data->basePath));
|
||||
Path path(absPath({$1.p, $1.l}, data->basePath));
|
||||
/* add back in the trailing '/' to the first segment */
|
||||
if ($1[strlen($1)-1] == '/' && strlen($1) > 1)
|
||||
if ($1.p[$1.l-1] == '/' && $1.l > 1)
|
||||
path += "/";
|
||||
$$ = new ExprPath(path);
|
||||
}
|
||||
| HPATH {
|
||||
Path path(getHome() + string($1 + 1));
|
||||
Path path(getHome() + string($1.p + 1, $1.l - 1));
|
||||
$$ = new ExprPath(path);
|
||||
}
|
||||
;
|
||||
|
||||
ind_string_parts
|
||||
: ind_string_parts IND_STR { $$ = $1; $1->push_back($2); }
|
||||
| ind_string_parts DOLLAR_CURLY expr '}' { $$ = $1; $1->push_back($3); }
|
||||
| { $$ = new vector<Expr *>; }
|
||||
: ind_string_parts IND_STR { $$ = $1; $1->emplace_back(makeCurPos(@2, data), $2); }
|
||||
| ind_string_parts DOLLAR_CURLY expr '}' { $$ = $1; $1->emplace_back(makeCurPos(@2, data), $3); }
|
||||
| { $$ = new vector<std::pair<Pos, Expr *> >; }
|
||||
;
|
||||
|
||||
binds
|
||||
@@ -563,7 +551,7 @@ attrpath
|
||||
|
||||
attr
|
||||
: ID { $$ = $1; }
|
||||
| OR_KW { $$ = "or"; }
|
||||
| OR_KW { $$ = {"or", 2}; }
|
||||
;
|
||||
|
||||
string_attr
|
||||
@@ -609,7 +597,7 @@ formal
|
||||
namespace nix {
|
||||
|
||||
|
||||
Expr * EvalState::parse(const char * text, FileOrigin origin,
|
||||
Expr * EvalState::parse(char * text, size_t length, FileOrigin origin,
|
||||
const Path & path, const Path & basePath, StaticEnv & staticEnv)
|
||||
{
|
||||
yyscan_t scanner;
|
||||
@@ -629,7 +617,7 @@ Expr * EvalState::parse(const char * text, FileOrigin origin,
|
||||
data.basePath = basePath;
|
||||
|
||||
yylex_init(&scanner);
|
||||
yy_scan_string(text, scanner);
|
||||
yy_scan_buffer(text, length, scanner);
|
||||
int res = yyparse(scanner, &data);
|
||||
yylex_destroy(scanner);
|
||||
|
||||
@@ -675,26 +663,33 @@ Expr * EvalState::parseExprFromFile(const Path & path)
|
||||
|
||||
Expr * EvalState::parseExprFromFile(const Path & path, StaticEnv & staticEnv)
|
||||
{
|
||||
return parse(readFile(path).c_str(), foFile, path, dirOf(path), staticEnv);
|
||||
auto buffer = readFile(path);
|
||||
// readFile should have left some extra space for terminators
|
||||
buffer.append("\0\0", 2);
|
||||
return parse(buffer.data(), buffer.size(), foFile, path, dirOf(path), staticEnv);
|
||||
}
|
||||
|
||||
|
||||
Expr * EvalState::parseExprFromString(std::string_view s, const Path & basePath, StaticEnv & staticEnv)
|
||||
Expr * EvalState::parseExprFromString(std::string s, const Path & basePath, StaticEnv & staticEnv)
|
||||
{
|
||||
return parse(s.data(), foString, "", basePath, staticEnv);
|
||||
s.append("\0\0", 2);
|
||||
return parse(s.data(), s.size(), foString, "", basePath, staticEnv);
|
||||
}
|
||||
|
||||
|
||||
Expr * EvalState::parseExprFromString(std::string_view s, const Path & basePath)
|
||||
Expr * EvalState::parseExprFromString(std::string s, const Path & basePath)
|
||||
{
|
||||
return parseExprFromString(s, basePath, staticBaseEnv);
|
||||
return parseExprFromString(std::move(s), basePath, staticBaseEnv);
|
||||
}
|
||||
|
||||
|
||||
Expr * EvalState::parseStdin()
|
||||
{
|
||||
//Activity act(*logger, lvlTalkative, format("parsing standard input"));
|
||||
return parse(drainFD(0).data(), foStdin, "", absPath("."), staticBaseEnv);
|
||||
auto buffer = drainFD(0);
|
||||
// drainFD should have left some extra space for terminators
|
||||
buffer.append("\0\0", 2);
|
||||
return parse(buffer.data(), buffer.size(), foStdin, "", absPath("."), staticBaseEnv);
|
||||
}
|
||||
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -7,8 +7,7 @@ namespace nix {
|
||||
static void prim_unsafeDiscardStringContext(EvalState & state, const Pos & pos, Value * * args, Value & v)
|
||||
{
|
||||
PathSet context;
|
||||
string s = state.coerceToString(pos, *args[0], context);
|
||||
mkString(v, s, PathSet());
|
||||
v.mkString(state.coerceToString(pos, *args[0], context));
|
||||
}
|
||||
|
||||
static RegisterPrimOp primop_unsafeDiscardStringContext("__unsafeDiscardStringContext", 1, prim_unsafeDiscardStringContext);
|
||||
@@ -18,7 +17,7 @@ static void prim_hasContext(EvalState & state, const Pos & pos, Value * * args,
|
||||
{
|
||||
PathSet context;
|
||||
state.forceString(*args[0], context, pos);
|
||||
mkBool(v, !context.empty());
|
||||
v.mkBool(!context.empty());
|
||||
}
|
||||
|
||||
static RegisterPrimOp primop_hasContext("__hasContext", 1, prim_hasContext);
|
||||
@@ -39,7 +38,7 @@ static void prim_unsafeDiscardOutputDependency(EvalState & state, const Pos & po
|
||||
for (auto & p : context)
|
||||
context2.insert(p.at(0) == '=' ? string(p, 1) : p);
|
||||
|
||||
mkString(v, s, context2);
|
||||
v.mkString(s, context2);
|
||||
}
|
||||
|
||||
static RegisterPrimOp primop_unsafeDiscardOutputDependency("__unsafeDiscardOutputDependency", 1, prim_unsafeDiscardOutputDependency);
|
||||
@@ -103,28 +102,26 @@ static void prim_getContext(EvalState & state, const Pos & pos, Value * * args,
|
||||
}
|
||||
}
|
||||
|
||||
state.mkAttrs(v, contextInfos.size());
|
||||
auto attrs = state.buildBindings(contextInfos.size());
|
||||
|
||||
auto sPath = state.symbols.create("path");
|
||||
auto sAllOutputs = state.symbols.create("allOutputs");
|
||||
for (const auto & info : contextInfos) {
|
||||
auto & infoVal = *state.allocAttr(v, state.symbols.create(info.first));
|
||||
state.mkAttrs(infoVal, 3);
|
||||
auto infoAttrs = state.buildBindings(3);
|
||||
if (info.second.path)
|
||||
mkBool(*state.allocAttr(infoVal, sPath), true);
|
||||
infoAttrs.alloc(sPath).mkBool(true);
|
||||
if (info.second.allOutputs)
|
||||
mkBool(*state.allocAttr(infoVal, sAllOutputs), true);
|
||||
infoAttrs.alloc(sAllOutputs).mkBool(true);
|
||||
if (!info.second.outputs.empty()) {
|
||||
auto & outputsVal = *state.allocAttr(infoVal, state.sOutputs);
|
||||
auto & outputsVal = infoAttrs.alloc(state.sOutputs);
|
||||
state.mkList(outputsVal, info.second.outputs.size());
|
||||
size_t i = 0;
|
||||
for (const auto & output : info.second.outputs) {
|
||||
mkString(*(outputsVal.listElems()[i++] = state.allocValue()), output);
|
||||
}
|
||||
for (const auto & [i, output] : enumerate(info.second.outputs))
|
||||
(outputsVal.listElems()[i] = state.allocValue())->mkString(output);
|
||||
}
|
||||
infoVal.attrs->sort();
|
||||
attrs.alloc(info.first).mkAttrs(infoAttrs);
|
||||
}
|
||||
v.attrs->sort();
|
||||
|
||||
v.mkAttrs(attrs);
|
||||
}
|
||||
|
||||
static RegisterPrimOp primop_getContext("__getContext", 1, prim_getContext);
|
||||
@@ -181,14 +178,14 @@ static void prim_appendContext(EvalState & state, const Pos & pos, Value * * arg
|
||||
.errPos = *i.pos
|
||||
});
|
||||
}
|
||||
for (unsigned int n = 0; n < iter->value->listSize(); ++n) {
|
||||
auto name = state.forceStringNoCtx(*iter->value->listElems()[n], *iter->pos);
|
||||
for (auto elem : iter->value->listItems()) {
|
||||
auto name = state.forceStringNoCtx(*elem, *iter->pos);
|
||||
context.insert("!" + name + "!" + string(i.name));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
mkString(v, orig, context);
|
||||
v.mkString(orig, context);
|
||||
}
|
||||
|
||||
static RegisterPrimOp primop_appendContext("__appendContext", 2, prim_appendContext);
|
||||
|
||||
@@ -70,19 +70,19 @@ static void prim_fetchMercurial(EvalState & state, const Pos & pos, Value * * ar
|
||||
// FIXME: use name
|
||||
auto [tree, input2] = input.fetch(state.store);
|
||||
|
||||
state.mkAttrs(v, 8);
|
||||
auto attrs2 = state.buildBindings(8);
|
||||
auto storePath = state.store->printStorePath(tree.storePath);
|
||||
mkString(*state.allocAttr(v, state.sOutPath), storePath, PathSet({storePath}));
|
||||
attrs2.alloc(state.sOutPath).mkString(storePath, {storePath});
|
||||
if (input2.getRef())
|
||||
mkString(*state.allocAttr(v, state.symbols.create("branch")), *input2.getRef());
|
||||
attrs2.alloc("branch").mkString(*input2.getRef());
|
||||
// Backward compatibility: set 'rev' to
|
||||
// 0000000000000000000000000000000000000000 for a dirty tree.
|
||||
auto rev2 = input2.getRev().value_or(Hash(htSHA1));
|
||||
mkString(*state.allocAttr(v, state.symbols.create("rev")), rev2.gitRev());
|
||||
mkString(*state.allocAttr(v, state.symbols.create("shortRev")), std::string(rev2.gitRev(), 0, 12));
|
||||
attrs2.alloc("rev").mkString(rev2.gitRev());
|
||||
attrs2.alloc("shortRev").mkString(rev2.gitRev().substr(0, 12));
|
||||
if (auto revCount = input2.getRevCount())
|
||||
mkInt(*state.allocAttr(v, state.symbols.create("revCount")), *revCount);
|
||||
v.attrs->sort();
|
||||
attrs2.alloc("revCount").mkInt(*revCount);
|
||||
v.mkAttrs(attrs2);
|
||||
|
||||
state.allowPath(tree.storePath);
|
||||
}
|
||||
|
||||
@@ -21,49 +21,48 @@ void emitTreeAttrs(
|
||||
{
|
||||
assert(input.isImmutable());
|
||||
|
||||
state.mkAttrs(v, 8);
|
||||
auto attrs = state.buildBindings(8);
|
||||
|
||||
auto storePath = state.store->printStorePath(tree.storePath);
|
||||
|
||||
mkString(*state.allocAttr(v, state.sOutPath), storePath, PathSet({storePath}));
|
||||
attrs.alloc(state.sOutPath).mkString(storePath, {storePath});
|
||||
|
||||
// FIXME: support arbitrary input attributes.
|
||||
|
||||
auto narHash = input.getNarHash();
|
||||
assert(narHash);
|
||||
mkString(*state.allocAttr(v, state.symbols.create("narHash")),
|
||||
narHash->to_string(SRI, true));
|
||||
attrs.alloc("narHash").mkString(narHash->to_string(SRI, true));
|
||||
|
||||
if (input.getType() == "git")
|
||||
mkBool(*state.allocAttr(v, state.symbols.create("submodules")),
|
||||
attrs.alloc("submodules").mkBool(
|
||||
fetchers::maybeGetBoolAttr(input.attrs, "submodules").value_or(false));
|
||||
|
||||
if (!forceDirty) {
|
||||
|
||||
if (auto rev = input.getRev()) {
|
||||
mkString(*state.allocAttr(v, state.symbols.create("rev")), rev->gitRev());
|
||||
mkString(*state.allocAttr(v, state.symbols.create("shortRev")), rev->gitShortRev());
|
||||
attrs.alloc("rev").mkString(rev->gitRev());
|
||||
attrs.alloc("shortRev").mkString(rev->gitShortRev());
|
||||
} else if (emptyRevFallback) {
|
||||
// Backwards compat for `builtins.fetchGit`: dirty repos return an empty sha1 as rev
|
||||
auto emptyHash = Hash(htSHA1);
|
||||
mkString(*state.allocAttr(v, state.symbols.create("rev")), emptyHash.gitRev());
|
||||
mkString(*state.allocAttr(v, state.symbols.create("shortRev")), emptyHash.gitShortRev());
|
||||
attrs.alloc("rev").mkString(emptyHash.gitRev());
|
||||
attrs.alloc("shortRev").mkString(emptyHash.gitShortRev());
|
||||
}
|
||||
|
||||
if (auto revCount = input.getRevCount())
|
||||
mkInt(*state.allocAttr(v, state.symbols.create("revCount")), *revCount);
|
||||
attrs.alloc("revCount").mkInt(*revCount);
|
||||
else if (emptyRevFallback)
|
||||
mkInt(*state.allocAttr(v, state.symbols.create("revCount")), 0);
|
||||
attrs.alloc("revCount").mkInt(0);
|
||||
|
||||
}
|
||||
|
||||
if (auto lastModified = input.getLastModified()) {
|
||||
mkInt(*state.allocAttr(v, state.symbols.create("lastModified")), *lastModified);
|
||||
mkString(*state.allocAttr(v, state.symbols.create("lastModifiedDate")),
|
||||
attrs.alloc("lastModified").mkInt(*lastModified);
|
||||
attrs.alloc("lastModifiedDate").mkString(
|
||||
fmt("%s", std::put_time(std::gmtime(&*lastModified), "%Y%m%d%H%M%S")));
|
||||
}
|
||||
|
||||
v.attrs->sort();
|
||||
v.mkAttrs(attrs);
|
||||
}
|
||||
|
||||
std::string fixURI(std::string uri, EvalState & state, const std::string & defaultScheme = "file")
|
||||
@@ -74,7 +73,10 @@ std::string fixURI(std::string uri, EvalState & state, const std::string & defau
|
||||
|
||||
std::string fixURIForGit(std::string uri, EvalState & state)
|
||||
{
|
||||
static std::regex scp_uri("([^/].*)@(.*):(.*)");
|
||||
/* Detects scp-style uris (e.g. git@github.com:NixOS/nix) and fixes
|
||||
* them by removing the `:` and assuming a scheme of `ssh://`
|
||||
* */
|
||||
static std::regex scp_uri("([^/]*)@(.*):(.*)");
|
||||
if (uri[0] != '/' && std::regex_match(uri, scp_uri))
|
||||
return fixURI(std::regex_replace(uri, scp_uri, "$1@$2/$3"), state, "ssh");
|
||||
else
|
||||
@@ -245,7 +247,7 @@ static void fetch(EvalState & state, const Pos & pos, Value * * args, Value & v,
|
||||
state.allowPath(storePath);
|
||||
|
||||
auto path = state.store->printStorePath(storePath);
|
||||
mkString(v, path, PathSet({path}));
|
||||
v.mkString(path, PathSet({path}));
|
||||
}
|
||||
|
||||
static void prim_fetchurl(EvalState & state, const Pos & pos, Value * * args, Value & v)
|
||||
|
||||
@@ -1,86 +1,76 @@
|
||||
#include "primops.hh"
|
||||
#include "eval-inline.hh"
|
||||
|
||||
#include "../../cpptoml/cpptoml.h"
|
||||
#include "../../toml11/toml.hpp"
|
||||
|
||||
namespace nix {
|
||||
|
||||
static void prim_fromTOML(EvalState & state, const Pos & pos, Value * * args, Value & v)
|
||||
static void prim_fromTOML(EvalState & state, const Pos & pos, Value * * args, Value & val)
|
||||
{
|
||||
using namespace cpptoml;
|
||||
|
||||
auto toml = state.forceStringNoCtx(*args[0], pos);
|
||||
|
||||
std::istringstream tomlStream(toml);
|
||||
|
||||
std::function<void(Value &, std::shared_ptr<base>)> visit;
|
||||
std::function<void(Value &, toml::value)> visit;
|
||||
|
||||
visit = [&](Value & v, std::shared_ptr<base> t) {
|
||||
visit = [&](Value & v, toml::value t) {
|
||||
|
||||
if (auto t2 = t->as_table()) {
|
||||
switch(t.type())
|
||||
{
|
||||
case toml::value_t::table:
|
||||
{
|
||||
auto table = toml::get<toml::table>(t);
|
||||
|
||||
size_t size = 0;
|
||||
for (auto & i : *t2) { (void) i; size++; }
|
||||
size_t size = 0;
|
||||
for (auto & i : table) { (void) i; size++; }
|
||||
|
||||
state.mkAttrs(v, size);
|
||||
auto attrs = state.buildBindings(size);
|
||||
|
||||
for (auto & i : *t2) {
|
||||
auto & v2 = *state.allocAttr(v, state.symbols.create(i.first));
|
||||
for(auto & elem : table)
|
||||
visit(attrs.alloc(elem.first), elem.second);
|
||||
|
||||
if (auto i2 = i.second->as_table_array()) {
|
||||
size_t size2 = i2->get().size();
|
||||
state.mkList(v2, size2);
|
||||
for (size_t j = 0; j < size2; ++j)
|
||||
visit(*(v2.listElems()[j] = state.allocValue()), i2->get()[j]);
|
||||
v.mkAttrs(attrs);
|
||||
}
|
||||
else
|
||||
visit(v2, i.second);
|
||||
}
|
||||
break;;
|
||||
case toml::value_t::array:
|
||||
{
|
||||
auto array = toml::get<std::vector<toml::value>>(t);
|
||||
|
||||
size_t size = array.size();
|
||||
state.mkList(v, size);
|
||||
for (size_t i = 0; i < size; ++i)
|
||||
visit(*(v.listElems()[i] = state.allocValue()), array[i]);
|
||||
}
|
||||
break;;
|
||||
case toml::value_t::boolean:
|
||||
v.mkBool(toml::get<bool>(t));
|
||||
break;;
|
||||
case toml::value_t::integer:
|
||||
v.mkInt(toml::get<int64_t>(t));
|
||||
break;;
|
||||
case toml::value_t::floating:
|
||||
v.mkFloat(toml::get<NixFloat>(t));
|
||||
break;;
|
||||
case toml::value_t::string:
|
||||
v.mkString(toml::get<std::string>(t));
|
||||
break;;
|
||||
case toml::value_t::local_datetime:
|
||||
case toml::value_t::offset_datetime:
|
||||
case toml::value_t::local_date:
|
||||
case toml::value_t::local_time:
|
||||
// We fail since Nix doesn't have date and time types
|
||||
throw std::runtime_error("Dates and times are not supported");
|
||||
break;;
|
||||
case toml::value_t::empty:
|
||||
v.mkNull();
|
||||
break;;
|
||||
|
||||
v.attrs->sort();
|
||||
}
|
||||
|
||||
else if (auto t2 = t->as_array()) {
|
||||
size_t size = t2->get().size();
|
||||
|
||||
state.mkList(v, size);
|
||||
|
||||
for (size_t i = 0; i < size; ++i)
|
||||
visit(*(v.listElems()[i] = state.allocValue()), t2->get()[i]);
|
||||
}
|
||||
|
||||
// Handle cases like 'a = [[{ a = true }]]', which IMHO should be
|
||||
// parsed as a array containing an array containing a table,
|
||||
// but instead are parsed as an array containing a table array
|
||||
// containing a table.
|
||||
else if (auto t2 = t->as_table_array()) {
|
||||
size_t size = t2->get().size();
|
||||
|
||||
state.mkList(v, size);
|
||||
|
||||
for (size_t j = 0; j < size; ++j)
|
||||
visit(*(v.listElems()[j] = state.allocValue()), t2->get()[j]);
|
||||
}
|
||||
|
||||
else if (t->is_value()) {
|
||||
if (auto val = t->as<int64_t>())
|
||||
mkInt(v, val->get());
|
||||
else if (auto val = t->as<NixFloat>())
|
||||
mkFloat(v, val->get());
|
||||
else if (auto val = t->as<bool>())
|
||||
mkBool(v, val->get());
|
||||
else if (auto val = t->as<std::string>())
|
||||
mkString(v, val->get());
|
||||
else
|
||||
throw EvalError("unsupported value type in TOML");
|
||||
}
|
||||
|
||||
else abort();
|
||||
};
|
||||
|
||||
try {
|
||||
visit(v, parser(tomlStream).parse());
|
||||
} catch (std::runtime_error & e) {
|
||||
visit(val, toml::parse(tomlStream, "fromTOML" /* the "filename" */));
|
||||
} catch (std::exception & e) { // TODO: toml::syntax_error
|
||||
throw EvalError({
|
||||
.msg = hintfmt("while parsing a TOML string: %s", e.what()),
|
||||
.errPos = pos
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
#pragma once
|
||||
|
||||
#include <list>
|
||||
#include <map>
|
||||
#include <unordered_set>
|
||||
#include <unordered_map>
|
||||
|
||||
#include "types.hh"
|
||||
|
||||
@@ -70,15 +71,21 @@ public:
|
||||
class SymbolTable
|
||||
{
|
||||
private:
|
||||
typedef std::unordered_set<string> Symbols;
|
||||
Symbols symbols;
|
||||
std::unordered_map<std::string_view, Symbol> symbols;
|
||||
std::list<string> store;
|
||||
|
||||
public:
|
||||
Symbol create(std::string_view s)
|
||||
{
|
||||
// FIXME: avoid allocation if 's' already exists in the symbol table.
|
||||
std::pair<Symbols::iterator, bool> res = symbols.emplace(std::string(s));
|
||||
return Symbol(&*res.first);
|
||||
// Most symbols are looked up more than once, so we trade off insertion performance
|
||||
// for lookup performance.
|
||||
// TODO: could probably be done more efficiently with transparent Hash and Equals
|
||||
// on the original implementation using unordered_set
|
||||
auto it = symbols.find(s);
|
||||
if (it != symbols.end()) return it->second;
|
||||
|
||||
const string & rawSym = store.emplace_back(s);
|
||||
return symbols.emplace(rawSym, Symbol(&rawSym)).first->second;
|
||||
}
|
||||
|
||||
size_t size() const
|
||||
@@ -91,7 +98,7 @@ public:
|
||||
template<typename T>
|
||||
void dump(T callback)
|
||||
{
|
||||
for (auto & s : symbols)
|
||||
for (auto & s : store)
|
||||
callback(s);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -10,11 +10,11 @@
|
||||
namespace nix {
|
||||
|
||||
void printValueAsJSON(EvalState & state, bool strict,
|
||||
Value & v, JSONPlaceholder & out, PathSet & context)
|
||||
Value & v, const Pos & pos, JSONPlaceholder & out, PathSet & context)
|
||||
{
|
||||
checkInterrupt();
|
||||
|
||||
if (strict) state.forceValue(v);
|
||||
if (strict) state.forceValue(v, pos);
|
||||
|
||||
switch (v.type()) {
|
||||
|
||||
@@ -40,7 +40,7 @@ void printValueAsJSON(EvalState & state, bool strict,
|
||||
break;
|
||||
|
||||
case nAttrs: {
|
||||
auto maybeString = state.tryAttrsToString(noPos, v, context, false, false);
|
||||
auto maybeString = state.tryAttrsToString(pos, v, context, false, false);
|
||||
if (maybeString) {
|
||||
out.write(*maybeString);
|
||||
break;
|
||||
@@ -54,18 +54,18 @@ void printValueAsJSON(EvalState & state, bool strict,
|
||||
for (auto & j : names) {
|
||||
Attr & a(*v.attrs->find(state.symbols.create(j)));
|
||||
auto placeholder(obj.placeholder(j));
|
||||
printValueAsJSON(state, strict, *a.value, placeholder, context);
|
||||
printValueAsJSON(state, strict, *a.value, *a.pos, placeholder, context);
|
||||
}
|
||||
} else
|
||||
printValueAsJSON(state, strict, *i->value, out, context);
|
||||
printValueAsJSON(state, strict, *i->value, *i->pos, out, context);
|
||||
break;
|
||||
}
|
||||
|
||||
case nList: {
|
||||
auto list(out.list());
|
||||
for (unsigned int n = 0; n < v.listSize(); ++n) {
|
||||
for (auto elem : v.listItems()) {
|
||||
auto placeholder(list.placeholder());
|
||||
printValueAsJSON(state, strict, *v.listElems()[n], placeholder, context);
|
||||
printValueAsJSON(state, strict, *elem, pos, placeholder, context);
|
||||
}
|
||||
break;
|
||||
}
|
||||
@@ -79,18 +79,20 @@ void printValueAsJSON(EvalState & state, bool strict,
|
||||
break;
|
||||
|
||||
case nThunk:
|
||||
throw TypeError("cannot convert %1% to JSON", showType(v));
|
||||
|
||||
case nFunction:
|
||||
throw TypeError("cannot convert %1% to JSON", showType(v));
|
||||
auto e = TypeError({
|
||||
.msg = hintfmt("cannot convert %1% to JSON", showType(v)),
|
||||
.errPos = v.determinePos(pos)
|
||||
});
|
||||
throw e.addTrace(pos, hintfmt("message for the trace"));
|
||||
}
|
||||
}
|
||||
|
||||
void printValueAsJSON(EvalState & state, bool strict,
|
||||
Value & v, std::ostream & str, PathSet & context)
|
||||
Value & v, const Pos & pos, std::ostream & str, PathSet & context)
|
||||
{
|
||||
JSONPlaceholder out(str);
|
||||
printValueAsJSON(state, strict, v, out, context);
|
||||
printValueAsJSON(state, strict, v, pos, out, context);
|
||||
}
|
||||
|
||||
void ExternalValueBase::printValueAsJSON(EvalState & state, bool strict,
|
||||
|
||||
@@ -11,9 +11,9 @@ namespace nix {
|
||||
class JSONPlaceholder;
|
||||
|
||||
void printValueAsJSON(EvalState & state, bool strict,
|
||||
Value & v, JSONPlaceholder & out, PathSet & context);
|
||||
Value & v, const Pos & pos, JSONPlaceholder & out, PathSet & context);
|
||||
|
||||
void printValueAsJSON(EvalState & state, bool strict,
|
||||
Value & v, std::ostream & str, PathSet & context);
|
||||
Value & v, const Pos & pos, std::ostream & str, PathSet & context);
|
||||
|
||||
}
|
||||
|
||||
@@ -18,7 +18,8 @@ static XMLAttrs singletonAttrs(const string & name, const string & value)
|
||||
|
||||
|
||||
static void printValueAsXML(EvalState & state, bool strict, bool location,
|
||||
Value & v, XMLWriter & doc, PathSet & context, PathSet & drvsSeen);
|
||||
Value & v, XMLWriter & doc, PathSet & context, PathSet & drvsSeen,
|
||||
const Pos & pos);
|
||||
|
||||
|
||||
static void posToXML(XMLAttrs & xmlAttrs, const Pos & pos)
|
||||
@@ -46,17 +47,18 @@ static void showAttrs(EvalState & state, bool strict, bool location,
|
||||
|
||||
XMLOpenElement _(doc, "attr", xmlAttrs);
|
||||
printValueAsXML(state, strict, location,
|
||||
*a.value, doc, context, drvsSeen);
|
||||
*a.value, doc, context, drvsSeen, *a.pos);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void printValueAsXML(EvalState & state, bool strict, bool location,
|
||||
Value & v, XMLWriter & doc, PathSet & context, PathSet & drvsSeen)
|
||||
Value & v, XMLWriter & doc, PathSet & context, PathSet & drvsSeen,
|
||||
const Pos & pos)
|
||||
{
|
||||
checkInterrupt();
|
||||
|
||||
if (strict) state.forceValue(v);
|
||||
if (strict) state.forceValue(v, pos);
|
||||
|
||||
switch (v.type()) {
|
||||
|
||||
@@ -91,14 +93,14 @@ static void printValueAsXML(EvalState & state, bool strict, bool location,
|
||||
Path drvPath;
|
||||
a = v.attrs->find(state.sDrvPath);
|
||||
if (a != v.attrs->end()) {
|
||||
if (strict) state.forceValue(*a->value);
|
||||
if (strict) state.forceValue(*a->value, *a->pos);
|
||||
if (a->value->type() == nString)
|
||||
xmlAttrs["drvPath"] = drvPath = a->value->string.s;
|
||||
}
|
||||
|
||||
a = v.attrs->find(state.sOutPath);
|
||||
if (a != v.attrs->end()) {
|
||||
if (strict) state.forceValue(*a->value);
|
||||
if (strict) state.forceValue(*a->value, *a->pos);
|
||||
if (a->value->type() == nString)
|
||||
xmlAttrs["outPath"] = a->value->string.s;
|
||||
}
|
||||
@@ -120,40 +122,36 @@ static void printValueAsXML(EvalState & state, bool strict, bool location,
|
||||
|
||||
case nList: {
|
||||
XMLOpenElement _(doc, "list");
|
||||
for (unsigned int n = 0; n < v.listSize(); ++n)
|
||||
printValueAsXML(state, strict, location, *v.listElems()[n], doc, context, drvsSeen);
|
||||
for (auto v2 : v.listItems())
|
||||
printValueAsXML(state, strict, location, *v2, doc, context, drvsSeen, pos);
|
||||
break;
|
||||
}
|
||||
|
||||
case nFunction: {
|
||||
|
||||
if (!v.isLambda()) {
|
||||
// FIXME: Serialize primops and partial apps
|
||||
// FIXME: Serialize primops and primopapps
|
||||
doc.writeEmptyElement("unevaluated");
|
||||
break;
|
||||
}
|
||||
|
||||
XMLAttrs xmlAttrs;
|
||||
if (location) posToXML(xmlAttrs, v.lambda.fun->pos);
|
||||
XMLOpenElement _(doc, "function", xmlAttrs);
|
||||
|
||||
auto & arg = v.lambda.fun->args[0];
|
||||
|
||||
if (arg.formals) {
|
||||
if (v.lambda.fun->hasFormals()) {
|
||||
XMLAttrs attrs;
|
||||
if (arg.arg != state.sEpsilon) attrs["name"] = arg.arg;
|
||||
if (arg.formals->ellipsis) attrs["ellipsis"] = "1";
|
||||
if (!v.lambda.fun->arg.empty()) attrs["name"] = v.lambda.fun->arg;
|
||||
if (v.lambda.fun->formals->ellipsis) attrs["ellipsis"] = "1";
|
||||
XMLOpenElement _(doc, "attrspat", attrs);
|
||||
for (auto & i : arg.formals->formals)
|
||||
for (auto & i : v.lambda.fun->formals->formals)
|
||||
doc.writeEmptyElement("attr", singletonAttrs("name", i.name));
|
||||
} else
|
||||
doc.writeEmptyElement("varpat", singletonAttrs("name", arg.arg));
|
||||
doc.writeEmptyElement("varpat", singletonAttrs("name", v.lambda.fun->arg));
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case nExternal:
|
||||
v.external->printValueAsXML(state, strict, location, doc, context, drvsSeen);
|
||||
v.external->printValueAsXML(state, strict, location, doc, context, drvsSeen, pos);
|
||||
break;
|
||||
|
||||
case nFloat:
|
||||
@@ -167,19 +165,20 @@ static void printValueAsXML(EvalState & state, bool strict, bool location,
|
||||
|
||||
|
||||
void ExternalValueBase::printValueAsXML(EvalState & state, bool strict,
|
||||
bool location, XMLWriter & doc, PathSet & context, PathSet & drvsSeen) const
|
||||
bool location, XMLWriter & doc, PathSet & context, PathSet & drvsSeen,
|
||||
const Pos & pos) const
|
||||
{
|
||||
doc.writeEmptyElement("unevaluated");
|
||||
}
|
||||
|
||||
|
||||
void printValueAsXML(EvalState & state, bool strict, bool location,
|
||||
Value & v, std::ostream & out, PathSet & context)
|
||||
Value & v, std::ostream & out, PathSet & context, const Pos & pos)
|
||||
{
|
||||
XMLWriter doc(true, out);
|
||||
XMLOpenElement root(doc, "expr");
|
||||
PathSet drvsSeen;
|
||||
printValueAsXML(state, strict, location, v, doc, context, drvsSeen);
|
||||
printValueAsXML(state, strict, location, v, doc, context, drvsSeen, pos);
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -9,6 +9,6 @@
|
||||
namespace nix {
|
||||
|
||||
void printValueAsXML(EvalState & state, bool strict, bool location,
|
||||
Value & v, std::ostream & out, PathSet & context);
|
||||
|
||||
Value & v, std::ostream & out, PathSet & context, const Pos & pos);
|
||||
|
||||
}
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#include <cassert>
|
||||
|
||||
#include "symbol-table.hh"
|
||||
|
||||
#if HAVE_BOEHMGC
|
||||
@@ -8,6 +10,8 @@
|
||||
|
||||
namespace nix {
|
||||
|
||||
class BindingsBuilder;
|
||||
|
||||
|
||||
typedef enum {
|
||||
tInt = 1,
|
||||
@@ -21,7 +25,6 @@ typedef enum {
|
||||
tListN,
|
||||
tThunk,
|
||||
tApp,
|
||||
tPartialApp,
|
||||
tLambda,
|
||||
tBlackhole,
|
||||
tPrimOp,
|
||||
@@ -95,7 +98,8 @@ class ExternalValueBase
|
||||
|
||||
/* Print the value as XML. Defaults to unevaluated */
|
||||
virtual void printValueAsXML(EvalState & state, bool strict, bool location,
|
||||
XMLWriter & doc, PathSet & context, PathSet & drvsSeen) const;
|
||||
XMLWriter & doc, PathSet & context, PathSet & drvsSeen,
|
||||
const Pos & pos) const;
|
||||
|
||||
virtual ~ExternalValueBase()
|
||||
{
|
||||
@@ -126,7 +130,6 @@ public:
|
||||
|
||||
// type() == nFunction
|
||||
inline bool isLambda() const { return internalType == tLambda; };
|
||||
inline bool isPartialApp() const { return internalType == tPartialApp; };
|
||||
inline bool isPrimOp() const { return internalType == tPrimOp; };
|
||||
inline bool isPrimOpApp() const { return internalType == tPrimOpApp; };
|
||||
|
||||
@@ -198,7 +201,7 @@ public:
|
||||
case tNull: return nNull;
|
||||
case tAttrs: return nAttrs;
|
||||
case tList1: case tList2: case tListN: return nList;
|
||||
case tLambda: case tPartialApp: case tPrimOp: case tPrimOpApp: return nFunction;
|
||||
case tLambda: case tPrimOp: case tPrimOpApp: return nFunction;
|
||||
case tExternal: return nExternal;
|
||||
case tFloat: return nFloat;
|
||||
case tThunk: case tApp: case tBlackhole: return nThunk;
|
||||
@@ -234,6 +237,17 @@ public:
|
||||
string.context = context;
|
||||
}
|
||||
|
||||
void mkString(std::string_view s);
|
||||
|
||||
void mkString(std::string_view s, const PathSet & context);
|
||||
|
||||
void mkStringMove(const char * s, const PathSet & context);
|
||||
|
||||
inline void mkString(const Symbol & s)
|
||||
{
|
||||
mkString(((const std::string &) s).c_str());
|
||||
}
|
||||
|
||||
inline void mkPath(const char * s)
|
||||
{
|
||||
clearValue();
|
||||
@@ -241,6 +255,8 @@ public:
|
||||
path = s;
|
||||
}
|
||||
|
||||
void mkPath(std::string_view s);
|
||||
|
||||
inline void mkNull()
|
||||
{
|
||||
clearValue();
|
||||
@@ -254,6 +270,8 @@ public:
|
||||
attrs = a;
|
||||
}
|
||||
|
||||
Value & mkAttrs(BindingsBuilder & bindings);
|
||||
|
||||
inline void mkList(size_t size)
|
||||
{
|
||||
clearValue();
|
||||
@@ -309,13 +327,6 @@ public:
|
||||
app.right = r;
|
||||
}
|
||||
|
||||
inline void mkPartialApp(Value * l, Value * r)
|
||||
{
|
||||
internalType = tPartialApp;
|
||||
app.left = l;
|
||||
app.right = r;
|
||||
}
|
||||
|
||||
inline void mkExternal(ExternalValueBase * e)
|
||||
{
|
||||
clearValue();
|
||||
@@ -358,54 +369,45 @@ public:
|
||||
bool isTrivial() const;
|
||||
|
||||
std::vector<std::pair<Path, std::string>> getContext();
|
||||
|
||||
auto listItems()
|
||||
{
|
||||
struct ListIterable
|
||||
{
|
||||
typedef Value * const * iterator;
|
||||
iterator _begin, _end;
|
||||
iterator begin() const { return _begin; }
|
||||
iterator end() const { return _end; }
|
||||
};
|
||||
assert(isList());
|
||||
auto begin = listElems();
|
||||
return ListIterable { begin, begin + listSize() };
|
||||
}
|
||||
|
||||
auto listItems() const
|
||||
{
|
||||
struct ConstListIterable
|
||||
{
|
||||
typedef const Value * const * iterator;
|
||||
iterator _begin, _end;
|
||||
iterator begin() const { return _begin; }
|
||||
iterator end() const { return _end; }
|
||||
};
|
||||
assert(isList());
|
||||
auto begin = listElems();
|
||||
return ConstListIterable { begin, begin + listSize() };
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
||||
// TODO: Remove these static functions, replace call sites with v.mk* instead
|
||||
static inline void mkInt(Value & v, NixInt n)
|
||||
{
|
||||
v.mkInt(n);
|
||||
}
|
||||
|
||||
static inline void mkFloat(Value & v, NixFloat n)
|
||||
{
|
||||
v.mkFloat(n);
|
||||
}
|
||||
|
||||
static inline void mkBool(Value & v, bool b)
|
||||
{
|
||||
v.mkBool(b);
|
||||
}
|
||||
|
||||
static inline void mkNull(Value & v)
|
||||
{
|
||||
v.mkNull();
|
||||
}
|
||||
|
||||
static inline void mkApp(Value & v, Value & left, Value & right)
|
||||
{
|
||||
v.mkApp(&left, &right);
|
||||
}
|
||||
|
||||
static inline void mkString(Value & v, const Symbol & s)
|
||||
{
|
||||
v.mkString(((const string &) s).c_str());
|
||||
}
|
||||
|
||||
|
||||
void mkString(Value & v, const char * s);
|
||||
|
||||
|
||||
void mkPath(Value & v, const char * s);
|
||||
|
||||
|
||||
#if HAVE_BOEHMGC
|
||||
typedef std::vector<Value *, traceable_allocator<Value *> > ValueVector;
|
||||
typedef std::map<Symbol, Value *, std::less<Symbol>, traceable_allocator<std::pair<const Symbol, Value *> > > ValueMap;
|
||||
typedef std::map<Symbol, ValueVector, std::less<Symbol>, traceable_allocator<std::pair<const Symbol, ValueVector> > > ValueVectorMap;
|
||||
#else
|
||||
typedef std::vector<Value *> ValueVector;
|
||||
typedef std::map<Symbol, Value *> ValueMap;
|
||||
typedef std::map<Symbol, ValueVector> ValueVectorMap;
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
@@ -51,7 +51,7 @@ struct GitInputScheme : InputScheme
|
||||
for (auto &[name, value] : url.query) {
|
||||
if (name == "rev" || name == "ref")
|
||||
attrs.emplace(name, value);
|
||||
else if (name == "shallow")
|
||||
else if (name == "shallow" || name == "submodules")
|
||||
attrs.emplace(name, Explicit<bool> { value == "1" });
|
||||
else
|
||||
url2.query.emplace(name, value);
|
||||
@@ -324,17 +324,13 @@ struct GitInputScheme : InputScheme
|
||||
Path cacheDir = getCacheDir() + "/nix/gitv3/" + hashString(htSHA256, actualUrl).to_string(Base32, false);
|
||||
repoDir = cacheDir;
|
||||
|
||||
Path cacheDirLock = cacheDir + ".lock";
|
||||
createDirs(dirOf(cacheDir));
|
||||
AutoCloseFD lock = openLockFile(cacheDirLock, true);
|
||||
lockFile(lock.get(), ltWrite, true);
|
||||
PathLocks cacheDirLock({cacheDir + ".lock"});
|
||||
|
||||
if (!pathExists(cacheDir)) {
|
||||
runProgram("git", true, { "-c", "init.defaultBranch=" + gitInitialBranch, "init", "--bare", repoDir });
|
||||
}
|
||||
|
||||
deleteLockFile(cacheDirLock, lock.get());
|
||||
|
||||
Path localRefFile =
|
||||
input.getRef()->compare(0, 5, "refs/") == 0
|
||||
? cacheDir + "/" + *input.getRef()
|
||||
@@ -399,6 +395,8 @@ struct GitInputScheme : InputScheme
|
||||
|
||||
if (!input.getRev())
|
||||
input.attrs.insert_or_assign("rev", Hash::parseAny(chomp(readFile(localRefFile)), htSHA1).gitRev());
|
||||
|
||||
// cache dir lock is removed at scope end; we will only use read-only operations on specific revisions in the remainder
|
||||
}
|
||||
|
||||
bool isShallow = chomp(runProgram("git", true, { "-C", repoDir, "rev-parse", "--is-shallow-repository" })) == "true";
|
||||
|
||||
@@ -300,7 +300,7 @@ struct GitLabInputScheme : GitArchiveInputScheme
|
||||
if ("PAT" == token.substr(0, fldsplit))
|
||||
return std::make_pair("Private-token", token.substr(fldsplit+1));
|
||||
warn("Unrecognized GitLab token type %s", token.substr(0, fldsplit));
|
||||
return std::nullopt;
|
||||
return std::make_pair(token.substr(0,fldsplit), token.substr(fldsplit+1));
|
||||
}
|
||||
|
||||
Hash getRevFromRef(nix::ref<Store> store, const Input & input) const override
|
||||
|
||||
@@ -97,7 +97,7 @@ struct PathInputScheme : InputScheme
|
||||
// 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 (!isInDir(absPath, storePath))
|
||||
if (!isDirOrInDir(absPath, storePath))
|
||||
throw BadStorePath("relative path '%s' points outside of its parent's store path '%s'", path, storePath);
|
||||
}
|
||||
} else
|
||||
|
||||
@@ -67,18 +67,18 @@ DownloadFileResult downloadFile(
|
||||
storePath = std::move(cached->storePath);
|
||||
} else {
|
||||
StringSink sink;
|
||||
dumpString(*res.data, sink);
|
||||
auto hash = hashString(htSHA256, *res.data);
|
||||
dumpString(res.data, sink);
|
||||
auto hash = hashString(htSHA256, res.data);
|
||||
ValidPathInfo info {
|
||||
store->makeFixedOutputPath(FileIngestionMethod::Flat, hash, name),
|
||||
hashString(htSHA256, *sink.s),
|
||||
hashString(htSHA256, sink.s),
|
||||
};
|
||||
info.narSize = sink.s->size();
|
||||
info.narSize = sink.s.size();
|
||||
info.ca = FixedOutputHash {
|
||||
.method = FileIngestionMethod::Flat,
|
||||
.hash = hash,
|
||||
};
|
||||
auto source = StringSource { *sink.s };
|
||||
auto source = StringSource(sink.s);
|
||||
store->addToStore(info, source, NoRepair, NoCheckSigs);
|
||||
storePath = std::move(info.path);
|
||||
}
|
||||
@@ -176,6 +176,7 @@ struct TarballInputScheme : InputScheme
|
||||
|
||||
if (!hasSuffix(url.path, ".zip")
|
||||
&& !hasSuffix(url.path, ".tar")
|
||||
&& !hasSuffix(url.path, ".tgz")
|
||||
&& !hasSuffix(url.path, ".tar.gz")
|
||||
&& !hasSuffix(url.path, ".tar.xz")
|
||||
&& !hasSuffix(url.path, ".tar.bz2")
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
|
||||
namespace nix {
|
||||
|
||||
static std::string getS(const std::vector<Logger::Field> & fields, size_t n)
|
||||
static std::string_view getS(const std::vector<Logger::Field> & fields, size_t n)
|
||||
{
|
||||
assert(n < fields.size());
|
||||
assert(fields[n].type == Logger::Field::tString);
|
||||
@@ -103,17 +103,19 @@ public:
|
||||
~ProgressBar()
|
||||
{
|
||||
stop();
|
||||
updateThread.join();
|
||||
}
|
||||
|
||||
void stop() override
|
||||
{
|
||||
auto state(state_.lock());
|
||||
if (!state->active) return;
|
||||
state->active = false;
|
||||
writeToStderr("\r\e[K");
|
||||
updateCV.notify_one();
|
||||
quitCV.notify_one();
|
||||
{
|
||||
auto state(state_.lock());
|
||||
if (!state->active) return;
|
||||
state->active = false;
|
||||
writeToStderr("\r\e[K");
|
||||
updateCV.notify_one();
|
||||
quitCV.notify_one();
|
||||
}
|
||||
updateThread.join();
|
||||
}
|
||||
|
||||
bool isVerbose() override {
|
||||
|
||||
@@ -15,9 +15,14 @@
|
||||
#include <sys/stat.h>
|
||||
#include <unistd.h>
|
||||
#include <signal.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/socket.h>
|
||||
#include <netdb.h>
|
||||
#ifdef __linux__
|
||||
#include <features.h>
|
||||
#endif
|
||||
#ifdef __GLIBC__
|
||||
#include <gnu/lib-names.h>
|
||||
#include <nss.h>
|
||||
#include <dlfcn.h>
|
||||
#endif
|
||||
|
||||
#include <openssl/crypto.h>
|
||||
|
||||
@@ -121,21 +126,30 @@ static void preloadNSS() {
|
||||
been loaded in the parent. So we force a lookup of an invalid domain to force the NSS machinery to
|
||||
load its lookup libraries in the parent before any child gets a chance to. */
|
||||
std::call_once(dns_resolve_flag, []() {
|
||||
struct addrinfo *res = NULL;
|
||||
|
||||
/* nss will only force the "local" (not through nscd) dns resolution if its on the LOCALDOMAIN.
|
||||
We need the resolution to be done locally, as nscd socket will not be accessible in the
|
||||
sandbox. */
|
||||
char * previous_env = getenv("LOCALDOMAIN");
|
||||
setenv("LOCALDOMAIN", "invalid", 1);
|
||||
if (getaddrinfo("this.pre-initializes.the.dns.resolvers.invalid.", "http", NULL, &res) == 0) {
|
||||
if (res) freeaddrinfo(res);
|
||||
}
|
||||
if (previous_env) {
|
||||
setenv("LOCALDOMAIN", previous_env, 1);
|
||||
} else {
|
||||
unsetenv("LOCALDOMAIN");
|
||||
}
|
||||
#ifdef __GLIBC__
|
||||
/* On linux, glibc will run every lookup through the nss layer.
|
||||
* That means every lookup goes, by default, through nscd, which acts as a local
|
||||
* cache.
|
||||
* Because we run builds in a sandbox, we also remove access to nscd otherwise
|
||||
* lookups would leak into the sandbox.
|
||||
*
|
||||
* But now we have a new problem, we need to make sure the nss_dns backend that
|
||||
* does the dns lookups when nscd is not available is loaded or available.
|
||||
*
|
||||
* We can't make it available without leaking nix's environment, so instead we'll
|
||||
* load the backend, and configure nss so it does not try to run dns lookups
|
||||
* through nscd.
|
||||
*
|
||||
* This is technically only used for builtins:fetch* functions so we only care
|
||||
* about dns.
|
||||
*
|
||||
* All other platforms are unaffected.
|
||||
*/
|
||||
if (!dlopen(LIBNSS_DNS_SO, RTLD_NOW))
|
||||
warn("unable to load nss_dns backend");
|
||||
// FIXME: get hosts entry from nsswitch.conf.
|
||||
__nss_configure_lookup("hosts", "files dns");
|
||||
#endif
|
||||
});
|
||||
}
|
||||
|
||||
@@ -413,7 +427,7 @@ RunPager::RunPager()
|
||||
});
|
||||
|
||||
pid.setKillSignal(SIGINT);
|
||||
|
||||
stdout = fcntl(STDOUT_FILENO, F_DUPFD_CLOEXEC, 0);
|
||||
if (dup2(toPager.writeSide.get(), STDOUT_FILENO) == -1)
|
||||
throw SysError("dupping stdout");
|
||||
}
|
||||
@@ -424,7 +438,7 @@ RunPager::~RunPager()
|
||||
try {
|
||||
if (pid != -1) {
|
||||
std::cout.flush();
|
||||
close(STDOUT_FILENO);
|
||||
dup2(stdout, STDOUT_FILENO);
|
||||
pid.wait();
|
||||
}
|
||||
} catch (...) {
|
||||
|
||||
@@ -88,6 +88,7 @@ public:
|
||||
|
||||
private:
|
||||
Pid pid;
|
||||
int stdout;
|
||||
};
|
||||
|
||||
extern volatile ::sig_atomic_t blockInt;
|
||||
|
||||
@@ -31,7 +31,7 @@ BinaryCacheStore::BinaryCacheStore(const Params & params)
|
||||
|
||||
StringSink sink;
|
||||
sink << narVersionMagic1;
|
||||
narMagic = *sink.s;
|
||||
narMagic = sink.s;
|
||||
}
|
||||
|
||||
void BinaryCacheStore::init()
|
||||
@@ -68,7 +68,7 @@ void BinaryCacheStore::upsertFile(const std::string & path,
|
||||
}
|
||||
|
||||
void BinaryCacheStore::getFile(const std::string & path,
|
||||
Callback<std::shared_ptr<std::string>> callback) noexcept
|
||||
Callback<std::optional<std::string>> callback) noexcept
|
||||
{
|
||||
try {
|
||||
callback(getFile(path));
|
||||
@@ -77,9 +77,9 @@ void BinaryCacheStore::getFile(const std::string & path,
|
||||
|
||||
void BinaryCacheStore::getFile(const std::string & path, Sink & sink)
|
||||
{
|
||||
std::promise<std::shared_ptr<std::string>> promise;
|
||||
std::promise<std::optional<std::string>> promise;
|
||||
getFile(path,
|
||||
{[&](std::future<std::shared_ptr<std::string>> result) {
|
||||
{[&](std::future<std::optional<std::string>> result) {
|
||||
try {
|
||||
promise.set_value(result.get());
|
||||
} catch (...) {
|
||||
@@ -89,15 +89,15 @@ void BinaryCacheStore::getFile(const std::string & path, Sink & sink)
|
||||
sink(*promise.get_future().get());
|
||||
}
|
||||
|
||||
std::shared_ptr<std::string> BinaryCacheStore::getFile(const std::string & path)
|
||||
std::optional<std::string> BinaryCacheStore::getFile(const std::string & path)
|
||||
{
|
||||
StringSink sink;
|
||||
try {
|
||||
getFile(path, sink);
|
||||
} catch (NoSuchBinaryCacheFile &) {
|
||||
return nullptr;
|
||||
return std::nullopt;
|
||||
}
|
||||
return sink.s;
|
||||
return std::move(sink.s);
|
||||
}
|
||||
|
||||
std::string BinaryCacheStore::narInfoFileFor(const StorePath & storePath)
|
||||
@@ -308,16 +308,17 @@ void BinaryCacheStore::addToStore(const ValidPathInfo & info, Source & narSource
|
||||
}
|
||||
|
||||
StorePath BinaryCacheStore::addToStoreFromDump(Source & dump, const string & name,
|
||||
FileIngestionMethod method, HashType hashAlgo, RepairFlag repair)
|
||||
FileIngestionMethod method, HashType hashAlgo, RepairFlag repair, const StorePathSet & references)
|
||||
{
|
||||
if (method != FileIngestionMethod::Recursive || hashAlgo != htSHA256)
|
||||
unsupported("addToStoreFromDump");
|
||||
return addToStoreCommon(dump, repair, CheckSigs, [&](HashResult nar) {
|
||||
ValidPathInfo info {
|
||||
makeFixedOutputPath(method, nar.first, name),
|
||||
makeFixedOutputPath(method, nar.first, name, references),
|
||||
nar.first,
|
||||
};
|
||||
info.narSize = nar.second;
|
||||
info.references = references;
|
||||
return info;
|
||||
})->path;
|
||||
}
|
||||
@@ -366,11 +367,11 @@ void BinaryCacheStore::queryPathInfoUncached(const StorePath & storePath,
|
||||
auto callbackPtr = std::make_shared<decltype(callback)>(std::move(callback));
|
||||
|
||||
getFile(narInfoFile,
|
||||
{[=](std::future<std::shared_ptr<std::string>> fut) {
|
||||
{[=](std::future<std::optional<std::string>> fut) {
|
||||
try {
|
||||
auto data = fut.get();
|
||||
|
||||
if (!data) return (*callbackPtr)(nullptr);
|
||||
if (!data) return (*callbackPtr)({});
|
||||
|
||||
stats.narInfoRead++;
|
||||
|
||||
@@ -385,7 +386,7 @@ void BinaryCacheStore::queryPathInfoUncached(const StorePath & storePath,
|
||||
}
|
||||
|
||||
StorePath BinaryCacheStore::addToStore(const string & name, const Path & srcPath,
|
||||
FileIngestionMethod method, HashType hashAlgo, PathFilter & filter, RepairFlag repair)
|
||||
FileIngestionMethod method, HashType hashAlgo, PathFilter & filter, RepairFlag repair, const StorePathSet & references)
|
||||
{
|
||||
/* FIXME: Make BinaryCacheStore::addToStoreCommon support
|
||||
non-recursive+sha256 so we can just use the default
|
||||
@@ -404,10 +405,11 @@ StorePath BinaryCacheStore::addToStore(const string & name, const Path & srcPath
|
||||
});
|
||||
return addToStoreCommon(*source, repair, CheckSigs, [&](HashResult nar) {
|
||||
ValidPathInfo info {
|
||||
makeFixedOutputPath(method, h, name),
|
||||
makeFixedOutputPath(method, h, name, references),
|
||||
nar.first,
|
||||
};
|
||||
info.narSize = nar.second;
|
||||
info.references = references;
|
||||
info.ca = FixedOutputHash {
|
||||
.method = method,
|
||||
.hash = h,
|
||||
@@ -427,7 +429,7 @@ StorePath BinaryCacheStore::addTextToStore(const string & name, const string & s
|
||||
|
||||
StringSink sink;
|
||||
dumpString(s, sink);
|
||||
auto source = StringSource { *sink.s };
|
||||
StringSource source(sink.s);
|
||||
return addToStoreCommon(source, repair, CheckSigs, [&](HashResult nar) {
|
||||
ValidPathInfo info { path, nar.first };
|
||||
info.narSize = nar.second;
|
||||
@@ -437,40 +439,29 @@ StorePath BinaryCacheStore::addTextToStore(const string & name, const string & s
|
||||
})->path;
|
||||
}
|
||||
|
||||
std::optional<const Realisation> BinaryCacheStore::queryRealisation(const DrvOutput & id)
|
||||
void BinaryCacheStore::queryRealisationUncached(const DrvOutput & id,
|
||||
Callback<std::shared_ptr<const Realisation>> callback) noexcept
|
||||
{
|
||||
if (diskCache) {
|
||||
auto [cacheOutcome, maybeCachedRealisation] =
|
||||
diskCache->lookupRealisation(getUri(), id);
|
||||
switch (cacheOutcome) {
|
||||
case NarInfoDiskCache::oValid:
|
||||
debug("Returning a cached realisation for %s", id.to_string());
|
||||
return *maybeCachedRealisation;
|
||||
case NarInfoDiskCache::oInvalid:
|
||||
debug("Returning a cached missing realisation for %s", id.to_string());
|
||||
return {};
|
||||
case NarInfoDiskCache::oUnknown:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
auto outputInfoFilePath = realisationsPrefix + "/" + id.to_string() + ".doi";
|
||||
auto rawOutputInfo = getFile(outputInfoFilePath);
|
||||
|
||||
if (rawOutputInfo) {
|
||||
auto realisation = Realisation::fromJSON(
|
||||
nlohmann::json::parse(*rawOutputInfo), outputInfoFilePath);
|
||||
auto callbackPtr = std::make_shared<decltype(callback)>(std::move(callback));
|
||||
|
||||
if (diskCache)
|
||||
diskCache->upsertRealisation(
|
||||
getUri(), realisation);
|
||||
Callback<std::optional<std::string>> newCallback = {
|
||||
[=](std::future<std::optional<std::string>> fut) {
|
||||
try {
|
||||
auto data = fut.get();
|
||||
if (!data) return (*callbackPtr)({});
|
||||
|
||||
return {realisation};
|
||||
} else {
|
||||
if (diskCache)
|
||||
diskCache->upsertAbsentRealisation(getUri(), id);
|
||||
return std::nullopt;
|
||||
}
|
||||
auto realisation = Realisation::fromJSON(
|
||||
nlohmann::json::parse(*data), outputInfoFilePath);
|
||||
return (*callbackPtr)(std::make_shared<const Realisation>(realisation));
|
||||
} catch (...) {
|
||||
callbackPtr->rethrow();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
getFile(outputInfoFilePath, std::move(newCallback));
|
||||
}
|
||||
|
||||
void BinaryCacheStore::registerDrvOutput(const Realisation& info) {
|
||||
@@ -499,7 +490,7 @@ void BinaryCacheStore::addSignatures(const StorePath & storePath, const StringSe
|
||||
writeNarInfo(narInfo);
|
||||
}
|
||||
|
||||
std::shared_ptr<std::string> BinaryCacheStore::getBuildLog(const StorePath & path)
|
||||
std::optional<std::string> BinaryCacheStore::getBuildLog(const StorePath & path)
|
||||
{
|
||||
auto drvPath = path;
|
||||
|
||||
@@ -507,10 +498,10 @@ std::shared_ptr<std::string> BinaryCacheStore::getBuildLog(const StorePath & pat
|
||||
try {
|
||||
auto info = queryPathInfo(path);
|
||||
// FIXME: add a "Log" field to .narinfo
|
||||
if (!info->deriver) return nullptr;
|
||||
if (!info->deriver) return std::nullopt;
|
||||
drvPath = *info->deriver;
|
||||
} catch (InvalidPath &) {
|
||||
return nullptr;
|
||||
return std::nullopt;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -521,4 +512,14 @@ std::shared_ptr<std::string> BinaryCacheStore::getBuildLog(const StorePath & pat
|
||||
return getFile(logPath);
|
||||
}
|
||||
|
||||
void BinaryCacheStore::addBuildLog(const StorePath & drvPath, std::string_view log)
|
||||
{
|
||||
assert(drvPath.isDerivation());
|
||||
|
||||
upsertFile(
|
||||
"log/" + std::string(drvPath.to_string()),
|
||||
(std::string) log, // FIXME: don't copy
|
||||
"text/plain; charset=utf-8");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -51,6 +51,7 @@ public:
|
||||
const std::string & mimeType) = 0;
|
||||
|
||||
void upsertFile(const std::string & path,
|
||||
// FIXME: use std::string_view
|
||||
std::string && data,
|
||||
const std::string & mimeType);
|
||||
|
||||
@@ -62,10 +63,11 @@ public:
|
||||
|
||||
/* Fetch the specified file and call the specified callback with
|
||||
the result. A subclass may implement this asynchronously. */
|
||||
virtual void getFile(const std::string & path,
|
||||
Callback<std::shared_ptr<std::string>> callback) noexcept;
|
||||
virtual void getFile(
|
||||
const std::string & path,
|
||||
Callback<std::optional<std::string>> callback) noexcept;
|
||||
|
||||
std::shared_ptr<std::string> getFile(const std::string & path);
|
||||
std::optional<std::string> getFile(const std::string & path);
|
||||
|
||||
public:
|
||||
|
||||
@@ -97,18 +99,19 @@ public:
|
||||
RepairFlag repair, CheckSigsFlag checkSigs) override;
|
||||
|
||||
StorePath addToStoreFromDump(Source & dump, const string & name,
|
||||
FileIngestionMethod method, HashType hashAlgo, RepairFlag repair) override;
|
||||
FileIngestionMethod method, HashType hashAlgo, RepairFlag repair, const StorePathSet & references ) override;
|
||||
|
||||
StorePath addToStore(const string & name, const Path & srcPath,
|
||||
FileIngestionMethod method, HashType hashAlgo,
|
||||
PathFilter & filter, RepairFlag repair) override;
|
||||
PathFilter & filter, RepairFlag repair, const StorePathSet & references) override;
|
||||
|
||||
StorePath addTextToStore(const string & name, const string & s,
|
||||
const StorePathSet & references, RepairFlag repair) override;
|
||||
|
||||
void registerDrvOutput(const Realisation & info) override;
|
||||
|
||||
std::optional<const Realisation> queryRealisation(const DrvOutput &) override;
|
||||
void queryRealisationUncached(const DrvOutput &,
|
||||
Callback<std::shared_ptr<const Realisation>> callback) noexcept override;
|
||||
|
||||
void narFromPath(const StorePath & path, Sink & sink) override;
|
||||
|
||||
@@ -116,7 +119,9 @@ public:
|
||||
|
||||
void addSignatures(const StorePath & storePath, const StringSet & sigs) override;
|
||||
|
||||
std::shared_ptr<std::string> getBuildLog(const StorePath & path) override;
|
||||
std::optional<std::string> getBuildLog(const StorePath & path) override;
|
||||
|
||||
void addBuildLog(const StorePath & drvPath, std::string_view log) override;
|
||||
|
||||
};
|
||||
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
#include <regex>
|
||||
#include <queue>
|
||||
|
||||
#include <fstream>
|
||||
#include <sys/types.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/un.h>
|
||||
@@ -193,7 +194,7 @@ void DerivationGoal::loadDerivation()
|
||||
assert(worker.evalStore.isValidPath(drvPath));
|
||||
|
||||
/* Get the derivation. */
|
||||
drv = std::make_unique<Derivation>(worker.evalStore.derivationFromPath(drvPath));
|
||||
drv = std::make_unique<Derivation>(worker.evalStore.readDerivation(drvPath));
|
||||
|
||||
haveDerivation();
|
||||
}
|
||||
@@ -277,7 +278,7 @@ void DerivationGoal::outputsSubstitutionTried()
|
||||
|
||||
if (nrFailed > 0 && nrFailed > nrNoSubstituters + nrIncompleteClosure && !settings.tryFallback) {
|
||||
done(BuildResult::TransientFailure,
|
||||
fmt("some substitutes for the outputs of derivation '%s' failed (usually happens due to networking issues); try '--fallback' to build derivation from source ",
|
||||
Error("some substitutes for the outputs of derivation '%s' failed (usually happens due to networking issues); try '--fallback' to build derivation from source ",
|
||||
worker.store.printStorePath(drvPath)));
|
||||
return;
|
||||
}
|
||||
@@ -464,7 +465,6 @@ void DerivationGoal::inputsRealised()
|
||||
Derivation drvResolved { *std::move(attempt) };
|
||||
|
||||
auto pathResolved = writeDerivation(worker.store, drvResolved);
|
||||
resolvedDrv = drvResolved;
|
||||
|
||||
auto msg = fmt("Resolved derivation: '%s' -> '%s'",
|
||||
worker.store.printStorePath(drvPath),
|
||||
@@ -475,9 +475,9 @@ void DerivationGoal::inputsRealised()
|
||||
worker.store.printStorePath(pathResolved),
|
||||
});
|
||||
|
||||
auto resolvedGoal = worker.makeDerivationGoal(
|
||||
resolvedDrvGoal = worker.makeDerivationGoal(
|
||||
pathResolved, wantedOutputs, buildMode);
|
||||
addWaitee(resolvedGoal);
|
||||
addWaitee(resolvedDrvGoal);
|
||||
|
||||
state = &DerivationGoal::resolvedFinished;
|
||||
return;
|
||||
@@ -655,7 +655,7 @@ void DerivationGoal::tryLocalBuild() {
|
||||
throw Error(
|
||||
"unable to build with a primary store that isn't a local store; "
|
||||
"either pass a different '--store' or enable remote builds."
|
||||
"\nhttps://nixos.org/nix/manual/#chap-distributed-builds");
|
||||
"\nhttps://nixos.org/manual/nix/stable/advanced-topics/distributed-builds.html");
|
||||
}
|
||||
|
||||
|
||||
@@ -938,16 +938,17 @@ void DerivationGoal::buildDone()
|
||||
}
|
||||
|
||||
void DerivationGoal::resolvedFinished() {
|
||||
assert(resolvedDrv);
|
||||
assert(resolvedDrvGoal);
|
||||
auto resolvedDrv = *resolvedDrvGoal->drv;
|
||||
|
||||
auto resolvedHashes = staticOutputHashes(worker.store, *resolvedDrv);
|
||||
auto resolvedHashes = staticOutputHashes(worker.store, resolvedDrv);
|
||||
|
||||
StorePathSet outputPaths;
|
||||
|
||||
// `wantedOutputs` might be empty, which means “all the outputs”
|
||||
auto realWantedOutputs = wantedOutputs;
|
||||
if (realWantedOutputs.empty())
|
||||
realWantedOutputs = resolvedDrv->outputNames();
|
||||
realWantedOutputs = resolvedDrv.outputNames();
|
||||
|
||||
for (auto & wantedOutput : realWantedOutputs) {
|
||||
assert(initialOutputs.count(wantedOutput) != 0);
|
||||
@@ -979,9 +980,17 @@ void DerivationGoal::resolvedFinished() {
|
||||
outputPaths
|
||||
);
|
||||
|
||||
// This is potentially a bit fishy in terms of error reporting. Not sure
|
||||
// how to do it in a cleaner way
|
||||
amDone(nrFailed == 0 ? ecSuccess : ecFailed, ex);
|
||||
auto status = [&]() {
|
||||
auto resolvedResult = resolvedDrvGoal->getResult();
|
||||
switch (resolvedResult.status) {
|
||||
case BuildResult::AlreadyValid:
|
||||
return BuildResult::ResolvesToAlreadyValid;
|
||||
default:
|
||||
return resolvedResult.status;
|
||||
}
|
||||
}();
|
||||
|
||||
done(status);
|
||||
}
|
||||
|
||||
HookReply DerivationGoal::tryBuildHook()
|
||||
@@ -1329,6 +1338,13 @@ void DerivationGoal::done(BuildResult::Status status, std::optional<Error> ex)
|
||||
}
|
||||
|
||||
worker.updateProgress();
|
||||
|
||||
auto traceBuiltOutputsFile = getEnv("_NIX_TRACE_BUILT_OUTPUTS").value_or("");
|
||||
if (traceBuiltOutputsFile != "") {
|
||||
std::fstream fs;
|
||||
fs.open(traceBuiltOutputsFile, std::fstream::out);
|
||||
fs << worker.store.printStorePath(drvPath) << "\t" << result.toString() << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user