Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
fecad91b67 | ||
|
|
0220da3e10 | ||
|
|
936f9d45ba | ||
|
|
4b83830d0c | ||
|
|
5904262640 | ||
|
|
2c1ecf8e81 | ||
|
|
88c07341a6 | ||
|
|
07a08bddf0 | ||
|
|
c57ed84e28 | ||
|
|
ef4f5ba85e | ||
|
|
06bb2d95b4 | ||
|
|
6f809194d7 | ||
|
|
57d18df7d0 | ||
|
|
92077b4547 | ||
|
|
33972629d7 | ||
|
|
ac1b754138 | ||
|
|
c28dfc0305 | ||
|
|
e9b92169a5 | ||
|
|
ecd830b3b9 | ||
|
|
6ec8dab06a | ||
|
|
b29d3f4aee | ||
|
|
efe4289464 | ||
|
|
afc6c1bad6 | ||
|
|
6cd6ce5608 | ||
|
|
34bb806f74 | ||
|
|
c6c024ca6f | ||
|
|
03eaef3d7a | ||
|
|
b3110a15e9 | ||
|
|
25a108bb9c | ||
|
|
d308aeaf53 | ||
|
|
297b762513 | ||
|
|
46222bbc43 | ||
|
|
58204a3c39 | ||
|
|
3fb7ae0586 | ||
|
|
161a2ccf7a | ||
|
|
a583a2bc59 | ||
|
|
263d668222 | ||
|
|
fd7d979c79 | ||
|
|
46ffcce0c3 | ||
|
|
8e74c0bfd1 | ||
|
|
3d77b28eac | ||
|
|
47701677e8 | ||
|
|
5d147e125c | ||
|
|
159e621d1a | ||
|
|
511455965e | ||
|
|
7df4ef983e | ||
|
|
0a470fc345 | ||
|
|
8ae6d55db1 | ||
|
|
e068f49f7d | ||
|
|
20866a7031 | ||
|
|
70e68e0ec6 | ||
|
|
e87d1a63bd | ||
|
|
7cf539c728 | ||
|
|
7b09e9f2c4 | ||
|
|
15e5ac8039 | ||
|
|
48858ad5ca | ||
|
|
2bc5de8635 | ||
|
|
dc5f2e7da6 | ||
|
|
a4921b8ceb | ||
|
|
16591eb3cc | ||
|
|
c3f5413e80 | ||
|
|
aeb810b01e | ||
|
|
25a00cae5b | ||
|
|
620d57f036 | ||
|
|
6fcc290619 | ||
|
|
ed09d0c0a4 | ||
|
|
656390062a | ||
|
|
212e96f39c | ||
|
|
b584a42e3d | ||
|
|
5116214343 | ||
|
|
7986711632 | ||
|
|
1917d750a0 | ||
|
|
7ccd946407 | ||
|
|
5558652709 | ||
|
|
1906cce6fc | ||
|
|
9b11165aec | ||
|
|
22144afa8d | ||
|
|
2b29e4b852 | ||
|
|
6016bcd30e | ||
|
|
1b6ee8f4c7 | ||
|
|
cd49ee0897 | ||
|
|
1e2c7c04b1 | ||
|
|
6cc2a8f8ed | ||
|
|
bfee9a2581 | ||
|
|
f9ff67e948 | ||
|
|
6b05f688ee | ||
|
|
5c06e5297d | ||
|
|
24a356bf71 | ||
|
|
24e063efdc | ||
|
|
ca70fba0bf | ||
|
|
5959c591a0 | ||
|
|
c5f9d0d080 | ||
|
|
75e12b8e66 | ||
|
|
f0576d6775 | ||
|
|
ff08306746 | ||
|
|
bc2e43f3c8 | ||
|
|
039d5a023f | ||
|
|
b09b87321c | ||
|
|
107505e13a | ||
|
|
72d8209548 | ||
|
|
18a48d80a0 | ||
|
|
1b3a03f161 | ||
|
|
229567293c | ||
|
|
a4cb62ac25 | ||
|
|
31a551a60f | ||
|
|
6e85d1b5ba | ||
|
|
3a0cc43ac8 | ||
|
|
be0b9dda31 | ||
|
|
78206f06ec | ||
|
|
2ee9da9e22 | ||
|
|
6eba05613a | ||
|
|
7a03cbf09d | ||
|
|
69b8f9980f | ||
|
|
e93acab852 | ||
|
|
806970349b | ||
|
|
ea019e9a26 | ||
|
|
cc837e2458 | ||
|
|
28034bfa49 | ||
|
|
93f4fa8a15 | ||
|
|
c51b6a893c | ||
|
|
e6c44d166a | ||
|
|
7391533ea5 | ||
|
|
5cc2fc46ec | ||
|
|
3628b61ce0 | ||
|
|
470553bd05 | ||
|
|
4ddd077bfa | ||
|
|
0374d94437 | ||
|
|
00f698eb8b | ||
|
|
938092a213 | ||
|
|
772b70952f | ||
|
|
f9974f856e | ||
|
|
6955d41f2b | ||
|
|
934cf2d1f4 | ||
|
|
08d96ffad0 | ||
|
|
a9b4e26b5c | ||
|
|
c642441beb | ||
|
|
05420e7883 | ||
|
|
258897c265 | ||
|
|
239841787b | ||
|
|
cc63db1dd5 | ||
|
|
2c9cf50746 | ||
|
|
c3fc60d936 | ||
|
|
f72ed36025 | ||
|
|
78d777ca15 | ||
|
|
a68ebf8e37 | ||
|
|
804709706c | ||
|
|
c56bc3d81c | ||
|
|
4b07476848 | ||
|
|
bdd4646338 | ||
|
|
e73d9e9488 | ||
|
|
28bba8c44f | ||
|
|
8057a192e3 | ||
|
|
9fa1bee575 | ||
|
|
f45c731cd7 | ||
|
|
88936411bc | ||
|
|
0111ba98ea | ||
|
|
b008674e46 | ||
|
|
826dc0d07d | ||
|
|
97c6009c47 | ||
|
|
ca9c02dff1 | ||
|
|
5526a282b5 | ||
|
|
dadf7a5b46 | ||
|
|
3e067ac11c | ||
|
|
5ea138dc4b | ||
|
|
79a3ba7fa3 | ||
|
|
5f18cd2e84 | ||
|
|
52172607cf | ||
|
|
01a5ea9914 | ||
|
|
8add116acd | ||
|
|
d6143801fd | ||
|
|
9842077cb2 | ||
|
|
5e9c3da412 | ||
|
|
99ed558a93 | ||
|
|
1943b60ad8 | ||
|
|
96fbbbde55 | ||
|
|
d6fd6d8aff | ||
|
|
536c85ea49 | ||
|
|
66fa9e6a4d | ||
|
|
e42df686f3 | ||
|
|
92926be2fe | ||
|
|
b424d29d1b | ||
|
|
3007f57377 | ||
|
|
def5160b61 | ||
|
|
0a4e90395c | ||
|
|
f12492c66d | ||
|
|
aebea2e489 | ||
|
|
649bb60617 | ||
|
|
42d6f640c1 | ||
|
|
299141ecbd | ||
|
|
1b3a78a459 | ||
|
|
82248abd8f | ||
|
|
12f9129f60 | ||
|
|
b7629778ef | ||
|
|
68dcbb187e | ||
|
|
5ee7d8fbab | ||
|
|
2754a07ead | ||
|
|
06f62defe6 | ||
|
|
9c29a2ed35 | ||
|
|
e775d4d84f | ||
|
|
fbf0b2fa45 | ||
|
|
228ea7c2f9 | ||
|
|
cc511fd65b | ||
|
|
a9045c727f | ||
|
|
9fa12fc201 | ||
|
|
3ad53e43c8 | ||
|
|
772778c0ec | ||
|
|
e087bfef5f | ||
|
|
a6ce6d9e7c | ||
|
|
8cc19ed089 | ||
|
|
52edef3495 | ||
|
|
8d100dbef1 | ||
|
|
5c487761c4 | ||
|
|
3631dc6b2f | ||
|
|
aa61bc7472 | ||
|
|
0f96966a44 | ||
|
|
566afa1e9c | ||
|
|
444b03a36f | ||
|
|
d5a01d0f9d | ||
|
|
a7b4aaa2c3 | ||
|
|
3100b29fc5 | ||
|
|
094a08f839 | ||
|
|
4387d19359 | ||
|
|
24d5875514 | ||
|
|
b215b23e9e | ||
|
|
56d29dcd62 | ||
|
|
2d5e8e267b | ||
|
|
5ad89398d1 | ||
|
|
21c2d8d102 | ||
|
|
d62fc71b85 | ||
|
|
4bb4d5479a | ||
|
|
8eed07cda4 | ||
|
|
6c98e6a5de | ||
|
|
8b8ee53bc7 | ||
|
|
5943f41b8b | ||
|
|
08964d7328 | ||
|
|
69c88f5028 | ||
|
|
408a7bfac1 | ||
|
|
8d8d47abd2 | ||
|
|
46a369ad95 | ||
|
|
a3d6585c5a | ||
|
|
9de6bc5d05 | ||
|
|
bf3725da2a | ||
|
|
17dc306aa3 | ||
|
|
f794465ca8 | ||
|
|
8541d27fce | ||
|
|
3acc8adcad | ||
|
|
ea89df2b76 | ||
|
|
48c19c4633 | ||
|
|
198dbe7fa1 | ||
|
|
10dcee99ed | ||
|
|
4c9e3fa641 | ||
|
|
182e15b661 | ||
|
|
88164325fa | ||
|
|
f581ce0b0c | ||
|
|
91ef4d9a81 | ||
|
|
a9a8baaccb | ||
|
|
c31ebc50aa | ||
|
|
e28b683324 | ||
|
|
bbc107ef1e | ||
|
|
3a95e1a17c | ||
|
|
82951e5582 | ||
|
|
620e92e880 | ||
|
|
df27db712d | ||
|
|
d0fc615af6 | ||
|
|
4c34d384e6 | ||
|
|
a28b4445a4 | ||
|
|
2980d1fba9 | ||
|
|
dde6486eab | ||
|
|
c8daeba303 | ||
|
|
167e36a5c3 | ||
|
|
ac238d619c | ||
|
|
600daf972f | ||
|
|
e345182055 | ||
|
|
ecedd9c50c | ||
|
|
70f75be199 | ||
|
|
ad328bea15 | ||
|
|
bfaa5635de | ||
|
|
904f50412c | ||
|
|
90b8a34f82 | ||
|
|
d1de836938 | ||
|
|
f766e146f4 | ||
|
|
e35d6f78dc | ||
|
|
522ecab9b8 | ||
|
|
7586095504 | ||
|
|
a562d544d8 | ||
|
|
9c41c66c5b | ||
|
|
2bbc4a214e | ||
|
|
2e90a5a2a7 | ||
|
|
0a7084567f | ||
|
|
a807edfae8 | ||
|
|
a3f205b249 | ||
|
|
2001895f3d | ||
|
|
cf46f19444 | ||
|
|
8e3a7bd712 | ||
|
|
9958bd6992 | ||
|
|
e666e1156f | ||
|
|
f406288cc7 | ||
|
|
95c74eae26 | ||
|
|
f46612be96 | ||
|
|
d534f137f0 | ||
|
|
0f358ca5b6 | ||
|
|
cb6651e878 | ||
|
|
e464b0247d | ||
|
|
28bf183d2d | ||
|
|
c1f91570b3 | ||
|
|
20582e9ae3 | ||
|
|
b9c2b4d5b4 | ||
|
|
b9124a5c33 | ||
|
|
76e88871b2 | ||
|
|
00092b2d35 | ||
|
|
b674665765 | ||
|
|
09eb230900 | ||
|
|
983220bcd4 | ||
|
|
9fd9dedf12 | ||
|
|
1bda006b74 | ||
|
|
aac14222f5 | ||
|
|
54fe8e0773 | ||
|
|
47ae3ce2ca | ||
|
|
e03a8a1c92 | ||
|
|
8fbe96cb31 | ||
|
|
e6e495649c | ||
|
|
2923b55f9d | ||
|
|
31114ec3a5 | ||
|
|
c845c0ccad | ||
|
|
b14717ab90 | ||
|
|
6c4ac29917 | ||
|
|
e56f71edaf | ||
|
|
4fca02077c | ||
|
|
479e9172b3 | ||
|
|
4ba47205c6 | ||
|
|
6762424e24 | ||
|
|
d4c3b6327f | ||
|
|
1f7901ec3b | ||
|
|
5e2ffd0b8a | ||
|
|
8cf672091e | ||
|
|
9dbda2b3fe | ||
|
|
a2785b7391 | ||
|
|
295027f533 | ||
|
|
d3004c78d9 | ||
|
|
360056e174 | ||
|
|
f7b650d234 | ||
|
|
15e1b2c223 | ||
|
|
9e2fc6951c | ||
|
|
cfd968dd94 | ||
|
|
f3077fd88d | ||
|
|
cc8641815b | ||
|
|
2688fb73f1 | ||
|
|
8207359227 | ||
|
|
8b8fe6139e | ||
|
|
babe54bf97 | ||
|
|
e94806d030 | ||
|
|
a9e6752bbd | ||
|
|
4aa1e5c554 | ||
|
|
d950cfe70b | ||
|
|
56e30e161c | ||
|
|
f0eab0636b | ||
|
|
862c4c5ec5 | ||
|
|
767101824a | ||
|
|
e5c589d271 | ||
|
|
3e89ef597c | ||
|
|
9c2d63084b | ||
|
|
198d0338be | ||
|
|
b9e5b908ed | ||
|
|
4ccd48ce24 | ||
|
|
62f72eb9e1 | ||
|
|
e82767910c | ||
|
|
325d1cfebf | ||
|
|
d025142f52 | ||
|
|
b6c989b801 | ||
|
|
108e14bb18 | ||
|
|
6763084ae5 | ||
|
|
01d56c1eec | ||
|
|
967d066d8e | ||
|
|
1df702d347 | ||
|
|
9c2decaa19 | ||
|
|
5170c5691a | ||
|
|
79bba3782c | ||
|
|
ca94b38371 | ||
|
|
46f852cda0 | ||
|
|
afa7e01878 | ||
|
|
234ce610e0 | ||
|
|
7b10562370 | ||
|
|
8a25d787d7 | ||
|
|
c770a2422a | ||
|
|
4d1b64f118 | ||
|
|
eb7849e3a2 | ||
|
|
90d9c58d4d | ||
|
|
89a8207029 | ||
|
|
157170059d | ||
|
|
97421eb5ec | ||
|
|
d50d7a2874 | ||
|
|
9cd63d2244 | ||
|
|
f3eb29c653 | ||
|
|
9de6d10d11 | ||
|
|
ab42bf1dab | ||
|
|
d059bf48e4 | ||
|
|
f9613da180 | ||
|
|
6183cf2f19 | ||
|
|
f15083c10a | ||
|
|
66a3ac6a56 | ||
|
|
6ecf4f13f6 | ||
|
|
e6ab52cdd1 | ||
|
|
73acb8b836 | ||
|
|
fbf59d95f6 | ||
|
|
3a8f841612 | ||
|
|
b4ea83249b | ||
|
|
7f8e805c8e | ||
|
|
67c6f3eded | ||
|
|
50395b71a9 | ||
|
|
e3ce54ccee | ||
|
|
609586a16d | ||
|
|
7861260a5f | ||
|
|
7892ad15ab | ||
|
|
dbce685e91 | ||
|
|
8c79100839 | ||
|
|
3a4623afbb | ||
|
|
2605f4f4e6 | ||
|
|
477b0fbeca | ||
|
|
f5954e2d94 | ||
|
|
1ef2d5765b | ||
|
|
b1112bbef1 | ||
|
|
566a30c007 | ||
|
|
e98c029717 | ||
|
|
fd63c8bfcd | ||
|
|
0f65793f94 | ||
|
|
680ab6f83d | ||
|
|
6193105710 | ||
|
|
564fb7d9fa | ||
|
|
ed59bf7a18 | ||
|
|
6852289c46 | ||
|
|
1832ab71db | ||
|
|
aa115e22df | ||
|
|
fe241ece29 | ||
|
|
a6f348599c | ||
|
|
04559a0d45 | ||
|
|
e4d6bcb6cd | ||
|
|
f2bdc87595 | ||
|
|
15c15da482 | ||
|
|
d287b62b64 | ||
|
|
b74d92755d | ||
|
|
09a6321aeb | ||
|
|
58ef4d9a95 | ||
|
|
667d5f1936 | ||
|
|
eb3036da87 | ||
|
|
6586414bc7 | ||
|
|
5ee8944155 | ||
|
|
ae72be1b8b | ||
|
|
099125435f | ||
|
|
98a423b75a | ||
|
|
425cc612ad | ||
|
|
400e556b34 | ||
|
|
11800e6198 | ||
|
|
cd94665f38 | ||
|
|
ae60643c15 | ||
|
|
89380c03e9 | ||
|
|
2a8e5c8b11 | ||
|
|
d694c599e2 | ||
|
|
8319b1ab9f | ||
|
|
cf49472d60 | ||
|
|
6ec7460af1 | ||
|
|
6b1e671ac6 | ||
|
|
440adfbdd1 | ||
|
|
f4190c38ba | ||
|
|
000132cbd1 | ||
|
|
37f7098464 | ||
|
|
91b8814f0d | ||
|
|
4911a10a4e | ||
|
|
49cd7387ad |
+6
-11
@@ -35,8 +35,11 @@ Makefile.in
|
||||
|
||||
# /doc/manual/
|
||||
/doc/manual/manual.html
|
||||
/doc/manual/manual.xmli
|
||||
/doc/manual/manual.pdf
|
||||
/doc/manual/manual.is-valid
|
||||
/doc/manual/*.1
|
||||
/doc/manual/*.5
|
||||
/doc/manual/*.8
|
||||
/doc/manual/images
|
||||
/doc/manual/version.txt
|
||||
@@ -60,14 +63,12 @@ Makefile.in
|
||||
/scripts/GeneratePatches.pm
|
||||
/scripts/download-using-manifests.pl
|
||||
/scripts/copy-from-other-stores.pl
|
||||
/scripts/download-from-binary-cache.pl
|
||||
/scripts/find-runtime-roots.pl
|
||||
/scripts/build-remote.pl
|
||||
/scripts/nix-reduce-build
|
||||
/scripts/nix-http-export.cgi
|
||||
|
||||
# /src/bin2c/
|
||||
/src/bin2c/bin2c
|
||||
|
||||
# /src/bsdiff-4.3/
|
||||
/src/bsdiff-4.3/bsdiff
|
||||
/src/bsdiff-4.3/bspatch
|
||||
@@ -85,15 +86,12 @@ Makefile.in
|
||||
|
||||
# /src/nix-env/
|
||||
/src/nix-env/nix-env
|
||||
/src/nix-env/help.txt.hh
|
||||
|
||||
# /src/nix-hash/
|
||||
/src/nix-hash/nix-hash
|
||||
/src/nix-hash/help.txt.hh
|
||||
|
||||
# /src/nix-instantiate/
|
||||
/src/nix-instantiate/nix-instantiate
|
||||
/src/nix-instantiate/help.txt.hh
|
||||
|
||||
# /src/nix-log2xml/
|
||||
/src/nix-log2xml/nix-log2xml
|
||||
@@ -104,15 +102,12 @@ Makefile.in
|
||||
|
||||
# /src/nix-setuid-helper/
|
||||
/src/nix-setuid-helper/nix-setuid-helper
|
||||
/src/nix-setuid-helper/help.txt.hh
|
||||
|
||||
# /src/nix-store/
|
||||
/src/nix-store/help.txt.hh
|
||||
/src/nix-store/nix-store
|
||||
|
||||
# /src/nix-worker/
|
||||
/src/nix-worker/nix-worker
|
||||
/src/nix-worker/help.txt.hh
|
||||
# /src/nix-daemon/
|
||||
/src/nix-daemon/nix-daemon
|
||||
|
||||
# /tests/
|
||||
/tests/test-tmp
|
||||
|
||||
+1
-3
@@ -1,6 +1,6 @@
|
||||
SUBDIRS = src perl scripts corepkgs doc misc tests
|
||||
EXTRA_DIST = substitute.mk nix.spec nix.spec.in bootstrap.sh \
|
||||
NEWS version misc/systemd/nix-worker.service
|
||||
NEWS version misc/systemd/nix-daemon.service
|
||||
|
||||
pkginclude_HEADERS = config.h
|
||||
|
||||
@@ -27,11 +27,9 @@ init-state:
|
||||
$(INSTALL) $(INIT_FLAGS) -d $(DESTDIR)$(localstatedir)/nix/profiles
|
||||
$(INSTALL) $(INIT_FLAGS) -d $(DESTDIR)$(localstatedir)/nix/gcroots
|
||||
$(INSTALL) $(INIT_FLAGS) -d $(DESTDIR)$(localstatedir)/nix/temproots
|
||||
ln -sfn $(localstatedir)/nix/profiles $(DESTDIR)$(localstatedir)/nix/gcroots/profiles
|
||||
$(INSTALL) $(INIT_FLAGS) -d $(DESTDIR)$(localstatedir)/nix/userpool
|
||||
-$(INSTALL) $(INIT_FLAGS) -d $(DESTDIR)$(storedir)
|
||||
$(INSTALL) $(INIT_FLAGS) $(GROUP_WRITABLE) -d $(DESTDIR)$(localstatedir)/nix/manifests
|
||||
ln -sfn $(localstatedir)/nix/manifests $(DESTDIR)$(localstatedir)/nix/gcroots/manifests
|
||||
|
||||
else
|
||||
|
||||
|
||||
+59
-9
@@ -25,12 +25,12 @@ AC_ARG_WITH(system, AC_HELP_STRING([--with-system=SYSTEM],
|
||||
|
||||
case "$host_os" in
|
||||
linux-gnu*)
|
||||
# For backward compatibility, strip the `-gnu' part.
|
||||
system="$machine_name-linux";;
|
||||
# For backward compatibility, strip the `-gnu' part.
|
||||
system="$machine_name-linux";;
|
||||
*)
|
||||
# Strip the version number from names such as `gnu0.3',
|
||||
# `darwin10.2.0', etc.
|
||||
system="$machine_name-`echo $host_os | "$SED" -e's/@<:@0-9.@:>@*$//g'`";;
|
||||
system="$machine_name-`echo $host_os | "$SED" -e's/@<:@0-9.@:>@*$//g'`";;
|
||||
esac])
|
||||
|
||||
sys_name=$(uname -s | tr 'A-Z ' 'a-z_')
|
||||
@@ -40,7 +40,7 @@ case $sys_name in
|
||||
sys_name=cygwin
|
||||
;;
|
||||
esac
|
||||
|
||||
|
||||
AC_MSG_RESULT($system)
|
||||
AC_SUBST(system)
|
||||
AC_DEFINE_UNQUOTED(SYSTEM, ["$system"], [platform identifier (`cpu-os')])
|
||||
@@ -68,6 +68,8 @@ if test "$sys_name" = sunos; then
|
||||
fi
|
||||
|
||||
|
||||
CFLAGS=${CFLAGS:--g -O3 -Wall}
|
||||
CXXFLAGS=${CXXFLAGS:--g -O3 -Wall}
|
||||
AC_PROG_CC
|
||||
AC_PROG_CXX
|
||||
|
||||
@@ -106,6 +108,7 @@ AC_LANG_POP(C++)
|
||||
# Check for chroot support (requires chroot() and bind mounts).
|
||||
AC_CHECK_FUNCS([chroot])
|
||||
AC_CHECK_FUNCS([unshare])
|
||||
AC_CHECK_FUNCS([statvfs])
|
||||
AC_CHECK_HEADERS([sched.h])
|
||||
AC_CHECK_HEADERS([sys/param.h])
|
||||
AC_CHECK_HEADERS([sys/mount.h], [], [],
|
||||
@@ -115,6 +118,31 @@ AC_CHECK_HEADERS([sys/mount.h], [], [],
|
||||
])
|
||||
|
||||
|
||||
# Check for vfork.
|
||||
#AC_FUNC_FORK()
|
||||
|
||||
|
||||
# Check for lutimes, optionally used for changing the mtime of
|
||||
# symlinks.
|
||||
AC_CHECK_FUNCS([lutimes])
|
||||
|
||||
|
||||
# Check for sched_setaffinity.
|
||||
AC_CHECK_FUNCS([sched_setaffinity])
|
||||
|
||||
|
||||
# Check whether the store optimiser can optimise symlinks.
|
||||
AC_MSG_CHECKING([whether it is possible to create a link to a symlink])
|
||||
ln -s bla tmp_link
|
||||
if ln tmp_link tmp_link2 2> /dev/null; then
|
||||
AC_MSG_RESULT(yes)
|
||||
AC_DEFINE(CAN_LINK_SYMLINK, 1, [Whether link() works on symlinks.])
|
||||
else
|
||||
AC_MSG_RESULT(no)
|
||||
fi
|
||||
rm -f tmp_link tmp_link2
|
||||
|
||||
|
||||
# Check for <locale>.
|
||||
AC_LANG_PUSH(C++)
|
||||
AC_CHECK_HEADERS([locale])
|
||||
@@ -161,9 +189,10 @@ NEED_PROG(perl, perl)
|
||||
NEED_PROG(sed, sed)
|
||||
NEED_PROG(tar, tar)
|
||||
NEED_PROG(bzip2, bzip2)
|
||||
NEED_PROG(gzip, gzip)
|
||||
NEED_PROG(xz, xz)
|
||||
AC_PATH_PROG(dot, dot)
|
||||
AC_PATH_PROG(dblatex, dblatex)
|
||||
AC_PATH_PROG(gzip, gzip)
|
||||
AC_PATH_PROG(pv, pv, pv)
|
||||
|
||||
|
||||
@@ -249,7 +278,7 @@ if test "$gc" = yes; then
|
||||
fi
|
||||
|
||||
|
||||
# Check for the required Perl dependencies (DBI and DBD::SQLite).
|
||||
# Check for the required Perl dependencies (DBI, DBD::SQLite and WWW::Curl).
|
||||
perlFlags="-I$perllibdir"
|
||||
|
||||
AC_ARG_WITH(dbi, AC_HELP_STRING([--with-dbi=PATH],
|
||||
@@ -260,13 +289,24 @@ AC_ARG_WITH(dbd-sqlite, AC_HELP_STRING([--with-dbd-sqlite=PATH],
|
||||
[prefix of the Perl DBD::SQLite library]),
|
||||
perlFlags="$perlFlags -I$withval")
|
||||
|
||||
AC_ARG_WITH(www-curl, AC_HELP_STRING([--with-www-curl=PATH],
|
||||
[prefix of the Perl WWW::Curl library]),
|
||||
perlFlags="$perlFlags -I$withval")
|
||||
|
||||
AC_MSG_CHECKING([whether DBD::SQLite works])
|
||||
if ! $perl $perlFlags -e 'use DBI; use DBD::SQLite;' 2>&5; then
|
||||
AC_MSG_RESULT(no)
|
||||
AC_MSG_FAILURE([The Perl modules DBI and/or DBD::SQLite are missing.])
|
||||
fi
|
||||
AC_MSG_RESULT(yes)
|
||||
|
||||
|
||||
AC_MSG_CHECKING([whether WWW::Curl works])
|
||||
if ! $perl $perlFlags -e 'use WWW::Curl;' 2>&5; then
|
||||
AC_MSG_RESULT(no)
|
||||
AC_MSG_FAILURE([The Perl module WWW::Curl is missing.])
|
||||
fi
|
||||
AC_MSG_RESULT(yes)
|
||||
|
||||
AC_SUBST(perlFlags)
|
||||
|
||||
|
||||
@@ -310,10 +350,20 @@ eval dynlib_suffix=$shrext_cmds
|
||||
AC_SUBST(dynlib_suffix)
|
||||
|
||||
|
||||
# Do we have GNU tar?
|
||||
AC_MSG_CHECKING([if you have a recent GNU tar])
|
||||
if $tar --version 2> /dev/null | grep -q GNU && tar cvf /dev/null --warning=no-timestamp ./config.log > /dev/null; then
|
||||
AC_MSG_RESULT(yes)
|
||||
tarFlags="--warning=no-timestamp"
|
||||
else
|
||||
AC_MSG_RESULT(no)
|
||||
fi
|
||||
AC_SUBST(tarFlags)
|
||||
|
||||
|
||||
AM_CONFIG_HEADER([config.h])
|
||||
AC_CONFIG_FILES([Makefile
|
||||
src/Makefile
|
||||
src/bin2c/Makefile
|
||||
src/boost/Makefile
|
||||
src/boost/format/Makefile
|
||||
src/libutil/Makefile
|
||||
@@ -324,7 +374,7 @@ AC_CONFIG_FILES([Makefile
|
||||
src/libexpr/Makefile
|
||||
src/nix-instantiate/Makefile
|
||||
src/nix-env/Makefile
|
||||
src/nix-worker/Makefile
|
||||
src/nix-daemon/Makefile
|
||||
src/nix-setuid-helper/Makefile
|
||||
src/nix-log2xml/Makefile
|
||||
src/bsdiff-4.3/Makefile
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
all-local: config.nix
|
||||
|
||||
files = nar.nix buildenv.nix buildenv.pl unpack-channel.nix unpack-channel.sh derivation.nix fetchurl.nix
|
||||
files = nar.nix buildenv.nix buildenv.pl unpack-channel.nix derivation.nix fetchurl.nix \
|
||||
imported-drv-to-derivation.nix
|
||||
|
||||
install-exec-local:
|
||||
$(INSTALL) -d $(DESTDIR)$(datadir)/nix/corepkgs
|
||||
|
||||
@@ -2,18 +2,22 @@ with import <nix/config.nix>;
|
||||
|
||||
{ derivations, manifest }:
|
||||
|
||||
derivation {
|
||||
derivation {
|
||||
name = "user-environment";
|
||||
system = builtins.currentSystem;
|
||||
builder = perl;
|
||||
args = [ "-w" ./buildenv.pl ];
|
||||
|
||||
|
||||
manifest = manifest;
|
||||
|
||||
# !!! grmbl, need structured data for passing this in a clean way.
|
||||
paths = derivations;
|
||||
active = map (x: if x ? meta && x.meta ? active then x.meta.active else "true") derivations;
|
||||
priority = map (x: if x ? meta && x.meta ? priority then x.meta.priority else "5") derivations;
|
||||
derivations =
|
||||
map (d:
|
||||
[ (d.meta.active or "true")
|
||||
(d.meta.priority or 5)
|
||||
(builtins.length d.outputs)
|
||||
] ++ map (output: builtins.getAttr output d) d.outputs)
|
||||
derivations;
|
||||
|
||||
# Building user environments remotely just causes huge amounts of
|
||||
# network traffic, so don't do that.
|
||||
|
||||
+26
-27
@@ -36,19 +36,16 @@ sub createLinks {
|
||||
if ($srcFile =~ /\/propagated-build-inputs$/ ||
|
||||
$srcFile =~ /\/nix-support$/ ||
|
||||
$srcFile =~ /\/perllocal.pod$/ ||
|
||||
$srcFile =~ /\/easy-install.pth$/ ||
|
||||
$srcFile =~ /\/site.py$/ ||
|
||||
$srcFile =~ /\/site.pyc$/ ||
|
||||
$srcFile =~ /\/info\/dir$/ ||
|
||||
$srcFile =~ /\/log$/)
|
||||
{
|
||||
# Do nothing.
|
||||
}
|
||||
}
|
||||
|
||||
elsif (-d $srcFile) {
|
||||
|
||||
lstat $dstFile;
|
||||
|
||||
|
||||
if (-d _) {
|
||||
createLinks($srcFile, $dstFile, $priority);
|
||||
}
|
||||
@@ -56,10 +53,10 @@ sub createLinks {
|
||||
elsif (-l _) {
|
||||
my $target = readlink $dstFile or die;
|
||||
if (!-d $target) {
|
||||
die "collission between directory `$srcFile' and non-directory `$target'";
|
||||
die "collision between directory `$srcFile' and non-directory `$target'";
|
||||
}
|
||||
unlink $dstFile or die "error unlinking `$dstFile': $!";
|
||||
mkdir $dstFile, 0755 ||
|
||||
mkdir $dstFile, 0755 ||
|
||||
die "error creating directory `$dstFile': $!";
|
||||
createLinks($target, $dstFile, $priorities{$dstFile});
|
||||
createLinks($srcFile, $dstFile, $priority);
|
||||
@@ -78,15 +75,15 @@ sub createLinks {
|
||||
if (-l $dstFile) {
|
||||
my $target = readlink $dstFile;
|
||||
my $prevPriority = $priorities{$dstFile};
|
||||
die ( "Collission between `$srcFile' and `$target'. "
|
||||
. "Suggested solution: use `nix-env --set-flag "
|
||||
die ( "collision between `$srcFile' and `$target'; "
|
||||
. "use `nix-env --set-flag "
|
||||
. "priority NUMBER PKGNAME' to change the priority of "
|
||||
. "one of the conflicting packages.\n" )
|
||||
. "one of the conflicting packages\n" )
|
||||
if $prevPriority == $priority;
|
||||
next if $prevPriority < $priority;
|
||||
unlink $dstFile or die;
|
||||
}
|
||||
|
||||
|
||||
symlink($srcFile, $dstFile) ||
|
||||
die "error creating link `$dstFile': $!";
|
||||
$priorities{$dstFile} = $priority;
|
||||
@@ -125,27 +122,29 @@ sub addPkg {
|
||||
|
||||
# Convert the stuff we get from the environment back into a coherent
|
||||
# data type.
|
||||
my @paths = split ' ', $ENV{"paths"};
|
||||
my @active = split ' ', $ENV{"active"};
|
||||
my @priority = split ' ', $ENV{"priority"};
|
||||
|
||||
die if scalar @paths != scalar @active;
|
||||
die if scalar @paths != scalar @priority;
|
||||
|
||||
my %pkgs;
|
||||
|
||||
for (my $n = 0; $n < scalar @paths; $n++) {
|
||||
$pkgs{$paths[$n]} =
|
||||
{ active => $active[$n]
|
||||
, priority => $priority[$n] };
|
||||
my @pkgs;
|
||||
my @derivations = split ' ', $ENV{"derivations"};
|
||||
while (scalar @derivations) {
|
||||
my $active = shift @derivations;
|
||||
my $priority = shift @derivations;
|
||||
my $outputs = shift @derivations;
|
||||
for (my $n = 0; $n < $outputs; $n++) {
|
||||
my $path = shift @derivations;
|
||||
push @pkgs,
|
||||
{ path => $path
|
||||
, active => $active ne "false"
|
||||
, priority => int($priority) };
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
# Symlink to the packages that have been installed explicitly by the
|
||||
# user.
|
||||
foreach my $pkg (sort (keys %pkgs)) {
|
||||
# user. Process in priority order to reduce unnecessary
|
||||
# symlink/unlink steps.
|
||||
@pkgs = sort { $a->{priority} <=> $b->{priority} || $a->{path} cmp $b->{path} } @pkgs;
|
||||
foreach my $pkg (@pkgs) {
|
||||
#print $pkg, " ", $pkgs{$pkg}->{priority}, "\n";
|
||||
addPkg($pkg, $pkgs{$pkg}->{priority}) if $pkgs{$pkg}->{active} ne "false";
|
||||
addPkg($pkg->{path}, $pkg->{priority}) if $pkg->{active};
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -6,8 +6,11 @@ in {
|
||||
perl = "@perl@";
|
||||
shell = "@shell@";
|
||||
coreutils = "@coreutils@";
|
||||
bzip2 = fromEnv "NIX_BZIP2" "@bzip2@";
|
||||
bzip2 = "@bzip2@";
|
||||
gzip = "@gzip@";
|
||||
xz = "@xz@";
|
||||
tar = "@tar@";
|
||||
tarFlags = "@tarFlags@";
|
||||
tr = "@tr@";
|
||||
curl = "@curl@";
|
||||
nixBinDir = fromEnv "NIX_BIN_DIR" "@bindir@";
|
||||
|
||||
@@ -6,7 +6,7 @@ drvAttrs @ { outputs ? [ "out" ], ... }:
|
||||
let
|
||||
|
||||
strict = derivationStrict drvAttrs;
|
||||
|
||||
|
||||
commonAttrs = drvAttrs // (builtins.listToAttrs outputsList) //
|
||||
{ all = map (x: x.value) outputsList;
|
||||
inherit drvAttrs;
|
||||
@@ -21,7 +21,7 @@ let
|
||||
inherit outputName;
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
outputsList = map outputToAttrListElement outputs;
|
||||
|
||||
|
||||
in (builtins.head outputsList).value
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
with import <nix/config.nix>;
|
||||
|
||||
{system ? builtins.currentSystem, url, outputHash ? "", outputHashAlgo ? "", md5 ? "", sha1 ? "", sha256 ? ""}:
|
||||
{system ? builtins.currentSystem, url, outputHash ? "", outputHashAlgo ? "", md5 ? "", sha1 ? "", sha256 ? "", executable ? false}:
|
||||
|
||||
assert (outputHash != "" && outputHashAlgo != "")
|
||||
|| md5 != "" || sha1 != "" || sha256 != "";
|
||||
@@ -8,10 +8,10 @@ assert (outputHash != "" && outputHashAlgo != "")
|
||||
let
|
||||
|
||||
builder = builtins.toFile "fetchurl.sh"
|
||||
''
|
||||
(''
|
||||
echo "downloading $url into $out"
|
||||
${curl} --fail --location --max-redirs 20 --insecure "$url" > "$out"
|
||||
'';
|
||||
'' + (if executable then "${coreutils}/chmod +x $out" else ""));
|
||||
|
||||
in
|
||||
|
||||
@@ -25,6 +25,7 @@ derivation {
|
||||
if sha256 != "" then "sha256" else if sha1 != "" then "sha1" else "md5";
|
||||
outputHash = if outputHash != "" then outputHash else
|
||||
if sha256 != "" then sha256 else if sha1 != "" then sha1 else md5;
|
||||
outputHashMode = if executable then "recursive" else "flat";
|
||||
|
||||
inherit system url;
|
||||
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
attrs @ { drvPath, outputs, ... }:
|
||||
|
||||
let
|
||||
|
||||
commonAttrs = (builtins.listToAttrs outputsList) //
|
||||
{ all = map (x: x.value) outputsList;
|
||||
inherit drvPath;
|
||||
type = "derivation";
|
||||
};
|
||||
|
||||
outputToAttrListElement = outputName:
|
||||
{ name = outputName;
|
||||
value = commonAttrs // {
|
||||
outPath = builtins.getAttr outputName attrs;
|
||||
inherit outputName;
|
||||
};
|
||||
};
|
||||
|
||||
outputsList = map outputToAttrListElement outputs;
|
||||
|
||||
in (builtins.head outputsList).value
|
||||
+21
-6
@@ -6,29 +6,44 @@ let
|
||||
''
|
||||
export PATH=${nixBinDir}:${coreutils}
|
||||
|
||||
if [ $compressionType = xz ]; then
|
||||
ext=.xz
|
||||
compressor="| ${xz} -7"
|
||||
elif [ $compressionType = bzip2 ]; then
|
||||
ext=.bz2
|
||||
compressor="| ${bzip2}"
|
||||
else
|
||||
ext=
|
||||
compressor=
|
||||
fi
|
||||
|
||||
echo "packing ‘$storePath’..."
|
||||
mkdir $out
|
||||
dst=$out/tmp.nar.bz2
|
||||
dst=$out/tmp.nar$ext
|
||||
|
||||
set -o pipefail
|
||||
nix-store --dump "$storePath" | ${bzip2} > $dst
|
||||
eval "nix-store --dump \"$storePath\" $compressor > $dst"
|
||||
|
||||
nix-hash --flat --type $hashAlgo --base32 $dst > $out/narbz2-hash
|
||||
hash=$(nix-hash --flat --type $hashAlgo --base32 $dst)
|
||||
echo -n $hash > $out/nar-compressed-hash
|
||||
|
||||
mv $out/tmp.nar.bz2 $out/$(cat $out/narbz2-hash).nar.bz2
|
||||
mv $dst $out/$hash.nar$ext
|
||||
'';
|
||||
|
||||
in
|
||||
|
||||
{ storePath, hashAlgo }:
|
||||
{ storePath, hashAlgo, compressionType }:
|
||||
|
||||
derivation {
|
||||
name = "nar";
|
||||
system = builtins.currentSystem;
|
||||
builder = shell;
|
||||
args = [ "-e" builder ];
|
||||
inherit storePath hashAlgo;
|
||||
inherit storePath hashAlgo compressionType;
|
||||
|
||||
# Don't build in a chroot because Nix's dependencies may not be there.
|
||||
__noChroot = true;
|
||||
|
||||
# Remote machines may not have ${nixBinDir} or ${coreutils} in the same prefixes
|
||||
preferLocalBuild = true;
|
||||
}
|
||||
|
||||
@@ -1,14 +1,39 @@
|
||||
with import <nix/config.nix>;
|
||||
|
||||
{ name, channelName, src }:
|
||||
let
|
||||
|
||||
builder = builtins.toFile "unpack-channel.sh"
|
||||
''
|
||||
mkdir $out
|
||||
cd $out
|
||||
xzpat="\.xz\$"
|
||||
gzpat="\.gz\$"
|
||||
if [[ "$src" =~ $xzpat ]]; then
|
||||
${xz} -d < $src | ${tar} xf - ${tarFlags}
|
||||
elif [[ "$src" =~ $gzpat ]]; then
|
||||
${gzip} -d < $src | ${tar} xf - ${tarFlags}
|
||||
else
|
||||
${bzip2} -d < $src | ${tar} xf - ${tarFlags}
|
||||
fi
|
||||
mv * $out/$channelName
|
||||
if [ -n "$binaryCacheURL" ]; then
|
||||
mkdir $out/binary-caches
|
||||
echo -n "$binaryCacheURL" > $out/binary-caches/$channelName
|
||||
fi
|
||||
'';
|
||||
|
||||
in
|
||||
|
||||
{ name, channelName, src, binaryCacheURL ? "" }:
|
||||
|
||||
derivation {
|
||||
system = builtins.currentSystem;
|
||||
builder = shell;
|
||||
args = [ "-e" ./unpack-channel.sh ];
|
||||
inherit name channelName src bzip2 tar tr;
|
||||
args = [ "-e" builder ];
|
||||
inherit name channelName src binaryCacheURL;
|
||||
|
||||
PATH = "${nixBinDir}:${coreutils}";
|
||||
|
||||
|
||||
# No point in doing this remotely.
|
||||
preferLocalBuild = true;
|
||||
|
||||
|
||||
@@ -1,4 +0,0 @@
|
||||
mkdir $out
|
||||
cd $out
|
||||
$bzip2 -d < $src | $tar xf -
|
||||
mv * $out/$channelName
|
||||
@@ -0,0 +1,17 @@
|
||||
#! /bin/sh
|
||||
if [ -e tests/test-tmp ]; then
|
||||
chmod -R u+w tests/test-tmp
|
||||
rm -rf tests/test-tmp
|
||||
fi
|
||||
|
||||
s=$(type -p nix-shell)
|
||||
exec $s release.nix -A tarball --command "
|
||||
export NIX_REMOTE=daemon
|
||||
export NIX_PATH='$NIX_PATH'
|
||||
export NIX_BUILD_SHELL=$(type -p bash)
|
||||
export c=\$configureFlags
|
||||
exec $s release.nix -A build.x86_64-linux --exclude tarball --command '
|
||||
configureFlags+=\" \$c --prefix=$(pwd)/inst --sysconfdir=$(pwd)/inst/etc\"
|
||||
return
|
||||
'" \
|
||||
"$@"
|
||||
@@ -1,33 +0,0 @@
|
||||
To produce a `stable' release from the trunk:
|
||||
|
||||
-1. Update the release notes; make sure that the release date is
|
||||
correct.
|
||||
|
||||
0. Make sure that the trunk builds in the release supervisor.
|
||||
|
||||
1. Branch the trunk, e.g., `svn cp .../trunk
|
||||
.../branches/0.5-release'.
|
||||
|
||||
2. Switch to the branch, e.g., `svn switch .../branches/0.5-release'.
|
||||
|
||||
3. In `configure.ac', change `STABLE=0' into `STABLE=1' and commit.
|
||||
|
||||
4. In the release supervisor, add a one-time job to build
|
||||
`.../branches/0.5-release'.
|
||||
|
||||
5. Make sure that the release succeeds.
|
||||
|
||||
6. Move the branch to a tag, e.g., `svn mv .../branches/0.5-release
|
||||
.../tags/0.5'.
|
||||
|
||||
Note that the branch should not be used for maintenance; it should
|
||||
be deleted after the release has been created. A maintenance
|
||||
branch (e.g., `.../branches/0.5') should be created from the
|
||||
original revision of the trunk (since maintenance releases should
|
||||
also be tested first; hence, we cannot have `STABLE=1'). The same
|
||||
procedure can then be followed to produce maintenance releases;
|
||||
just substitute `.../branches/VERSION' for the trunk.
|
||||
|
||||
7. Switch back to the trunk.
|
||||
|
||||
8. Bump the version number in `configure.ac' (in AC_INIT).
|
||||
@@ -16,14 +16,14 @@ dblatex_opts = \
|
||||
# Note: we use GIF for now, since the PNGs shipped with Docbook aren't
|
||||
# transparent.
|
||||
|
||||
man1_MANS = nix-env.1 nix-build.1 nix-store.1 nix-instantiate.1 \
|
||||
man1_MANS = nix-env.1 nix-build.1 nix-shell.1 nix-store.1 nix-instantiate.1 \
|
||||
nix-collect-garbage.1 nix-push.1 nix-pull.1 \
|
||||
nix-prefetch-url.1 nix-channel.1 \
|
||||
nix-install-package.1 nix-hash.1 nix-copy-closure.1
|
||||
|
||||
man5_MANS = nix.conf.5
|
||||
|
||||
man8_MANS = nix-worker.8
|
||||
man8_MANS = nix-daemon.8
|
||||
|
||||
FIGURES = figures/user-environments.png
|
||||
|
||||
|
||||
+113
-57
@@ -19,7 +19,7 @@ is also available as <function>builtins.derivation</function>.</para>
|
||||
|
||||
<variablelist>
|
||||
|
||||
|
||||
|
||||
<varlistentry><term><function>abort</function> <replaceable>s</replaceable></term>
|
||||
|
||||
<listitem><para>Abort Nix expression evaluation, print error
|
||||
@@ -27,7 +27,7 @@ is also available as <function>builtins.derivation</function>.</para>
|
||||
|
||||
</varlistentry>
|
||||
|
||||
|
||||
|
||||
<varlistentry><term><function>builtins.add</function>
|
||||
<replaceable>e1</replaceable> <replaceable>e2</replaceable></term>
|
||||
|
||||
@@ -37,7 +37,7 @@ is also available as <function>builtins.derivation</function>.</para>
|
||||
|
||||
</varlistentry>
|
||||
|
||||
|
||||
|
||||
<varlistentry><term><function>builtins.attrNames</function>
|
||||
<replaceable>attrs</replaceable></term>
|
||||
|
||||
@@ -55,7 +55,7 @@ attrValues = attrs: map (name: builtins.getAttr name attrs) (builtins.attrNames
|
||||
|
||||
</varlistentry>
|
||||
|
||||
|
||||
|
||||
<varlistentry><term><function>baseNameOf</function> <replaceable>s</replaceable></term>
|
||||
|
||||
<listitem><para>Return the <emphasis>base name</emphasis> of the
|
||||
@@ -65,7 +65,7 @@ attrValues = attrs: map (name: builtins.getAttr name attrs) (builtins.attrNames
|
||||
|
||||
</varlistentry>
|
||||
|
||||
|
||||
|
||||
<varlistentry><term><varname>builtins</varname></term>
|
||||
|
||||
<listitem><para>The attribute set <varname>builtins</varname>
|
||||
@@ -82,7 +82,7 @@ if builtins ? getEnv then builtins.getEnv "PATH" else ""</programlisting>
|
||||
|
||||
</varlistentry>
|
||||
|
||||
|
||||
|
||||
<varlistentry><term><function>builtins.compareVersions</function>
|
||||
<replaceable>s1</replaceable> <replaceable>s2</replaceable></term>
|
||||
|
||||
@@ -99,7 +99,16 @@ if builtins ? getEnv then builtins.getEnv "PATH" else ""</programlisting>
|
||||
|
||||
</varlistentry>
|
||||
|
||||
|
||||
|
||||
<varlistentry><term><function>builtins.concatLists</function>
|
||||
<replaceable>lists</replaceable></term>
|
||||
|
||||
<listitem><para>Concatenate a list of lists into a single
|
||||
list.</para></listitem>
|
||||
|
||||
</varlistentry>
|
||||
|
||||
|
||||
<varlistentry
|
||||
xml:id='builtin-currentSystem'><term><varname>builtins.currentSystem</varname></term>
|
||||
|
||||
@@ -124,7 +133,7 @@ if builtins ? getEnv then builtins.getEnv "PATH" else ""</programlisting>
|
||||
</varlistentry>
|
||||
-->
|
||||
|
||||
|
||||
|
||||
<!--
|
||||
<varlistentry><term><function>dependencyClosure</function></term>
|
||||
|
||||
@@ -133,7 +142,7 @@ if builtins ? getEnv then builtins.getEnv "PATH" else ""</programlisting>
|
||||
</varlistentry>
|
||||
-->
|
||||
|
||||
|
||||
|
||||
<varlistentry><term><function>derivation</function>
|
||||
<replaceable>attrs</replaceable></term>
|
||||
|
||||
@@ -152,7 +161,7 @@ if builtins ? getEnv then builtins.getEnv "PATH" else ""</programlisting>
|
||||
|
||||
</varlistentry>
|
||||
|
||||
|
||||
|
||||
<varlistentry><term><function>builtins.div</function>
|
||||
<replaceable>e1</replaceable> <replaceable>e2</replaceable></term>
|
||||
|
||||
@@ -162,7 +171,40 @@ if builtins ? getEnv then builtins.getEnv "PATH" else ""</programlisting>
|
||||
|
||||
</varlistentry>
|
||||
|
||||
|
||||
|
||||
<varlistentry><term><function>builtins.elem</function>
|
||||
<replaceable>x</replaceable> <replaceable>xs</replaceable></term>
|
||||
|
||||
<listitem><para>Return <literal>true</literal> if a value equal to
|
||||
<replaceable>x</replaceable> occurs in the list
|
||||
<replaceable>xs</replaceable>, and <literal>false</literal>
|
||||
otherwise.</para></listitem>
|
||||
|
||||
</varlistentry>
|
||||
|
||||
|
||||
<varlistentry><term><function>builtins.elemAt</function>
|
||||
<replaceable>xs</replaceable> <replaceable>n</replaceable></term>
|
||||
|
||||
<listitem><para>Return element <replaceable>n</replaceable> from
|
||||
the list <replaceable>xs</replaceable>. Elements are counted
|
||||
starting from 0. A fatal error occurs in the index is out of
|
||||
bounds.</para></listitem>
|
||||
|
||||
</varlistentry>
|
||||
|
||||
|
||||
<varlistentry><term><function>builtins.filter</function>
|
||||
<replaceable>f</replaceable> <replaceable>xs</replaceable></term>
|
||||
|
||||
<listitem><para>Return a list consisting of the elements of
|
||||
<replaceable>xs</replaceable> for which the function
|
||||
<replaceable>f</replaceable> returns
|
||||
<literal>true</literal>.</para></listitem>
|
||||
|
||||
</varlistentry>
|
||||
|
||||
|
||||
<varlistentry><term><function>builtins.filterSource</function>
|
||||
<replaceable>e1</replaceable> <replaceable>e2</replaceable></term>
|
||||
|
||||
@@ -214,7 +256,7 @@ stdenv.mkDerivation {
|
||||
|
||||
</varlistentry>
|
||||
|
||||
|
||||
|
||||
<varlistentry><term><function>builtins.getAttr</function>
|
||||
<replaceable>s</replaceable> <replaceable>attrs</replaceable></term>
|
||||
|
||||
@@ -227,7 +269,7 @@ stdenv.mkDerivation {
|
||||
|
||||
</varlistentry>
|
||||
|
||||
|
||||
|
||||
<varlistentry><term><function>builtins.getEnv</function>
|
||||
<replaceable>s</replaceable></term>
|
||||
|
||||
@@ -245,7 +287,7 @@ stdenv.mkDerivation {
|
||||
|
||||
</varlistentry>
|
||||
|
||||
|
||||
|
||||
<varlistentry><term><function>builtins.hasAttr</function>
|
||||
<replaceable>s</replaceable> <replaceable>attrs</replaceable></term>
|
||||
|
||||
@@ -259,7 +301,19 @@ stdenv.mkDerivation {
|
||||
|
||||
</varlistentry>
|
||||
|
||||
|
||||
|
||||
<varlistentry><term><function>builtins.hashString</function>
|
||||
<replaceable>type</replaceable> <replaceable>s</replaceable></term>
|
||||
|
||||
<listitem><para>Return a base-16 representation of the
|
||||
cryptographic hash of string <replaceable>s</replaceable>. The
|
||||
hash algorithm specified by <replaceable>type</replaceable> must
|
||||
be one of <literal>"md5"</literal>, <literal>"sha1"</literal> or
|
||||
<literal>"sha256"</literal>.</para></listitem>
|
||||
|
||||
</varlistentry>
|
||||
|
||||
|
||||
<varlistentry><term><function>builtins.head</function>
|
||||
<replaceable>list</replaceable></term>
|
||||
|
||||
@@ -270,13 +324,15 @@ stdenv.mkDerivation {
|
||||
|
||||
</varlistentry>
|
||||
|
||||
|
||||
|
||||
<varlistentry><term><function>import</function>
|
||||
<replaceable>path</replaceable></term>
|
||||
|
||||
<listitem><para>Load, parse and return the Nix expression in the
|
||||
file <replaceable>path</replaceable>. Evaluation aborts if the
|
||||
file doesn’t exist or contains an incorrect Nix
|
||||
file <replaceable>path</replaceable>. If <replaceable>path
|
||||
</replaceable> is a directory, the file <filename>default.nix
|
||||
</filename> in that directory is loaded. Evaluation aborts if
|
||||
the file doesn’t exist or contains an incorrect Nix
|
||||
expression. <function>import</function> implements Nix’s module
|
||||
system: you can put any Nix expression (such as an attribute set
|
||||
or a function) in a separate file, and use it from Nix expressions
|
||||
@@ -288,7 +344,7 @@ stdenv.mkDerivation {
|
||||
built-in). Therefore, it cannot refer to variables that are in
|
||||
scope at the call site. For instance, if you have a calling
|
||||
expression
|
||||
|
||||
|
||||
<programlisting>
|
||||
rec {
|
||||
x = 123;
|
||||
@@ -323,7 +379,7 @@ x: x + 456</programlisting>
|
||||
|
||||
</varlistentry>
|
||||
|
||||
|
||||
|
||||
<varlistentry><term><function>builtins.intersectAttrs</function>
|
||||
<replaceable>e1</replaceable> <replaceable>e2</replaceable></term>
|
||||
|
||||
@@ -332,8 +388,8 @@ x: x + 456</programlisting>
|
||||
exist in the set <replaceable>e1</replaceable>.</para></listitem>
|
||||
|
||||
</varlistentry>
|
||||
|
||||
|
||||
|
||||
|
||||
<varlistentry><term><function>builtins.isAttrs</function>
|
||||
<replaceable>e</replaceable></term>
|
||||
|
||||
@@ -343,7 +399,7 @@ x: x + 456</programlisting>
|
||||
|
||||
</varlistentry>
|
||||
|
||||
|
||||
|
||||
<varlistentry><term><function>builtins.isList</function>
|
||||
<replaceable>e</replaceable></term>
|
||||
|
||||
@@ -353,7 +409,7 @@ x: x + 456</programlisting>
|
||||
|
||||
</varlistentry>
|
||||
|
||||
|
||||
|
||||
<varlistentry><term><function>builtins.isFunction</function>
|
||||
<replaceable>e</replaceable></term>
|
||||
|
||||
@@ -363,7 +419,7 @@ x: x + 456</programlisting>
|
||||
|
||||
</varlistentry>
|
||||
|
||||
|
||||
|
||||
<varlistentry><term><function>builtins.isString</function>
|
||||
<replaceable>e</replaceable></term>
|
||||
|
||||
@@ -373,17 +429,17 @@ x: x + 456</programlisting>
|
||||
|
||||
</varlistentry>
|
||||
|
||||
|
||||
|
||||
<varlistentry><term><function>builtins.isInt</function>
|
||||
<replaceable>e</replaceable></term>
|
||||
|
||||
<listitem><para>Return <literal>true</literal> if
|
||||
<replaceable>e</replaceable> evaluates to a int, and
|
||||
<replaceable>e</replaceable> evaluates to an int, and
|
||||
<literal>false</literal> otherwise.</para></listitem>
|
||||
|
||||
</varlistentry>
|
||||
|
||||
|
||||
|
||||
<varlistentry><term><function>builtins.isBool</function>
|
||||
<replaceable>e</replaceable></term>
|
||||
|
||||
@@ -393,7 +449,7 @@ x: x + 456</programlisting>
|
||||
|
||||
</varlistentry>
|
||||
|
||||
|
||||
|
||||
<varlistentry><term><function>isNull</function>
|
||||
<replaceable>e</replaceable></term>
|
||||
|
||||
@@ -403,12 +459,12 @@ x: x + 456</programlisting>
|
||||
|
||||
<warning><para>This function is <emphasis>deprecated</emphasis>;
|
||||
just write <literal>e == null</literal> instead.</para></warning>
|
||||
|
||||
|
||||
</listitem>
|
||||
|
||||
</varlistentry>
|
||||
|
||||
|
||||
|
||||
<varlistentry><term><function>builtins.length</function>
|
||||
<replaceable>e</replaceable></term>
|
||||
|
||||
@@ -417,7 +473,7 @@ x: x + 456</programlisting>
|
||||
|
||||
</varlistentry>
|
||||
|
||||
|
||||
|
||||
<varlistentry><term><function>builtins.lessThan</function>
|
||||
<replaceable>e1</replaceable> <replaceable>e2</replaceable></term>
|
||||
|
||||
@@ -430,7 +486,7 @@ x: x + 456</programlisting>
|
||||
|
||||
</varlistentry>
|
||||
|
||||
|
||||
|
||||
<varlistentry><term><function>builtins.listToAttrs</function>
|
||||
<replaceable>e</replaceable></term>
|
||||
|
||||
@@ -457,7 +513,7 @@ builtins.listToAttrs
|
||||
</para></listitem>
|
||||
|
||||
</varlistentry>
|
||||
|
||||
|
||||
<varlistentry><term><function>map</function>
|
||||
<replaceable>f</replaceable> <replaceable>list</replaceable></term>
|
||||
|
||||
@@ -470,10 +526,10 @@ map (x: "foo" + x) [ "bar" "bla" "abc" ]</programlisting>
|
||||
|
||||
evaluates to <literal>[ "foobar" "foobla" "fooabc"
|
||||
]</literal>.</para></listitem>
|
||||
|
||||
|
||||
</varlistentry>
|
||||
|
||||
|
||||
|
||||
<varlistentry><term><function>builtins.mul</function>
|
||||
<replaceable>e1</replaceable> <replaceable>e2</replaceable></term>
|
||||
|
||||
@@ -483,7 +539,7 @@ map (x: "foo" + x) [ "bar" "bla" "abc" ]</programlisting>
|
||||
|
||||
</varlistentry>
|
||||
|
||||
|
||||
|
||||
<varlistentry><term><function>builtins.parseDrvName</function>
|
||||
<replaceable>s</replaceable></term>
|
||||
|
||||
@@ -498,7 +554,7 @@ map (x: "foo" + x) [ "bar" "bla" "abc" ]</programlisting>
|
||||
|
||||
</varlistentry>
|
||||
|
||||
|
||||
|
||||
<varlistentry><term><function>builtins.pathExists</function>
|
||||
<replaceable>path</replaceable></term>
|
||||
|
||||
@@ -531,7 +587,7 @@ in config.someSetting</programlisting>
|
||||
</varlistentry>
|
||||
-->
|
||||
|
||||
|
||||
|
||||
<varlistentry><term><function>builtins.readFile</function>
|
||||
<replaceable>path</replaceable></term>
|
||||
|
||||
@@ -539,8 +595,8 @@ in config.someSetting</programlisting>
|
||||
<replaceable>path</replaceable> as a string.</para></listitem>
|
||||
|
||||
</varlistentry>
|
||||
|
||||
|
||||
|
||||
|
||||
<varlistentry><term><function>removeAttrs</function>
|
||||
<replaceable>attrs</replaceable> <replaceable>list</replaceable></term>
|
||||
|
||||
@@ -556,7 +612,7 @@ removeAttrs { x = 1; y = 2; z = 3; } [ "a" "x" "z" ]</screen>
|
||||
|
||||
</varlistentry>
|
||||
|
||||
|
||||
|
||||
<varlistentry><term><function>builtins.stringLength</function>
|
||||
<replaceable>e</replaceable></term>
|
||||
|
||||
@@ -566,7 +622,7 @@ removeAttrs { x = 1; y = 2; z = 3; } [ "a" "x" "z" ]</screen>
|
||||
|
||||
</varlistentry>
|
||||
|
||||
|
||||
|
||||
<varlistentry><term><function>builtins.sub</function>
|
||||
<replaceable>e1</replaceable> <replaceable>e2</replaceable></term>
|
||||
|
||||
@@ -576,7 +632,7 @@ removeAttrs { x = 1; y = 2; z = 3; } [ "a" "x" "z" ]</screen>
|
||||
|
||||
</varlistentry>
|
||||
|
||||
|
||||
|
||||
<varlistentry><term><function>builtins.substring</function>
|
||||
<replaceable>start</replaceable> <replaceable>len</replaceable>
|
||||
<replaceable>s</replaceable></term>
|
||||
@@ -594,7 +650,7 @@ removeAttrs { x = 1; y = 2; z = 3; } [ "a" "x" "z" ]</screen>
|
||||
|
||||
</varlistentry>
|
||||
|
||||
|
||||
|
||||
<varlistentry><term><function>builtins.tail</function>
|
||||
<replaceable>list</replaceable></term>
|
||||
|
||||
@@ -604,7 +660,7 @@ removeAttrs { x = 1; y = 2; z = 3; } [ "a" "x" "z" ]</screen>
|
||||
|
||||
</varlistentry>
|
||||
|
||||
|
||||
|
||||
<varlistentry><term><function>throw</function>
|
||||
<replaceable>s</replaceable></term>
|
||||
|
||||
@@ -618,7 +674,7 @@ removeAttrs { x = 1; y = 2; z = 3; } [ "a" "x" "z" ]</screen>
|
||||
|
||||
</varlistentry>
|
||||
|
||||
|
||||
|
||||
<varlistentry
|
||||
xml:id='builtin-toFile'><term><function>builtins.toFile</function>
|
||||
<replaceable>name</replaceable> <replaceable>s</replaceable></term>
|
||||
@@ -636,7 +692,7 @@ removeAttrs { x = 1; y = 2; z = 3; } [ "a" "x" "z" ]</screen>
|
||||
|
||||
stdenv.mkDerivation {
|
||||
name = "hello-2.1.1";
|
||||
|
||||
|
||||
builder = builtins.toFile "builder.sh" "
|
||||
source $stdenv/setup
|
||||
|
||||
@@ -655,7 +711,7 @@ stdenv.mkDerivation {
|
||||
};
|
||||
inherit perl;
|
||||
}</programlisting>
|
||||
|
||||
|
||||
</para>
|
||||
|
||||
<para>It is even possible for one file to refer to another, e.g.,
|
||||
@@ -693,7 +749,7 @@ in foo</programlisting>
|
||||
|
||||
</varlistentry>
|
||||
|
||||
|
||||
|
||||
<varlistentry><term><function>builtins.toPath</function> <replaceable>s</replaceable></term>
|
||||
|
||||
<listitem><para>Convert the string value
|
||||
@@ -706,7 +762,7 @@ in foo</programlisting>
|
||||
|
||||
</varlistentry>
|
||||
|
||||
|
||||
|
||||
<varlistentry><term><function>toString</function> <replaceable>e</replaceable></term>
|
||||
|
||||
<listitem><para>Convert the expression
|
||||
@@ -718,7 +774,7 @@ in foo</programlisting>
|
||||
|
||||
</varlistentry>
|
||||
|
||||
|
||||
|
||||
<varlistentry xml:id='builtin-toXML'><term><function>builtins.toXML</function> <replaceable>e</replaceable></term>
|
||||
|
||||
<listitem><para>Return a string containing an XML representation
|
||||
@@ -763,7 +819,7 @@ in foo</programlisting>
|
||||
|
||||
<example xml:id='ex-toxml'><title>Passing information to a builder
|
||||
using <function>toXML</function></title>
|
||||
|
||||
|
||||
<programlisting><![CDATA[
|
||||
{ stdenv, fetchurl, libxslt, jira, uberwiki }:
|
||||
|
||||
@@ -771,7 +827,7 @@ stdenv.mkDerivation (rec {
|
||||
name = "web-server";
|
||||
|
||||
buildInputs = [ libxslt ];
|
||||
|
||||
|
||||
builder = builtins.toFile "builder.sh" "
|
||||
source $stdenv/setup
|
||||
mkdir $out
|
||||
@@ -804,7 +860,7 @@ stdenv.mkDerivation (rec {
|
||||
|
||||
<example xml:id='ex-toxml-result'><title>XML representation produced by
|
||||
<function>toXML</function></title>
|
||||
|
||||
|
||||
<programlisting><![CDATA[<?xml version='1.0' encoding='utf-8'?>
|
||||
<expr>
|
||||
<list>
|
||||
@@ -833,7 +889,7 @@ stdenv.mkDerivation (rec {
|
||||
|
||||
</varlistentry>
|
||||
|
||||
|
||||
|
||||
<varlistentry><term><function>builtins.trace</function>
|
||||
<replaceable>e1</replaceable> <replaceable>e2</replaceable></term>
|
||||
|
||||
@@ -844,7 +900,7 @@ stdenv.mkDerivation (rec {
|
||||
|
||||
</varlistentry>
|
||||
|
||||
|
||||
|
||||
</variablelist>
|
||||
|
||||
|
||||
|
||||
+155
-42
@@ -30,18 +30,21 @@ gc-keep-derivations = true # Idem
|
||||
env-keep-derivations = false
|
||||
</programlisting>
|
||||
|
||||
<para>The following settings are currently available:
|
||||
<para>You can override settings using the <option>--option</option>
|
||||
flag, e.g. <literal>--option gc-keep-outputs false</literal>.</para>
|
||||
|
||||
<para>The following settings are currently available:
|
||||
|
||||
<variablelist>
|
||||
|
||||
|
||||
|
||||
<varlistentry xml:id="conf-gc-keep-outputs"><term><literal>gc-keep-outputs</literal></term>
|
||||
|
||||
<listitem><para>If <literal>true</literal>, the garbage collector
|
||||
will keep the outputs of non-garbage derivations. If
|
||||
<literal>false</literal> (default), outputs will be deleted unless
|
||||
they are GC roots themselves (or reachable from other roots).</para>
|
||||
|
||||
|
||||
<para>In general, outputs must be registered as roots separately.
|
||||
However, even if the output of a derivation is registered as a
|
||||
root, the collector will still delete store paths that are used
|
||||
@@ -50,7 +53,7 @@ env-keep-derivations = false
|
||||
this option to <literal>true</literal>.</para></listitem>
|
||||
|
||||
</varlistentry>
|
||||
|
||||
|
||||
|
||||
<varlistentry xml:id="conf-gc-keep-derivations"><term><literal>gc-keep-derivations</literal></term>
|
||||
|
||||
@@ -68,7 +71,7 @@ env-keep-derivations = false
|
||||
|
||||
</varlistentry>
|
||||
|
||||
|
||||
|
||||
<varlistentry><term><literal>env-keep-derivations</literal></term>
|
||||
|
||||
<listitem><para>If <literal>false</literal> (default), derivations
|
||||
@@ -92,14 +95,14 @@ env-keep-derivations = false
|
||||
|
||||
</varlistentry>
|
||||
|
||||
|
||||
|
||||
<varlistentry xml:id="conf-build-max-jobs"><term><literal>build-max-jobs</literal></term>
|
||||
|
||||
<listitem><para>This option defines the maximum number of jobs
|
||||
that Nix will try to build in parallel. The default is
|
||||
<literal>1</literal>. You should generally set it to the number
|
||||
of CPUs in your system (e.g., <literal>2</literal> on a Athlon 64
|
||||
X2). It can be overriden using the <option
|
||||
of CPUs in your system (e.g., <literal>2</literal> on an Athlon 64
|
||||
X2). It can be overridden using the <option
|
||||
linkend='opt-max-jobs'>--max-jobs</option> (<option>-j</option>)
|
||||
command line switch.</para></listitem>
|
||||
|
||||
@@ -116,7 +119,7 @@ env-keep-derivations = false
|
||||
<varname>enableParallelBuilding</varname> is set to
|
||||
<literal>true</literal>, the builder passes the
|
||||
<option>-j<replaceable>N</replaceable></option> flag to GNU Make.
|
||||
It can be overriden using the <option
|
||||
It can be overridden using the <option
|
||||
linkend='opt-cores'>--cores</option> command line switch and
|
||||
defaults to <literal>1</literal>. The value <literal>0</literal>
|
||||
means that the builder should use all available CPU cores in the
|
||||
@@ -131,10 +134,10 @@ env-keep-derivations = false
|
||||
|
||||
<para>This option defines the maximum number of seconds that a
|
||||
builder can go without producing any data on standard output or
|
||||
standard error. This is useful (for instance in a automated
|
||||
standard error. This is useful (for instance in an automated
|
||||
build system) to catch builds that are stuck in an infinite
|
||||
loop, or to catch remote builds that are hanging due to network
|
||||
problems. It can be overriden using the <option
|
||||
problems. It can be overridden using the <option
|
||||
linkend="opt-max-silent-time">--max-silent-time</option> command
|
||||
line switch.</para>
|
||||
|
||||
@@ -145,15 +148,16 @@ env-keep-derivations = false
|
||||
|
||||
</varlistentry>
|
||||
|
||||
|
||||
<varlistentry xml:id="conf-build-timeout"><term><literal>build-timeout</literal></term>
|
||||
|
||||
<listitem>
|
||||
|
||||
<para>This option defines the maximum number of seconds that a
|
||||
builder can run. This is useful (for instance in a automated
|
||||
builder can run. This is useful (for instance in an automated
|
||||
build system) to catch builds that are stuck in an infinite loop
|
||||
but keep writing to their standard output or standard error. It
|
||||
can be overriden using the <option
|
||||
can be overridden using the <option
|
||||
linkend="opt-timeout">--timeout</option> command line
|
||||
switch.</para>
|
||||
|
||||
@@ -165,6 +169,20 @@ env-keep-derivations = false
|
||||
</varlistentry>
|
||||
|
||||
|
||||
<varlistentry xml:id="conf-build-max-log-size"><term><literal>build-max-log-size</literal></term>
|
||||
|
||||
<listitem>
|
||||
|
||||
<para>This option defines the maximum number of bytes that a
|
||||
builder can write to its stdout/stderr. If the builder exceeds
|
||||
this limit, it’s killed. A value of <literal>0</literal> (the
|
||||
default) means that there is no limit.</para>
|
||||
|
||||
</listitem>
|
||||
|
||||
</varlistentry>
|
||||
|
||||
|
||||
<varlistentry xml:id="conf-build-users-group"><term><literal>build-users-group</literal></term>
|
||||
|
||||
<listitem><para>This options specifies the Unix group containing
|
||||
@@ -198,10 +216,8 @@ env-keep-derivations = false
|
||||
under the uid of the Nix process (that is, the uid of the caller
|
||||
if <envar>NIX_REMOTE</envar> is empty, the uid under which the Nix
|
||||
daemon runs if <envar>NIX_REMOTE</envar> is
|
||||
<literal>daemon</literal>, or the uid that owns the setuid
|
||||
<command>nix-worker</command> program if <envar>NIX_REMOTE</envar>
|
||||
is <literal>slave</literal>). Obviously, this should not be used
|
||||
in multi-user settings with untrusted users.</para>
|
||||
<literal>daemon</literal>). Obviously, this should not be used in
|
||||
multi-user settings with untrusted users.</para>
|
||||
|
||||
</listitem>
|
||||
|
||||
@@ -233,7 +249,27 @@ env-keep-derivations = false
|
||||
|
||||
</varlistentry>
|
||||
|
||||
|
||||
|
||||
<varlistentry xml:id="conf-build-chroot-dirs"><term><literal>build-chroot-dirs</literal></term>
|
||||
|
||||
<listitem><para>When builds are performed in a chroot environment,
|
||||
Nix will mount some directories from the normal file system
|
||||
hierarchy inside the chroot. These are the Nix store, the
|
||||
temporary build directory (usually
|
||||
<filename>/tmp/nix-build-<replaceable>drvname</replaceable>-<replaceable>number</replaceable></filename>),
|
||||
the <literal>/proc</literal> filesystem, and the directories
|
||||
listed here. The default is <literal>/dev /dev/pts</literal>,
|
||||
since these contain files needed by many builds (such as
|
||||
<filename>/dev/null</filename>). You can use the syntax
|
||||
<literal><replaceable>target</replaceable>=<replaceable>source</replaceable></literal>
|
||||
to mount a path in a different location in the chroot; for
|
||||
instance, <literal>/bin=/nix-bin</literal> will mount the
|
||||
directory <literal>/nix-bin</literal> as <literal>/bin</literal>
|
||||
inside the chroot.</para></listitem>
|
||||
|
||||
</varlistentry>
|
||||
|
||||
|
||||
<varlistentry><term><literal>build-use-substitutes</literal></term>
|
||||
|
||||
<listitem><para>If set to <literal>true</literal> (default), Nix
|
||||
@@ -242,34 +278,17 @@ env-keep-derivations = false
|
||||
|
||||
</varlistentry>
|
||||
|
||||
|
||||
<varlistentry xml:id="conf-build-chroot-dirs"><term><literal>build-chroot-dirs</literal></term>
|
||||
|
||||
<listitem><para>When builds are performed in a chroot environment,
|
||||
Nix will mount (using <command>mount --bind</command> on Linux)
|
||||
some directories from the normal file system hierarchy inside the
|
||||
chroot. These are the Nix store, the temporary build directory
|
||||
(usually
|
||||
<filename>/tmp/nix-<replaceable>pid</replaceable>-<replaceable>number</replaceable></filename>)
|
||||
and the directories listed here. The default is <literal>dev
|
||||
/proc</literal>. Files in <filename>/dev</filename> (such as
|
||||
<filename>/dev/null</filename>) are needed by many builds, and
|
||||
some files in <filename>/proc</filename> may also be needed
|
||||
occasionally.</para>
|
||||
<varlistentry><term><literal>build-fallback</literal></term>
|
||||
|
||||
<para>The value used on NixOS is
|
||||
|
||||
<programlisting>
|
||||
build-use-chroot = /dev /proc /bin</programlisting>
|
||||
|
||||
to make the <filename>/bin/sh</filename> symlink available (which
|
||||
is still needed by many builders).</para>
|
||||
|
||||
</listitem>
|
||||
<listitem><para>If set to <literal>true</literal>, Nix will fall
|
||||
back to building from source if a binary substitute fails. This
|
||||
is equivalent to the <option>--fallback</option> flag. The
|
||||
default is <literal>false</literal>.</para></listitem>
|
||||
|
||||
</varlistentry>
|
||||
|
||||
|
||||
|
||||
<varlistentry><term><literal>build-cache-failures</literal></term>
|
||||
|
||||
<listitem><para>If set to <literal>true</literal>, Nix will
|
||||
@@ -307,6 +326,86 @@ build-use-chroot = /dev /proc /bin</programlisting>
|
||||
</varlistentry>
|
||||
|
||||
|
||||
<varlistentry><term><literal>use-binary-caches</literal></term>
|
||||
|
||||
<listitem><para>If set to <literal>true</literal> (the default),
|
||||
Nix will check the binary caches specified by
|
||||
<option>binary-caches</option> and related options to obtain
|
||||
binary substitutes.</para></listitem>
|
||||
|
||||
</varlistentry>
|
||||
|
||||
|
||||
<varlistentry><term><literal>binary-caches</literal></term>
|
||||
|
||||
<listitem><para>A list of URLs of binary caches, separated by
|
||||
whitespace. The default is
|
||||
<literal>http://cache.nixos.org</literal>.</para></listitem>
|
||||
|
||||
</varlistentry>
|
||||
|
||||
|
||||
<varlistentry><term><literal>binary-caches-files</literal></term>
|
||||
|
||||
<listitem><para>A list of names of files that will be read to
|
||||
obtain additional binary cache URLs. The default is
|
||||
<literal>/nix/var/nix/profiles/per-user/<replaceable>username</replaceable>/channels/binary-caches/*</literal>.
|
||||
Note that when you’re using the Nix daemon,
|
||||
<replaceable>username</replaceable> is always equal to
|
||||
<literal>root</literal>, so Nix will only use the binary caches
|
||||
provided by the channels installed by root. Do not set this
|
||||
option to read files created by untrusted users!</para></listitem>
|
||||
|
||||
</varlistentry>
|
||||
|
||||
|
||||
<varlistentry><term><literal>trusted-binary-caches</literal></term>
|
||||
|
||||
<listitem><para>A list of URLs of binary caches, separated by
|
||||
whitespace. These are not used by default, but can be enabled by
|
||||
users of the Nix daemon by specifying <literal>--option
|
||||
binary-caches <replaceable>urls</replaceable></literal> on the
|
||||
command line. Unprivileged users are only allowed to pass a
|
||||
subset of the URLs listed in <literal>binary-caches</literal> and
|
||||
<literal>trusted-binary-caches</literal>.</para></listitem>
|
||||
|
||||
</varlistentry>
|
||||
|
||||
|
||||
<varlistentry><term><literal>extra-binary-caches</literal></term>
|
||||
|
||||
<listitem><para>Additional binary caches appended to those
|
||||
specified in <option>binary-caches</option> and
|
||||
<option>binary-caches-files</option>. When used by unprivileged
|
||||
users, untrusted binary caches (i.e. those not listed in
|
||||
<option>trusted-binary-caches</option>) are silently
|
||||
ignored.</para></listitem>
|
||||
|
||||
</varlistentry>
|
||||
|
||||
|
||||
<varlistentry><term><literal>binary-caches-parallel-connections</literal></term>
|
||||
|
||||
<listitem><para>The maximum number of parallel HTTP connections
|
||||
used by the binary cache substituter to get NAR info files. This
|
||||
number should be high to minimise latency. It defaults to
|
||||
150.</para></listitem>
|
||||
|
||||
</varlistentry>
|
||||
|
||||
|
||||
<varlistentry><term><literal>force-manifest</literal></term>
|
||||
|
||||
<listitem><para>If this option is set to <literal>false</literal>
|
||||
(default) and a Nix channel provides both a manifest and a binary
|
||||
cache, only the binary cache will be used. If set to
|
||||
<literal>true</literal>, the manifest will be fetched as well.
|
||||
This is useful if you want to use binary patches (which are
|
||||
currently not supported by binary caches).</para></listitem>
|
||||
|
||||
</varlistentry>
|
||||
|
||||
|
||||
<varlistentry><term><literal>system</literal></term>
|
||||
|
||||
<listitem><para>This option specifies the canonical Nix system
|
||||
@@ -337,7 +436,21 @@ build-use-chroot = /dev /proc /bin</programlisting>
|
||||
<literal>true</literal>.</para></listitem>
|
||||
|
||||
</varlistentry>
|
||||
|
||||
|
||||
|
||||
<varlistentry><term><literal>auto-optimise-store</literal></term>
|
||||
|
||||
<listitem><para>If set to <literal>true</literal>, Nix
|
||||
automatically detects files in the store that have identical
|
||||
contents, and replaces them with hard links to a single copy.
|
||||
This saves disk space. If set to <literal>false</literal> (the
|
||||
default), you can still run <command>nix-store
|
||||
--optimise</command> to get rid of duplicate
|
||||
files.</para></listitem>
|
||||
|
||||
</varlistentry>
|
||||
|
||||
|
||||
</variablelist>
|
||||
|
||||
</para>
|
||||
|
||||
@@ -283,7 +283,7 @@ $ mount -o bind /mnt/otherdisk/nix /nix</screen>
|
||||
|
||||
<listitem><para>This variable should be set to
|
||||
<literal>daemon</literal> if you want to use the Nix daemon to
|
||||
executed Nix operations, which is necessary in <link
|
||||
execute Nix operations. This is necessary in <link
|
||||
linkend="ssec-multi-user">multi-user Nix installations</link>.
|
||||
Otherwise, it should be left unset.</para></listitem>
|
||||
|
||||
@@ -293,7 +293,7 @@ $ mount -o bind /mnt/otherdisk/nix /nix</screen>
|
||||
<varlistentry xml:id="envar-other-stores"><term><envar>NIX_OTHER_STORES</envar></term>
|
||||
|
||||
<listitem><para>This variable contains the paths of remote Nix
|
||||
installations from whichs paths can be copied, separated by colons.
|
||||
installations from which packages can be copied, separated by colons.
|
||||
<phrase condition="manual">See <xref linkend="sec-sharing-packages"
|
||||
/> for details.</phrase> Each path should be the
|
||||
<filename>/nix</filename> directory of a remote Nix installation
|
||||
@@ -304,9 +304,9 @@ $ mount -o bind /mnt/otherdisk/nix /nix</screen>
|
||||
<literal>/var/run/nix/remote-stores</literal>.</para>
|
||||
|
||||
<para>Note that if you’re building through the <link
|
||||
linkend="sec-nix-worker">Nix daemon</link>, the only setting for
|
||||
linkend="sec-nix-daemon">Nix daemon</link>, the only setting for
|
||||
this variable that matters is the one that the
|
||||
<command>nix-worker</command> process uses. So if you want to
|
||||
<command>nix-daemon</command> process uses. So if you want to
|
||||
change it, you have to restart the daemon.</para></listitem>
|
||||
|
||||
</varlistentry>
|
||||
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 83 KiB After Width: | Height: | Size: 83 KiB |
@@ -160,6 +160,18 @@
|
||||
</glossentry>
|
||||
|
||||
|
||||
<glossentry xml:id="gloss-nar"><glossterm>NAR</glossterm>
|
||||
|
||||
<glossdef><para>A <emphasis>N</emphasis>ix
|
||||
<emphasis>AR</emphasis>chive. This is a serialisation of a path in
|
||||
the Nix store. It can contain regular files, directories and
|
||||
symbolic links. NARs are generated and unpacked using
|
||||
<command>nix-store --dump</command> and <command>nix-store
|
||||
--restore</command>.</para></glossdef>
|
||||
|
||||
</glossentry>
|
||||
|
||||
|
||||
|
||||
</glosslist>
|
||||
|
||||
|
||||
@@ -0,0 +1,41 @@
|
||||
<appendix xmlns="http://docbook.org/ns/docbook"
|
||||
xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||
xml:id="chap-hacking">
|
||||
|
||||
<title>Hacking</title>
|
||||
|
||||
<para>This section provides some notes on how to hack on Nix. To get
|
||||
the latest version of Nix from GitHub:
|
||||
<screen>
|
||||
$ git clone git://github.com/NixOS/nix.git
|
||||
$ cd hydra
|
||||
</screen>
|
||||
</para>
|
||||
|
||||
<para>To build it and its dependencies:
|
||||
<screen>
|
||||
$ nix-build release.nix -A build.x86_64-linux
|
||||
</screen>
|
||||
</para>
|
||||
|
||||
<para>To build all dependencies and start a shell in which all
|
||||
environment variables are set up so that those dependencies can be
|
||||
found:
|
||||
<screen>
|
||||
$ ./dev-shell
|
||||
</screen>
|
||||
To build Nix itself in this shell:
|
||||
<screen>
|
||||
[nix-shell]$ ./bootstrap
|
||||
[nix-shell]$ configurePhase
|
||||
[nix-shell]$ make
|
||||
</screen>
|
||||
To test it:
|
||||
<screen>
|
||||
[nix-shell]$ make install
|
||||
[nix-shell]$ make installcheck
|
||||
</screen>
|
||||
|
||||
</para>
|
||||
|
||||
</appendix>
|
||||
+13
-11
@@ -116,6 +116,8 @@ a source distribution.</para>
|
||||
|
||||
<itemizedlist>
|
||||
|
||||
<listitem><para>GNU Make.</para></listitem>
|
||||
|
||||
<listitem><para>A fairly recent version of GCC/G++. Version 2.95
|
||||
and higher should work. Clang will also work.</para></listitem>
|
||||
|
||||
@@ -164,11 +166,10 @@ a source distribution.</para>
|
||||
|
||||
<listitem><para>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.3 or
|
||||
higher (1.875 does <emphasis>not</emphasis> work), which can be
|
||||
obtained from the <link
|
||||
reentrancy support in Flex.) For Bison, you need version 2.6, which
|
||||
can be obtained from the <link
|
||||
xlink:href="ftp://alpha.gnu.org/pub/gnu/bison">GNU FTP
|
||||
server</link>. For Flex, you need version 2.5.33, which is
|
||||
server</link>. For Flex, you need version 2.5.35, which is
|
||||
available on <link
|
||||
xlink:href="http://lex.sourceforge.net/">SourceForge</link>.
|
||||
Slightly older versions may also work, but ancient versions like the
|
||||
@@ -217,7 +218,8 @@ $ ./configure <replaceable>options...</replaceable>
|
||||
$ make
|
||||
$ make install</screen>
|
||||
|
||||
</para>
|
||||
Nix requires GNU Make so you may need to invoke
|
||||
<command>gmake</command> instead.</para>
|
||||
|
||||
<para>When building from the Git repository, these should be preceded
|
||||
by the command:
|
||||
@@ -409,11 +411,11 @@ $ chown -R root /nix/store /nix/var/nix</screen>
|
||||
|
||||
</para>
|
||||
|
||||
<para>The <link linkend="sec-nix-worker">Nix daemon</link> should be
|
||||
<para>The <link linkend="sec-nix-daemon">Nix daemon</link> should be
|
||||
started as follows (as <literal>root</literal>):
|
||||
|
||||
<screen>
|
||||
$ nix-worker --daemon</screen>
|
||||
$ nix-daemon</screen>
|
||||
|
||||
You’ll want to put that line somewhere in your system’s boot
|
||||
scripts.</para>
|
||||
@@ -447,11 +449,11 @@ named anything. It should own the Nix store and database:
|
||||
<screen>
|
||||
$ chown -R nix /nix/store /nix/var/nix</screen>
|
||||
|
||||
and of course <command>nix-worker --daemon</command> should be started
|
||||
under that user, e.g.,
|
||||
and of course <command>nix-daemon</command> should be started under
|
||||
that user, e.g.,
|
||||
|
||||
<screen>
|
||||
$ su - nix -c "exec /nix/bin/nix-worker --daemon"</screen>
|
||||
$ su - nix -c "exec /nix/bin/nix-daemon"</screen>
|
||||
|
||||
</para>
|
||||
|
||||
@@ -536,7 +538,7 @@ a symbolic link to the current <emphasis>user environment</emphasis>
|
||||
installed packages). The simplest way to set the required environment
|
||||
variables is to include the file
|
||||
<filename><replaceable>prefix</replaceable>/etc/profile.d/nix.sh</filename>
|
||||
in your <filename>~/.bashrc</filename> (or similar), like this:</para>
|
||||
in your <filename>~/.profile</filename> (or similar), like this:</para>
|
||||
|
||||
<screen>
|
||||
source <replaceable>prefix</replaceable>/etc/profile.d/nix.sh</screen>
|
||||
|
||||
+11
-9
@@ -19,15 +19,15 @@
|
||||
</author>
|
||||
|
||||
<copyright>
|
||||
<year>2004-2012</year>
|
||||
<year>2004-2013</year>
|
||||
<holder>Eelco Dolstra</holder>
|
||||
</copyright>
|
||||
|
||||
<date>May 2012</date>
|
||||
|
||||
<date>July 2013</date>
|
||||
|
||||
</info>
|
||||
|
||||
|
||||
|
||||
<xi:include href="introduction.xml" />
|
||||
<xi:include href="quick-start.xml" />
|
||||
<xi:include href="installation.xml" />
|
||||
@@ -40,17 +40,18 @@
|
||||
<title>Command Reference</title>
|
||||
<xi:include href="opt-common.xml" />
|
||||
<xi:include href="env-common.xml" />
|
||||
|
||||
|
||||
<section>
|
||||
<title>Main commands</title>
|
||||
<xi:include href="nix-env.xml" />
|
||||
<xi:include href="nix-instantiate.xml" />
|
||||
<xi:include href="nix-store.xml" />
|
||||
</section>
|
||||
|
||||
|
||||
<section>
|
||||
<title>Utilities</title>
|
||||
<xi:include href="nix-build.xml" />
|
||||
<xi:include href="nix-shell.xml" />
|
||||
<xi:include href="nix-channel.xml" />
|
||||
<xi:include href="nix-collect-garbage.xml" />
|
||||
<xi:include href="nix-copy-closure.xml" />
|
||||
@@ -59,24 +60,25 @@
|
||||
<xi:include href="nix-prefetch-url.xml" />
|
||||
<xi:include href="nix-pull.xml" />
|
||||
<xi:include href="nix-push.xml" />
|
||||
<xi:include href="nix-worker.xml" />
|
||||
<xi:include href="nix-daemon.xml" />
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<title>Files</title>
|
||||
<xi:include href="conf-file.xml" />
|
||||
</section>
|
||||
|
||||
|
||||
</appendix>
|
||||
|
||||
<xi:include href="troubleshooting.xml" />
|
||||
<!-- <xi:include href="bugs.xml" /> -->
|
||||
<xi:include href="glossary.xml" />
|
||||
<xi:include href="hacking.xml" />
|
||||
|
||||
<appendix>
|
||||
<title>Nix Release Notes</title>
|
||||
<xi:include href="release-notes.xml"
|
||||
xpointer="xmlns(x=http://docbook.org/ns/docbook)xpointer(x:article/x:section)" />
|
||||
</appendix>
|
||||
|
||||
|
||||
</book>
|
||||
|
||||
+25
-52
@@ -38,11 +38,6 @@
|
||||
</group>
|
||||
<replaceable>outlink</replaceable>
|
||||
</arg>
|
||||
<arg>
|
||||
<option>--run-env</option>
|
||||
<arg><option>--command</option> <replaceable>cmd</replaceable></arg>
|
||||
<arg><option>--exclude</option> <replaceable>regexp</replaceable></arg>
|
||||
</arg>
|
||||
<arg choice='plain' rep='repeat'><replaceable>paths</replaceable></arg>
|
||||
</cmdsynopsis>
|
||||
</refsynopsisdiv>
|
||||
@@ -75,13 +70,6 @@ a root of the Nix garbage collector. This root disappears
|
||||
automatically when the <filename>result</filename> symlink is deleted
|
||||
or renamed. So don’t rename the symlink.</para></warning>
|
||||
|
||||
<para>The subcommand <command>nix-build --run-env</command> will build
|
||||
the dependencies of the derivation, but not the derivation itself. It
|
||||
will then start an interactive shell in which all environment
|
||||
variables defined by the derivation have been set to their
|
||||
corresponding values. This is useful for reproducing the environment
|
||||
of a derivation for development.</para>
|
||||
|
||||
</refsection>
|
||||
|
||||
|
||||
@@ -96,7 +84,7 @@ also <xref linkend="sec-common-options" />.</phrase></para>
|
||||
<variablelist>
|
||||
|
||||
<varlistentry><term><option>--drv-link</option> <replaceable>drvlink</replaceable></term>
|
||||
|
||||
|
||||
<listitem><para>Add a symlink named
|
||||
<replaceable>drvlink</replaceable> to the store derivation
|
||||
produced by <command>nix-instantiate</command>. The derivation is
|
||||
@@ -108,14 +96,14 @@ also <xref linkend="sec-common-options" />.</phrase></para>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry><term><option>--add-drv-link</option></term>
|
||||
|
||||
|
||||
<listitem><para>Shorthand for <option>--drv-link</option>
|
||||
<filename>./derivation</filename>.</para></listitem>
|
||||
|
||||
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry><term><option>--no-out-link</option></term>
|
||||
|
||||
|
||||
<listitem><para>Do not create a symlink to the output path. Note
|
||||
that as a result the output does not become a root of the garbage
|
||||
collector, and so might be deleted by <command>nix-store
|
||||
@@ -125,7 +113,7 @@ also <xref linkend="sec-common-options" />.</phrase></para>
|
||||
|
||||
<varlistentry xml:id='opt-out-link'><term><option>--out-link</option> /
|
||||
<option>-o</option> <replaceable>outlink</replaceable></term>
|
||||
|
||||
|
||||
<listitem><para>Change the name of the symlink to the output path
|
||||
created from <filename>result</filename> to
|
||||
<replaceable>outlink</replaceable>.</para></listitem>
|
||||
@@ -134,32 +122,12 @@ also <xref linkend="sec-common-options" />.</phrase></para>
|
||||
|
||||
</variablelist>
|
||||
|
||||
<para>The following common options are supported:</para>
|
||||
|
||||
<variablelist condition="manpage">
|
||||
<xi:include href="opt-common.xml#xmlns(db=http://docbook.org/ns/docbook)xpointer(//db:variablelist[@xml:id='opt-common']/*)" />
|
||||
</variablelist>
|
||||
|
||||
<para>The following options apply to <command>nix-build --run-env</command>.</para>
|
||||
|
||||
<variablelist>
|
||||
|
||||
<varlistentry><term><option>--command</option> <replaceable>cmd</replaceable></term>
|
||||
|
||||
<listitem><para>In the environment of the derivation, executeq the
|
||||
command <replaceable>cmd</replaceable> instead of the default
|
||||
interactive shell.</para></listitem>
|
||||
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry><term><option>--exclude</option> <replaceable>regexp</replaceable></term>
|
||||
|
||||
<listitem><para>Do not build any dependencies whose store path
|
||||
matches the regular expression <replaceable>regexp</replaceable>.
|
||||
This option may be specified multiple times.</para></listitem>
|
||||
|
||||
</varlistentry>
|
||||
|
||||
</variablelist>
|
||||
|
||||
</refsection>
|
||||
|
||||
|
||||
@@ -176,20 +144,25 @@ lrwxrwxrwx <replaceable>...</replaceable> result -> /nix/store/d18hyl92g30l...
|
||||
$ ls ./result/bin/
|
||||
firefox firefox-config</screen>
|
||||
|
||||
<para>To build the dependencies of the package Pan, and start an
|
||||
interactive shell in which to build it:
|
||||
|
||||
<para>If a derivation has multiple outputs,
|
||||
<command>nix-build</command> will build the default (first) output.
|
||||
You can also build all outputs:
|
||||
<screen>
|
||||
$ nix-build '<nixpkgs>' --run-env -A pan
|
||||
$ tar xf $src
|
||||
$ cd pan-*
|
||||
$ ./configure
|
||||
$ make
|
||||
$ ./pan/gui/pan
|
||||
$ nix-build '<nixpkgs>' -A openssl.all
|
||||
</screen>
|
||||
|
||||
</para>
|
||||
|
||||
This will create a symlink for each output named
|
||||
<filename>result-<replaceable>outputname</replaceable></filename>.
|
||||
The suffix is omitted if the output name is <literal>out</literal>.
|
||||
So if <literal>openssl</literal> has outputs <literal>out</literal>,
|
||||
<literal>bin</literal> and <literal>man</literal>,
|
||||
<command>nix-build</command> will create symlinks
|
||||
<literal>result</literal>, <literal>result-bin</literal> and
|
||||
<literal>result-man</literal>. It’s also possible to build a specific
|
||||
output:
|
||||
<screen>
|
||||
$ nix-build '<nixpkgs>' -A openssl.man
|
||||
</screen>
|
||||
This will create a symlink <literal>result-man</literal>.</para>
|
||||
|
||||
</refsection>
|
||||
|
||||
@@ -201,6 +174,6 @@ $ ./pan/gui/pan
|
||||
</variablelist>
|
||||
|
||||
</refsection>
|
||||
|
||||
|
||||
|
||||
</refentry>
|
||||
|
||||
@@ -103,7 +103,7 @@ respectively.</para>
|
||||
<para>To subscribe to the Nixpkgs channel and install the GNU Hello package:</para>
|
||||
|
||||
<screen>
|
||||
$ nix-channel --add http://nixos.org/releases/nixpkgs/channels/nixpkgs-unstable
|
||||
$ nix-channel --add http://nixos.org/channels/nixpkgs-unstable
|
||||
$ nix-channel --update
|
||||
$ nix-env -iA nixpkgs.hello</screen>
|
||||
|
||||
|
||||
@@ -26,6 +26,7 @@
|
||||
<arg choice='plain'><option>--print-dead</option></arg>
|
||||
<arg choice='plain'><option>--delete</option></arg>
|
||||
</group>
|
||||
<arg><option>--dry-run</option></arg>
|
||||
</cmdsynopsis>
|
||||
</refsynopsisdiv>
|
||||
|
||||
|
||||
@@ -28,6 +28,8 @@
|
||||
<arg><option>--xz</option></arg>
|
||||
<arg><option>--show-progress</option></arg>
|
||||
<arg><option>--include-outputs</option></arg>
|
||||
<arg><option>--use-substitutes</option></arg>
|
||||
<arg><option>-s</option></arg>
|
||||
<arg choice='plain'>
|
||||
<replaceable>user@</replaceable><replaceable>machine</replaceable>
|
||||
</arg>
|
||||
@@ -65,7 +67,7 @@ those paths. If this bothers you, use
|
||||
<refsection><title>Options</title>
|
||||
|
||||
<variablelist>
|
||||
|
||||
|
||||
<varlistentry><term><option>--to</option></term>
|
||||
|
||||
<listitem><para>Copy the closure of
|
||||
@@ -119,10 +121,22 @@ those paths. If this bothers you, use
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry><term><option>--include-outputs</option></term>
|
||||
|
||||
<listitem><para>Also copy the outputs of store derivations included
|
||||
in the closure.</para></listitem>
|
||||
|
||||
|
||||
<listitem><para>Also copy the outputs of store derivations
|
||||
included in the closure.</para></listitem>
|
||||
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry><term><option>--use-substitutes</option> / <option>-s</option></term>
|
||||
|
||||
<listitem><para>Attempt to download missing paths on the target
|
||||
machine using Nix’s substitute mechanism. Any paths that cannot
|
||||
be substituted on the target are still copied normally from the
|
||||
source. This is useful, for instance, if the connection between
|
||||
the source and target machine is slow, but the connection between
|
||||
the target machine and <literal>nixos.org</literal> (the default
|
||||
binary cache server) is fast.</para></listitem>
|
||||
|
||||
</varlistentry>
|
||||
|
||||
</variablelist>
|
||||
@@ -140,7 +154,7 @@ those paths. If this bothers you, use
|
||||
<command>ssh</command> on the command line.</para></listitem>
|
||||
|
||||
</varlistentry>
|
||||
|
||||
|
||||
</variablelist>
|
||||
|
||||
</refsection>
|
||||
|
||||
@@ -1,24 +1,23 @@
|
||||
<refentry xmlns="http://docbook.org/ns/docbook"
|
||||
xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||
xmlns:xi="http://www.w3.org/2001/XInclude"
|
||||
xml:id="sec-nix-worker">
|
||||
xml:id="sec-nix-daemon">
|
||||
|
||||
<refmeta>
|
||||
<refentrytitle>nix-worker</refentrytitle>
|
||||
<refentrytitle>nix-daemon</refentrytitle>
|
||||
<manvolnum>8</manvolnum>
|
||||
<refmiscinfo class="source">Nix</refmiscinfo>
|
||||
<refmiscinfo class="version"><xi:include href="version.txt" parse="text"/></refmiscinfo>
|
||||
</refmeta>
|
||||
|
||||
<refnamediv>
|
||||
<refname>nix-worker</refname>
|
||||
<refname>nix-daemon</refname>
|
||||
<refpurpose>Nix multi-user support daemon</refpurpose>
|
||||
</refnamediv>
|
||||
|
||||
<refsynopsisdiv>
|
||||
<cmdsynopsis>
|
||||
<command>nix-worker</command>
|
||||
<arg choice="plain"><option>--daemon</option></arg>
|
||||
<command>nix-daemon</command>
|
||||
</cmdsynopsis>
|
||||
</refsynopsisdiv>
|
||||
|
||||
+32
-8
@@ -93,11 +93,10 @@ also <xref linkend="sec-common-options" />.</phrase></para>
|
||||
|
||||
<listitem><para>Specifies the profile to be used by those
|
||||
operations that operate on a profile (designated below as the
|
||||
<emphasis>active profile</emphasis>). A profile is sequence of
|
||||
<emphasis>active profile</emphasis>). A profile is a sequence of
|
||||
user environments called <emphasis>generations</emphasis>, one of
|
||||
which is the <emphasis>current generation</emphasis>. The default
|
||||
profile is the target of the symbolic link
|
||||
<filename>~/.nix-profile</filename> (see below).</para></listitem>
|
||||
which is the <emphasis>current
|
||||
generation</emphasis>.</para></listitem>
|
||||
|
||||
</varlistentry>
|
||||
|
||||
@@ -105,7 +104,8 @@ also <xref linkend="sec-common-options" />.</phrase></para>
|
||||
|
||||
<listitem><para>For the <option>--install</option>,
|
||||
<option>--upgrade</option>, <option>--uninstall</option>,
|
||||
<option>--switch-generation</option> and
|
||||
<option>--switch-generation</option>,
|
||||
<option>--delete-generations</option> and
|
||||
<option>--rollback</option> operations, this flag will cause
|
||||
<command>nix-env</command> to print what
|
||||
<emphasis>would</emphasis> be done if this flag had not been
|
||||
@@ -209,6 +209,10 @@ also <xref linkend="sec-common-options" />.</phrase></para>
|
||||
<arg choice='plain'><option>--preserve-installed</option></arg>
|
||||
<arg choice='plain'><option>-P</option></arg>
|
||||
</group>
|
||||
<group choice='opt'>
|
||||
<arg choice='plain'><option>--remove-all</option></arg>
|
||||
<arg choice='plain'><option>-r</option></arg>
|
||||
</group>
|
||||
<arg choice='plain' rep='repeat'><replaceable>args</replaceable></arg>
|
||||
</cmdsynopsis>
|
||||
|
||||
@@ -272,7 +276,7 @@ number of possible ways:
|
||||
linkend="ss-functions">functions</link> that are called with the
|
||||
active Nix expression as their single argument. The derivations
|
||||
returned by those function calls are installed. This allows
|
||||
derivations to be specified in a unambiguous way, which is necessary
|
||||
derivations to be specified in an unambiguous way, which is necessary
|
||||
if there are multiple derivations with the same
|
||||
name.</para></listitem>
|
||||
|
||||
@@ -318,6 +322,16 @@ number of possible ways:
|
||||
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry><term><option>--remove-all</option></term>
|
||||
<term><option>-r</option></term>
|
||||
|
||||
<listitem><para>Remove all previously installed packages first.
|
||||
This is equivalent to running <literal>nix-env -e '*'</literal>
|
||||
first, except that everything happens in a single
|
||||
transaction.</para></listitem>
|
||||
|
||||
</varlistentry>
|
||||
|
||||
</variablelist>
|
||||
|
||||
</refsection>
|
||||
@@ -692,7 +706,7 @@ firefox-2.0.0.9 <lineannotation>(the current one)</lineannotation>
|
||||
$ nix-env --preserve-installed -i firefox-2.0.0.11
|
||||
installing `firefox-2.0.0.11'
|
||||
building path(s) `/nix/store/myy0y59q3ig70dgq37jqwg1j0rsapzsl-user-environment'
|
||||
Collission between `/nix/store/<replaceable>...</replaceable>-firefox-2.0.0.11/bin/firefox'
|
||||
collision between `/nix/store/<replaceable>...</replaceable>-firefox-2.0.0.11/bin/firefox'
|
||||
and `/nix/store/<replaceable>...</replaceable>-firefox-2.0.0.9/bin/firefox'.
|
||||
<lineannotation>(i.e., can’t have two active at the same time)</lineannotation>
|
||||
|
||||
@@ -1244,7 +1258,7 @@ wrapper around <option>--list-generations</option> and
|
||||
$ nix-env --rollback
|
||||
switching from generation 92 to 91
|
||||
|
||||
$ nix-env --rolback
|
||||
$ nix-env --rollback
|
||||
error: no generation older than the current (91) exists</screen>
|
||||
|
||||
</refsection>
|
||||
@@ -1255,6 +1269,16 @@ error: no generation older than the current (91) exists</screen>
|
||||
<refsection condition="manpage"><title>Environment variables</title>
|
||||
|
||||
<variablelist>
|
||||
|
||||
<varlistentry><term><envar>NIX_PROFILE</envar></term>
|
||||
|
||||
<listitem><para>Location of the Nix profile. Defaults to the
|
||||
target of the symlink <filename>~/.nix-profile</filename>, if it
|
||||
exists, or <filename>/nix/var/nix/profiles/default</filename>
|
||||
otherwise.</para></listitem>
|
||||
|
||||
</varlistentry>
|
||||
|
||||
<xi:include href="env-common.xml#xmlns(db=http://docbook.org/ns/docbook)xpointer(//db:variablelist[@xml:id='env-common']/*)" />
|
||||
</variablelist>
|
||||
|
||||
|
||||
@@ -91,8 +91,9 @@ cryptographic hash as <literal>nix-store --dump
|
||||
|
||||
<varlistentry><term><option>--type</option> <replaceable>hashAlgo</replaceable></term>
|
||||
|
||||
<listitem><para>Specify a cryptographic hash, which can be one of
|
||||
<literal>md5</literal>, <literal>sha1</literal>, and
|
||||
<listitem><para>Use the specified cryptographic hash algorithm,
|
||||
which can be one of <literal>md5</literal>,
|
||||
<literal>sha1</literal>, and
|
||||
<literal>sha256</literal>.</para></listitem>
|
||||
|
||||
</varlistentry>
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
<refsynopsisdiv>
|
||||
<cmdsynopsis>
|
||||
<command>nix-prefetch-url</command>
|
||||
<arg><option>--type</option> <replaceable>hashAlgo</replaceable></arg>
|
||||
<arg choice='plain'><replaceable>url</replaceable></arg>
|
||||
<arg><replaceable>hash</replaceable></arg>
|
||||
</cmdsynopsis>
|
||||
@@ -45,11 +46,6 @@ download it again when you build your Nix expression. Since
|
||||
as <command>nix-prefetch-url</command>, the redundant download can be
|
||||
avoided.</para>
|
||||
|
||||
<para>The environment variable <envar>NIX_HASH_ALGO</envar> specifies
|
||||
which hash algorithm to use. It can be either <literal>md5</literal>,
|
||||
<literal>sha1</literal>, or <literal>sha256</literal>. The default is
|
||||
<literal>sha256</literal>.</para>
|
||||
|
||||
<para>If <replaceable>hash</replaceable> is specified, then a download
|
||||
is not performed if the Nix store already contains a file with the
|
||||
same hash and base name. Otherwise, the file is downloaded, and an
|
||||
@@ -63,6 +59,24 @@ of the downloaded file in the Nix store is also printed.</para>
|
||||
</refsection>
|
||||
|
||||
|
||||
<refsection><title>Options</title>
|
||||
|
||||
<variablelist>
|
||||
|
||||
<varlistentry><term><option>--type</option> <replaceable>hashAlgo</replaceable></term>
|
||||
|
||||
<listitem><para>Use the specified cryptographic hash algorithm,
|
||||
which can be one of <literal>md5</literal>,
|
||||
<literal>sha1</literal>, and
|
||||
<literal>sha256</literal>.</para></listitem>
|
||||
|
||||
</varlistentry>
|
||||
|
||||
</variablelist>
|
||||
|
||||
</refsection>
|
||||
|
||||
|
||||
<refsection><title>Examples</title>
|
||||
|
||||
<screen>
|
||||
|
||||
+345
-76
@@ -12,24 +12,20 @@
|
||||
|
||||
<refnamediv>
|
||||
<refname>nix-push</refname>
|
||||
<refpurpose>push store paths onto a network cache</refpurpose>
|
||||
<refpurpose>generate a binary cache</refpurpose>
|
||||
</refnamediv>
|
||||
|
||||
<refsynopsisdiv>
|
||||
<cmdsynopsis>
|
||||
<command>nix-push</command>
|
||||
<group choice='req'>
|
||||
<arg choice='req'>
|
||||
<arg choice='plain'><replaceable>archivesPutURL</replaceable></arg>
|
||||
<arg choice='plain'><replaceable>archivesGetURL</replaceable></arg>
|
||||
<arg choice='plain'><replaceable>manifestPutURL</replaceable></arg>
|
||||
</arg>
|
||||
<arg choice='req'>
|
||||
<arg choice='plain'><option>--copy</option></arg>
|
||||
<arg choice='plain'><replaceable>archivesDir</replaceable></arg>
|
||||
<arg choice='plain'><replaceable>manifestFile</replaceable></arg>
|
||||
</arg>
|
||||
</group>
|
||||
<arg choice='plain'><option>--dest</option> <replaceable>dest-dir</replaceable></arg>
|
||||
<arg><option>--bzip2</option></arg>
|
||||
<arg><option>--none</option></arg>
|
||||
<arg><option>--force</option></arg>
|
||||
<arg><option>--link</option></arg>
|
||||
<arg><option>--manifest</option></arg>
|
||||
<arg><option>--manifest-path</option> <replaceable>filename</replaceable></arg>
|
||||
<arg><option>--url-prefix</option> <replaceable>url</replaceable></arg>
|
||||
<arg choice='plain' rep='repeat'><replaceable>paths</replaceable></arg>
|
||||
</cmdsynopsis>
|
||||
</refsynopsisdiv>
|
||||
@@ -37,93 +33,366 @@
|
||||
|
||||
<refsection><title>Description</title>
|
||||
|
||||
<para>The command <command>nix-push</command> builds a set of store
|
||||
paths (if necessary), and then packages and uploads all store paths in
|
||||
the resulting closures to a server. A network cache thus populated
|
||||
can subsequently be used to speed up software deployment on other
|
||||
machines using the <command>nix-pull</command> command.</para>
|
||||
<para>The command <command>nix-push</command> produces a
|
||||
<emphasis>binary cache</emphasis>, a directory containing compressed
|
||||
Nix archives (NARs) plus some metadata of the closure of the specified
|
||||
store paths. This directory can then be made available through a web
|
||||
server to other Nix installations, allowing them to skip building from
|
||||
source and instead download binaries from the cache
|
||||
automatically.</para>
|
||||
|
||||
<para><command>nix-push</command> performs the following actions.
|
||||
|
||||
<orderedlist>
|
||||
|
||||
<listitem><para>Each path in <replaceable>paths</replaceable> is
|
||||
realised (using <link
|
||||
linkend='rsec-nix-store-realise'><literal>nix-store
|
||||
--realise</literal></link>).</para></listitem>
|
||||
built (using <link
|
||||
linkend='rsec-nix-store-realise'><command>nix-store
|
||||
--realise</command></link>).</para></listitem>
|
||||
|
||||
<listitem><para>All paths in the closure of the store expressions
|
||||
stored in <replaceable>paths</replaceable> are determined (using
|
||||
<literal>nix-store --query --requisites
|
||||
--include-outputs</literal>). It should be noted that since the
|
||||
<option>--include-outputs</option> flag is used, you get a combined
|
||||
source/binary distribution.</para></listitem>
|
||||
<listitem><para>All paths in the closure of
|
||||
<replaceable>paths</replaceable> are determined (using
|
||||
<command>nix-store --query --requisites
|
||||
--include-outputs</command>). Note that since the
|
||||
<option>--include-outputs</option> flag is used, if
|
||||
<replaceable>paths</replaceable> includes a store derivation, you
|
||||
get a combined source/binary distribution (e.g., source tarballs
|
||||
will be included).</para></listitem>
|
||||
|
||||
<listitem><para>All store paths determined in the previous step are
|
||||
packaged and compressed into a <command>bzip</command>ped NAR
|
||||
archive (extension <filename>.nar.bz2</filename>).</para></listitem>
|
||||
packaged into a NAR (using <command>nix-store --dump</command>) and
|
||||
compressed using <command>xz</command> or <command>bzip2</command>.
|
||||
The resulting files have the extension <filename>.nar.xz</filename>
|
||||
or <filename>.nar.bz2</filename>. Also for each store path, Nix
|
||||
generates a file with extension <filename>.narinfo</filename>
|
||||
containing metadata such as the references, cryptographic hash and
|
||||
size of each path.</para></listitem>
|
||||
|
||||
<listitem><para>A <emphasis>manifest</emphasis> is created that
|
||||
contains information on the store paths, their eventual URLs in the
|
||||
cache, and cryptographic hashes of the contents of the NAR
|
||||
archives.</para></listitem>
|
||||
<listitem><para>Optionally, a single <emphasis>manifest</emphasis>
|
||||
file is created that contains the same metadata as the
|
||||
<filename>.narinfo</filename> files. This is for compatibility with
|
||||
Nix versions prior to 1.2 (see <command>nix-pull</command> for
|
||||
details).</para></listitem>
|
||||
|
||||
<listitem><para>Each store path is uploaded to the remote directory
|
||||
specified by <replaceable>archivesPutURL</replaceable>. HTTP PUT
|
||||
requests are used to do this. However, before a file
|
||||
<varname>x</varname> is uploaded to
|
||||
<literal><replaceable>archivesPutURL</replaceable>/</literal><varname>x</varname>,
|
||||
<command>nix-push</command> first determines whether this upload is
|
||||
unnecessary by issuing a HTTP HEAD request on
|
||||
<literal><replaceable>archivesGetURL</replaceable>/</literal><varname>x</varname>.
|
||||
This allows a cache to be shared between many partially overlapping
|
||||
<command>nix-push</command> invocations. (We use two URLs because
|
||||
the upload URL typically refers to a CGI script, while the download
|
||||
URL just refers to a file system directory on the
|
||||
server.)</para></listitem>
|
||||
<listitem><para>A file named <option>nix-cache-info</option> is
|
||||
placed in the destination directory. The existence of this file
|
||||
marks the directory as a binary cache.</para></listitem>
|
||||
|
||||
<listitem><para>The manifest is uploaded using an HTTP PUT request
|
||||
to <replaceable>manifestPutURL</replaceable>. The corresponding
|
||||
URL to download the manifest can then be used by
|
||||
<command>nix-pull</command>.</para></listitem>
|
||||
|
||||
</orderedlist>
|
||||
|
||||
</para>
|
||||
|
||||
<!--
|
||||
<para>TODO: <option>- -copy</option></para>
|
||||
-->
|
||||
|
||||
</refsection>
|
||||
|
||||
|
||||
<refsection><title>Options</title>
|
||||
|
||||
<variablelist>
|
||||
|
||||
<varlistentry><term><option>--dest</option> <replaceable>dest-dir</replaceable></term>
|
||||
|
||||
<listitem><para>Set the destination directory to
|
||||
<replaceable>dir</replaceable>, which is created if it does not
|
||||
exist. This flag is required.</para></listitem>
|
||||
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry><term><option>--bzip2</option></term>
|
||||
|
||||
<listitem><para>Compress NARs using <command>bzip2</command>
|
||||
instead of <command>xz -9</command>. The latter compresses about
|
||||
30% better on typical archives, decompresses about twice as fast,
|
||||
but compresses a lot slower and is not supported by Nix prior to
|
||||
version 1.2.</para></listitem>
|
||||
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry><term><option>--none</option></term>
|
||||
|
||||
<listitem><para>Do not compress NARs.</para></listitem>
|
||||
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry><term><option>--force</option></term>
|
||||
|
||||
<listitem><para>Overwrite <filename>.narinfo</filename> files if
|
||||
they already exist.</para></listitem>
|
||||
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry><term><option>--link</option></term>
|
||||
|
||||
<listitem><para>By default, NARs are generated in the Nix store
|
||||
and then copied to <replaceable>dest-dir</replaceable>. If this
|
||||
option is given, hard links are used instead. This only works if
|
||||
<replaceable>dest-dir</replaceable> is on the same filesystem as
|
||||
the Nix store.</para></listitem>
|
||||
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry><term><option>--manifest</option></term>
|
||||
|
||||
<listitem><para>Force the generation of a manifest suitable for
|
||||
use by <command>nix-pull</command>. The manifest is stored as
|
||||
<filename><replaceable>dest-dir</replaceable>/MANIFEST</filename>.</para></listitem>
|
||||
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry><term><option>--manifest-path</option> <replaceable>filename</replaceable></term>
|
||||
|
||||
<listitem><para>Like <option>--manifest</option>, but store the
|
||||
manifest in <replaceable>filename</replaceable>.</para></listitem>
|
||||
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry><term><option>--url-prefix</option> <replaceable>url</replaceable></term>
|
||||
|
||||
<listitem><para>Manifests are expected to contain the absolute
|
||||
URLs of NARs. For generating these URLs, the prefix
|
||||
<replaceable>url</replaceable> is used. It defaults to
|
||||
<uri>file://<replaceable>dest-dir</replaceable></uri>.</para></listitem>
|
||||
|
||||
</varlistentry>
|
||||
|
||||
</variablelist>
|
||||
|
||||
</refsection>
|
||||
|
||||
|
||||
<refsection><title>Examples</title>
|
||||
|
||||
<para>To upload files there typically is some CGI script on the server
|
||||
side. This script should be be protected with a password. The
|
||||
following example uploads the store paths resulting from building the
|
||||
Nix expressions in <filename>foo.nix</filename>, passing appropriate
|
||||
authentication information:
|
||||
|
||||
<screen>
|
||||
$ nix-push \
|
||||
http://foo@bar:server.domain/cgi-bin/upload.pl/cache \
|
||||
http://server.domain/cache \
|
||||
http://foo@bar:server.domain/cgi-bin/upload.pl/MANIFEST \
|
||||
$(nix-instantiate foo.nix)</screen>
|
||||
<para>To add the closure of Thunderbird to a binary cache:
|
||||
|
||||
This will push both sources and binaries (and any build-time
|
||||
dependencies used in the build, such as compilers).</para>
|
||||
|
||||
<para>If we just want to push binaries, not sources and build-time
|
||||
dependencies, we can do:
|
||||
|
||||
<screen>
|
||||
$ nix-push <replaceable>urls</replaceable> $(nix-store -r $(nix-instantiate foo.nix))</screen>
|
||||
|
||||
$ nix-push --dest /tmp/cache $(nix-build -A thunderbird)
|
||||
</screen>
|
||||
|
||||
Assuming that <filename>/tmp/cache</filename> is exported by a web
|
||||
server as <uri>http://example.org/cache</uri>, you can then use this
|
||||
cache on another machine to speed up the installation of Thunderbird:
|
||||
|
||||
<screen>
|
||||
$ nix-build -A thunderbird --option binary-caches http://example.org/cache
|
||||
</screen>
|
||||
|
||||
Alternatively, you could add <literal>binary-caches =
|
||||
http://example.org/cache</literal> to
|
||||
<filename>nix.conf</filename>.</para>
|
||||
|
||||
<para>To also include build-time dependencies (such as source
|
||||
tarballs):
|
||||
|
||||
<screen>
|
||||
$ nix-push --dest /tmp/cache $(nix-instantiate -A thunderbird)
|
||||
</screen>
|
||||
|
||||
</para>
|
||||
|
||||
<para>To generate a manifest suitable for <command>nix-pull</command>:
|
||||
|
||||
<screen>
|
||||
$ nix-push --dest /tmp/cache $(nix-build -A thunderbird) --manifest
|
||||
</screen>
|
||||
|
||||
On another machine you can then do:
|
||||
|
||||
<screen>
|
||||
$ nix-pull http://example.org/cache
|
||||
</screen>
|
||||
|
||||
to cause the binaries to be used by subsequent Nix operations.</para>
|
||||
|
||||
</refsection>
|
||||
|
||||
|
||||
|
||||
<refsection><title>Binary cache format and operation</title>
|
||||
|
||||
<para>A binary cache with URL <replaceable>url</replaceable> only
|
||||
denotes a valid binary cache if the file
|
||||
<uri><replaceable>url</replaceable>/nix-cache-info</uri> exists. If
|
||||
this file does not exist (or cannot be downloaded), the cache is
|
||||
ignored. If it does exist, it must be a text file containing cache
|
||||
properties. Here’s an example:
|
||||
|
||||
<screen>
|
||||
StoreDir: /nix/store
|
||||
WantMassQuery: 1
|
||||
Priority: 10
|
||||
</screen>
|
||||
|
||||
The properties that are currently supported are:
|
||||
|
||||
<variablelist>
|
||||
|
||||
<varlistentry><term><literal>StoreDir</literal></term>
|
||||
|
||||
<listitem><para>The path of the Nix store to which this binary
|
||||
cache applies. Binaries are not relocatable — a binary built for
|
||||
<filename>/nix/store</filename> won’t generally work in
|
||||
<filename>/home/alice/store</filename> — so to prevent binaries
|
||||
from being used in a wrong store, a binary cache is only used if
|
||||
its <literal>StoreDir</literal> matches the local Nix
|
||||
configuration. The default is
|
||||
<filename>/nix/store</filename>.</para></listitem>
|
||||
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry><term><literal>WantMassQuery</literal></term>
|
||||
|
||||
<listitem><para>Query operations such as <command>nix-env
|
||||
-qas</command> can cause thousands of cache queries, and thus
|
||||
thousands of HTTP requests, to determine which packages are
|
||||
available in binary form. While these requests are small, not
|
||||
every server may appreciate a potential onslaught of queries. If
|
||||
<literal>WantMassQuery</literal> is set to <literal>0</literal>
|
||||
(default), “mass queries” such as <command>nix-env -qas</command>
|
||||
will skip this cache. Thus a package may appear not to have a
|
||||
binary substitute. However, the binary will still be used when
|
||||
you actually install the package. If
|
||||
<literal>WantMassQuery</literal> is set to <literal>1</literal>,
|
||||
mass queries will use this cache.</para></listitem>
|
||||
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry><term><literal>Priority</literal></term>
|
||||
|
||||
<listitem><para>Each binary cache has a priority (defaulting to
|
||||
50). Binary caches are checked for binaries in order of ascending
|
||||
priority; thus a higher number denotes a lower priority. The
|
||||
binary cache <uri>http://cache.nixos.org</uri> has priority
|
||||
40.</para></listitem>
|
||||
|
||||
</varlistentry>
|
||||
|
||||
</variablelist>
|
||||
|
||||
</para>
|
||||
|
||||
<para>Every time Nix needs to build some store path
|
||||
<replaceable>p</replaceable>, it will check each configured binary
|
||||
cache to see if it has a NAR file for <replaceable>p</replaceable>,
|
||||
until it finds one. If no cache has a NAR, Nix will fall back to
|
||||
building the path from source (if applicable). To see if a cache with
|
||||
URL <replaceable>url</replaceable> has a binary for
|
||||
<replaceable>p</replaceable>, Nix fetches
|
||||
<replaceable>url/h</replaceable>, where <replaceable>h</replaceable>
|
||||
is the hash part of <replaceable>p</replaceable>. Thus, if we have a
|
||||
cache <uri>http://cache.nixos.org</uri> and we want to obtain
|
||||
the store path
|
||||
<screen>
|
||||
/nix/store/a8922c0h87iilxzzvwn2hmv8x210aqb9-glibc-2.7
|
||||
</screen>
|
||||
then Nix will attempt to fetch
|
||||
<screen>
|
||||
http://cache.nixos.org/a8922c0h87iilxzzvwn2hmv8x210aqb9.narinfo
|
||||
</screen>
|
||||
(Commands such as <command>nix-env -qas</command> will issue an HTTP
|
||||
HEAD request, since it only needs to know if the
|
||||
<filename>.narinfo</filename> file exists.) The
|
||||
<filename>.narinfo</filename> file is a simple text file that looks
|
||||
like this:
|
||||
|
||||
<screen>
|
||||
StorePath: /nix/store/a8922c0h87iilxzzvwn2hmv8x210aqb9-glibc-2.7
|
||||
URL: nar/0zzjpdz46mdn74v09m053yczlz4am038g8r74iy8w43gx8801h70.nar.bz2
|
||||
Compression: bzip2
|
||||
FileHash: sha256:0zzjpdz46mdn74v09m053yczlz4am038g8r74iy8w43gx8801h70
|
||||
FileSize: 24473768
|
||||
NarHash: sha256:0s491y1h9hxj5ghiizlxk7ax6jwbha00zwn7lpyd5xg5bhf60vzg
|
||||
NarSize: 109521136
|
||||
References: 2ma2k0ys8knh4an48n28vigcmc2z8773-linux-headers-2.6.23.16 ...
|
||||
Deriver: 7akyyc87ka32xwmqza9dvyg5pwx3j212-glibc-2.7.drv
|
||||
</screen>
|
||||
|
||||
The fields are as follows:
|
||||
|
||||
<variablelist>
|
||||
|
||||
<varlistentry><term><literal>StorePath</literal></term>
|
||||
|
||||
<listitem><para>The full store path, including the name part
|
||||
(e.g., <literal>glibc-2.7</literal>). It must match the
|
||||
requested store path.</para></listitem>
|
||||
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry><term><literal>URL</literal></term>
|
||||
|
||||
<listitem><para>The URL of the NAR, relative to the binary cache
|
||||
URL.</para></listitem>
|
||||
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry><term><literal>Compression</literal></term>
|
||||
|
||||
<listitem><para>The compression method; either
|
||||
<literal>xz</literal> or
|
||||
<literal>bzip2</literal>.</para></listitem>
|
||||
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry><term><literal>FileHash</literal></term>
|
||||
|
||||
<listitem><para>The SHA-256 hash of the compressed
|
||||
NAR.</para></listitem>
|
||||
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry><term><literal>FileSize</literal></term>
|
||||
|
||||
<listitem><para>The size of the compressed NAR.</para></listitem>
|
||||
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry><term><literal>NarHash</literal></term>
|
||||
|
||||
<listitem><para>The SHA-256 hash of the uncompressed NAR. This is
|
||||
equal to the hash of the store path as returned by
|
||||
<command>nix-store -q --hash
|
||||
<replaceable>p</replaceable></command>.</para></listitem>
|
||||
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry><term><literal>NarSize</literal></term>
|
||||
|
||||
<listitem><para>The size of the uncompressed NAR.</para></listitem>
|
||||
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry><term><literal>References</literal></term>
|
||||
|
||||
<listitem><para>The references of the store path, without the Nix
|
||||
store prefix.</para></listitem>
|
||||
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry><term><literal>Deriver</literal></term>
|
||||
|
||||
<listitem><para>The deriver of the store path, without the Nix
|
||||
store prefix. This field is optional.</para></listitem>
|
||||
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry><term><literal>System</literal></term>
|
||||
|
||||
<listitem><para>The Nix platform type of this binary, if known.
|
||||
This field is optional.</para></listitem>
|
||||
|
||||
</varlistentry>
|
||||
|
||||
</variablelist>
|
||||
|
||||
</para>
|
||||
|
||||
<para>Thus, in our example, after recursively ensuring that the
|
||||
references exist (e.g.,
|
||||
<filename>/nix/store/2ma2k0ys8knh4an48n28vigcmc2z8773-linux-headers-2.6.23.16</filename>),
|
||||
Nix will fetch <screen>
|
||||
http://cache.nixos.org/nar/0zzjpdz46mdn74v09m053yczlz4am038g8r74iy8w43gx8801h70.nar.bz2
|
||||
</screen> and decompress and unpack it to
|
||||
<filename>/nix/store/a8922c0h87iilxzzvwn2hmv8x210aqb9-glibc-2.7</filename>.</para>
|
||||
|
||||
</refsection>
|
||||
|
||||
|
||||
</refentry>
|
||||
|
||||
@@ -0,0 +1,142 @@
|
||||
<refentry xmlns="http://docbook.org/ns/docbook"
|
||||
xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||
xmlns:xi="http://www.w3.org/2001/XInclude"
|
||||
xml:id="sec-nix-shell">
|
||||
|
||||
<refmeta>
|
||||
<refentrytitle>nix-shell</refentrytitle>
|
||||
<manvolnum>1</manvolnum>
|
||||
<refmiscinfo class="source">Nix</refmiscinfo>
|
||||
<refmiscinfo class="version"><xi:include href="version.txt" parse="text"/></refmiscinfo>
|
||||
</refmeta>
|
||||
|
||||
<refnamediv>
|
||||
<refname>nix-shell</refname>
|
||||
<refpurpose>start an interactive shell based on a Nix expression</refpurpose>
|
||||
</refnamediv>
|
||||
|
||||
<refsynopsisdiv>
|
||||
<cmdsynopsis>
|
||||
<command>nix-shell</command>
|
||||
<xi:include xmlns:xi="http://www.w3.org/2001/XInclude" href="opt-common-syn.xml#xmlns(db=http://docbook.org/ns/docbook)xpointer(/db:nop/*)" />
|
||||
<arg><option>--arg</option> <replaceable>name</replaceable> <replaceable>value</replaceable></arg>
|
||||
<arg><option>--argstr</option> <replaceable>name</replaceable> <replaceable>value</replaceable></arg>
|
||||
<arg>
|
||||
<group choice='req'>
|
||||
<arg choice='plain'><option>--attr</option></arg>
|
||||
<arg choice='plain'><option>-A</option></arg>
|
||||
</group>
|
||||
<replaceable>attrPath</replaceable>
|
||||
</arg>
|
||||
<arg><option>--command</option> <replaceable>cmd</replaceable></arg>
|
||||
<arg><option>--exclude</option> <replaceable>regexp</replaceable></arg>
|
||||
<arg><option>--pure</option></arg>
|
||||
<arg choice='plain' rep='repeat'><replaceable>paths</replaceable></arg>
|
||||
</cmdsynopsis>
|
||||
</refsynopsisdiv>
|
||||
|
||||
<refsection><title>Description</title>
|
||||
|
||||
<para>The command <command>nix-shell</command> will build
|
||||
the dependencies of the specified derivation, but not the derivation
|
||||
itself. It will then start an interactive shell in which all
|
||||
environment variables defined by the derivation have been set to their
|
||||
corresponding values, and the script <literal>$stdenv/setup</literal>
|
||||
has been sourced. This is useful for reproducing the environment of a
|
||||
derivation for development.</para>
|
||||
|
||||
</refsection>
|
||||
|
||||
|
||||
<refsection><title>Options</title>
|
||||
|
||||
<para>All options not listed here are passed to <command>nix-store
|
||||
--realise</command>, except for <option>--arg</option> and
|
||||
<option>--attr</option> / <option>-A</option> which are passed to
|
||||
<command>nix-instantiate</command>. <phrase condition="manual">See
|
||||
also <xref linkend="sec-common-options" />.</phrase></para>
|
||||
|
||||
<variablelist>
|
||||
|
||||
<varlistentry><term><option>--command</option> <replaceable>cmd</replaceable></term>
|
||||
|
||||
<listitem><para>In the environment of the derivation, run the
|
||||
shell command <replaceable>cmd</replaceable> instead of starting
|
||||
an interactive shell. However, if you end the shell command with
|
||||
<literal>return</literal>, you still get an interactive shell.
|
||||
This can be useful for doing any additional
|
||||
initialisation.</para></listitem>
|
||||
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry><term><option>--exclude</option> <replaceable>regexp</replaceable></term>
|
||||
|
||||
<listitem><para>Do not build any dependencies whose store path
|
||||
matches the regular expression <replaceable>regexp</replaceable>.
|
||||
This option may be specified multiple times.</para></listitem>
|
||||
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry><term><option>--pure</option></term>
|
||||
|
||||
<listitem><para>If this flag is specified, the environment is
|
||||
almost entirely cleared before the interactive shell is started,
|
||||
so you get an environment that more closely corresponds to the
|
||||
“real” Nix build. A few variables, in particular
|
||||
<envar>HOME</envar>, <envar>USER</envar> and
|
||||
<envar>DISPLAY</envar>, are retained. Note that
|
||||
<filename>~/.bashrc</filename> and (depending on your Bash
|
||||
installation) <filename>/etc/bashrc</filename> are still sourced,
|
||||
so any variables set there will affect the interactive
|
||||
shell.</para></listitem>
|
||||
|
||||
</varlistentry>
|
||||
|
||||
</variablelist>
|
||||
|
||||
<para>The following common options are supported:</para>
|
||||
|
||||
<variablelist condition="manpage">
|
||||
<xi:include href="opt-common.xml#xmlns(db=http://docbook.org/ns/docbook)xpointer(//db:variablelist[@xml:id='opt-common']/*)" />
|
||||
</variablelist>
|
||||
|
||||
</refsection>
|
||||
|
||||
|
||||
<refsection><title>Examples</title>
|
||||
|
||||
<para>To build the dependencies of the package Pan, and start an
|
||||
interactive shell in which to build it:
|
||||
|
||||
<screen>
|
||||
$ nix-shell '<nixpkgs>' -A pan
|
||||
$ unpackPhase
|
||||
$ cd pan-*
|
||||
$ configurePhase
|
||||
$ buildPhase
|
||||
$ ./pan/gui/pan
|
||||
</screen>
|
||||
|
||||
To clear the environment first, and do some additional automatic
|
||||
initialisation of the interactive shell:
|
||||
|
||||
<screen>
|
||||
$ nix-shell '<nixpkgs>' -A pan --pure \
|
||||
--command 'export NIX_DEBUG=1; export NIX_CORES=8; return'
|
||||
</screen>
|
||||
|
||||
</para>
|
||||
|
||||
</refsection>
|
||||
|
||||
|
||||
<refsection condition="manpage"><title>Environment variables</title>
|
||||
|
||||
<variablelist>
|
||||
<xi:include href="env-common.xml#xmlns(db=http://docbook.org/ns/docbook)xpointer(//db:variablelist[@xml:id='env-common']/*)" />
|
||||
</variablelist>
|
||||
|
||||
</refsection>
|
||||
|
||||
|
||||
</refentry>
|
||||
+84
-11
@@ -164,7 +164,7 @@ the specified store paths. Realisation is a somewhat overloaded term:
|
||||
exist in the file system). If the path is already valid, we are
|
||||
done immediately. Otherwise, the path and any missing paths in its
|
||||
closure may be produced through substitutes. If there are no
|
||||
(succesful) subsitutes, realisation fails.</para></listitem>
|
||||
(successful) subsitutes, realisation fails.</para></listitem>
|
||||
|
||||
</itemizedlist>
|
||||
|
||||
@@ -174,10 +174,26 @@ the specified store paths. Realisation is a somewhat overloaded term:
|
||||
output. (For non-derivations argument, the argument itself is
|
||||
printed.)</para>
|
||||
|
||||
<para>If the <option>--dry-run</option> option is used, then
|
||||
<command>nix-store</command> will print on standard error a
|
||||
description of what packages would be built or downloaded, and then
|
||||
quit.</para>
|
||||
<para>The following flags are available:</para>
|
||||
|
||||
<variablelist>
|
||||
|
||||
<varlistentry><term><option>--dry-run</option></term>
|
||||
|
||||
<listitem><para>Print on standard error a description of what
|
||||
packages would be built or downloaded, without actually performing
|
||||
the operation.</para></listitem>
|
||||
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry><term><option>--ignore-unknown</option></term>
|
||||
|
||||
<listitem><para>If a non-derivation path does not have a
|
||||
substitute, then silently ignore it.</para></listitem>
|
||||
|
||||
</varlistentry>
|
||||
|
||||
</variablelist>
|
||||
|
||||
</refsection>
|
||||
|
||||
@@ -773,6 +789,7 @@ $ nix-store --add ./foo.c
|
||||
<command>nix-store</command>
|
||||
<arg choice='plain'><option>--verify</option></arg>
|
||||
<arg><option>--check-contents</option></arg>
|
||||
<arg><option>--repair</option></arg>
|
||||
</cmdsynopsis>
|
||||
</refsection>
|
||||
|
||||
@@ -785,7 +802,7 @@ automatically repaired. Inconsistencies are generally the result of
|
||||
the Nix store or database being modified by non-Nix tools, or of bugs
|
||||
in Nix itself.</para>
|
||||
|
||||
<para>There is one option:
|
||||
<para>This operation has the following options:
|
||||
|
||||
<variablelist>
|
||||
|
||||
@@ -800,6 +817,16 @@ in Nix itself.</para>
|
||||
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry><term><option>--repair</option></term>
|
||||
|
||||
<listitem><para>If any valid path is missing from the store, or
|
||||
(if <option>--check-contents</option> is given) the contents of a
|
||||
valid path has been modified, then try to repair the path by
|
||||
redownloading it. See <command>nix-store --repair-path</command>
|
||||
for details.</para></listitem>
|
||||
|
||||
</varlistentry>
|
||||
|
||||
</variablelist>
|
||||
|
||||
</para>
|
||||
@@ -825,7 +852,7 @@ in Nix itself.</para>
|
||||
|
||||
<refsection><title>Description</title>
|
||||
|
||||
<para>The operation <option>--verify-paths</option> compares the
|
||||
<para>The operation <option>--verify-path</option> compares the
|
||||
contents of the given store paths to their cryptographic hashes stored
|
||||
in Nix’s database. For every changed path, it prints a warning
|
||||
message. The exit status is 0 if no path has changed, and 1
|
||||
@@ -848,6 +875,52 @@ $ nix-store --verify-path $(nix-store -qR $(which svn))
|
||||
</refsection>
|
||||
|
||||
|
||||
<!--######################################################################-->
|
||||
|
||||
<refsection><title>Operation <option>--repair-path</option></title>
|
||||
|
||||
<refsection>
|
||||
<title>Synopsis</title>
|
||||
<cmdsynopsis>
|
||||
<command>nix-store</command>
|
||||
<arg choice='plain'><option>--repair-path</option></arg>
|
||||
<arg choice='plain' rep='repeat'><replaceable>paths</replaceable></arg>
|
||||
</cmdsynopsis>
|
||||
</refsection>
|
||||
|
||||
<refsection><title>Description</title>
|
||||
|
||||
<para>The operation <option>--repair-path</option> attempts to
|
||||
“repair” the specified paths by redownloading them using the available
|
||||
substituters. If no substitutes are available, then repair is not
|
||||
possible.</para>
|
||||
|
||||
<warning><para>During repair, there is a very small time window during
|
||||
which the old path (if it exists) is moved out of the way and replaced
|
||||
with the new path. If repair is interrupted in between, then the
|
||||
system may be left in a broken state (e.g., if the path contains a
|
||||
critical system component like the GNU C Library).</para></warning>
|
||||
|
||||
</refsection>
|
||||
|
||||
<refsection><title>Example</title>
|
||||
|
||||
<screen>
|
||||
$ nix-store --verify-path /nix/store/dj7a81wsm1ijwwpkks3725661h3263p5-glibc-2.13
|
||||
path `/nix/store/dj7a81wsm1ijwwpkks3725661h3263p5-glibc-2.13' was modified!
|
||||
expected hash `2db57715ae90b7e31ff1f2ecb8c12ec1cc43da920efcbe3b22763f36a1861588',
|
||||
got `481c5aa5483ebc97c20457bb8bca24deea56550d3985cda0027f67fe54b808e4'
|
||||
|
||||
$ nix-store --repair-path /nix/store/dj7a81wsm1ijwwpkks3725661h3263p5-glibc-2.13
|
||||
fetching path `/nix/store/d7a81wsm1ijwwpkks3725661h3263p5-glibc-2.13'...
|
||||
…
|
||||
</screen>
|
||||
|
||||
</refsection>
|
||||
|
||||
</refsection>
|
||||
|
||||
|
||||
<!--######################################################################-->
|
||||
|
||||
<refsection xml:id='refsec-nix-store-dump'><title>Operation <option>--dump</option></title>
|
||||
@@ -1169,9 +1242,9 @@ variable <envar>_args</envar>.</para>
|
||||
$ nix-store --print-env $(nix-instantiate '<nixpkgs>' -A firefox)
|
||||
<replaceable>…</replaceable>
|
||||
export src; src='/nix/store/plpj7qrwcz94z2psh6fchsi7s8yihc7k-firefox-12.0.source.tar.bz2'
|
||||
export stdenv; stdenv='/nix/store/7c8asx3yfrg5dg1gzhzyq2236zfgibnm-stdenv'
|
||||
export stdenv; stdenv='/nix/store/7c8asx3yfrg5dg1gzhzyq2236zfgibnn-stdenv'
|
||||
export system; system='x86_64-linux'
|
||||
export _args; _args='-e /nix/store/9krlzvny65gdc8s7kpb6lkx8cd02c25b-default-builder.sh'
|
||||
export _args; _args='-e /nix/store/9krlzvny65gdc8s7kpb6lkx8cd02c25c-default-builder.sh'
|
||||
</screen>
|
||||
|
||||
</refsection>
|
||||
@@ -1246,8 +1319,8 @@ paths.</para>
|
||||
<refsection><title>Example</title>
|
||||
|
||||
<screen>
|
||||
$ nix-store --clear-failed-path /nix/store/000zi5dcla86l92jn1g997jb06sidm7x-perl-PerlMagick-6.59
|
||||
$ nix-store --clear-failed-path *
|
||||
$ nix-store --clear-failed-paths /nix/store/000zi5dcla86l92jn1g997jb06sidm7x-perl-PerlMagick-6.59
|
||||
$ nix-store --clear-failed-paths *
|
||||
</screen>
|
||||
|
||||
</refsection>
|
||||
|
||||
@@ -37,6 +37,11 @@
|
||||
<option>-I</option>
|
||||
<replaceable>path</replaceable>
|
||||
</arg>
|
||||
<arg>
|
||||
<option>--option</option>
|
||||
<replaceable>name</replaceable>
|
||||
<replaceable>value</replaceable>
|
||||
</arg>
|
||||
<sbr />
|
||||
|
||||
</nop>
|
||||
|
||||
@@ -181,7 +181,7 @@
|
||||
from, say, a network repository. If the repository is down, the
|
||||
realisation of the derivation will fail. When this option is
|
||||
specified, Nix will build the derivation instead. Thus,
|
||||
installation from binaries falls back on nstallation from source.
|
||||
installation from binaries falls back on installation from source.
|
||||
This option is not the default since it is generally not desirable
|
||||
for a transient failure in obtaining the substitutes to lead to a
|
||||
full build from source (with the related consumption of
|
||||
@@ -236,7 +236,7 @@
|
||||
interpreted by the <command>nix-log2xml</command> tool in the
|
||||
Nix source distribution. The resulting XML file can be fed into
|
||||
the <command>log2html.xsl</command> stylesheet to create an HTML
|
||||
file that can be browsed interactively, using Javascript to
|
||||
file that can be browsed interactively, using JavaScript to
|
||||
expand and collapse parts of the output.</para></listitem>
|
||||
|
||||
</varlistentry>
|
||||
@@ -337,20 +337,42 @@
|
||||
|
||||
<listitem><para>Causes Nix to print out a stack trace in case of Nix
|
||||
expression evaluation errors.</para></listitem>
|
||||
|
||||
|
||||
</varlistentry>
|
||||
|
||||
|
||||
<varlistentry><term><option>-I</option> <replaceable>path</replaceable></term>
|
||||
|
||||
<listitem><para>Add a path to the Nix expression search path. See
|
||||
the <envar>NIX_PATH</envar> environment variable for details. Paths
|
||||
added through <option>-I</option> take precedence over
|
||||
<envar>NIX_PATH</envar>.</para></listitem>
|
||||
<listitem><para>Add a path to the Nix expression search path. This
|
||||
option may be given multiple times. See the <envar>NIX_PATH</envar>
|
||||
environment variable for information on the semantics of the Nix
|
||||
search path. Paths added through <option>-I</option> take
|
||||
precedence over <envar>NIX_PATH</envar>.</para></listitem>
|
||||
|
||||
</varlistentry>
|
||||
|
||||
|
||||
<varlistentry><term><option>--option</option> <replaceable>name</replaceable> <replaceable>value</replaceable></term>
|
||||
|
||||
<listitem><para>Set the Nix configuration option
|
||||
<replaceable>name</replaceable> to <replaceable>value</replaceable>.
|
||||
This overrides settings in the Nix configuration file (see
|
||||
<citerefentry><refentrytitle>nix.conf</refentrytitle><manvolnum>5</manvolnum></citerefentry>).</para></listitem>
|
||||
|
||||
</varlistentry>
|
||||
|
||||
|
||||
<varlistentry><term><option>--repair</option></term>
|
||||
|
||||
<listitem><para>Fix corrupted or missing store paths by
|
||||
redownloading or rebuilding them. Note that this is slow because it
|
||||
requires computing a cryptographic hash of the contents of every
|
||||
path in the closure of the build. Also note the warning under
|
||||
<command>nix-store --repair-path</command>.</para></listitem>
|
||||
|
||||
</varlistentry>
|
||||
|
||||
|
||||
</variablelist>
|
||||
|
||||
|
||||
|
||||
@@ -129,7 +129,7 @@ downloading binaries from <systemitem
|
||||
class='fqdomainname'>nixos.org</systemitem>, instead of building
|
||||
them from source. This might still take a while since all
|
||||
dependencies must be downloaded, but on a reasonably fast connection
|
||||
such as an DSL line it’s on the order of a few minutes.</para>
|
||||
such as a DSL line it’s on the order of a few minutes.</para>
|
||||
|
||||
<para>Naturally, packages can also be uninstalled:
|
||||
|
||||
@@ -175,21 +175,6 @@ upgrading `coreutils-5.0' to `coreutils-5.2.1'</screen>
|
||||
|
||||
</para>
|
||||
|
||||
<para>If you grow bored of specifying the Nix expressions using
|
||||
<parameter>-f</parameter> all the time, you can set a default
|
||||
location:
|
||||
|
||||
<screen>
|
||||
$ nix-env -I nixpkgs-<replaceable>version</replaceable></screen>
|
||||
|
||||
After this you can just say, for instance, <literal>nix-env -u
|
||||
'*'</literal>.<footnote><para>Setting a default using
|
||||
<parameter>-I</parameter> currently clashes with using Nix channels,
|
||||
since <literal>nix-channel --update</literal> calls <literal>nix-env
|
||||
-I</literal> to set the default to the Nix expressions it downloaded
|
||||
from the channel, replacing whatever default you had
|
||||
set.</para></footnote></para>
|
||||
|
||||
</section>
|
||||
|
||||
|
||||
@@ -240,7 +225,7 @@ uses is to create directory trees of symlinks to
|
||||
themselves (though automatically generated by
|
||||
<command>nix-env</command>), so they too reside in the Nix store. For
|
||||
instance, in <xref linkend='fig-user-environments' /> the user
|
||||
environment <filename>/nix/store/5mq2jcn36ldl...-user-env</filename>
|
||||
environment <filename>/nix/store/0c1p5z4kda11...-user-env</filename>
|
||||
contains a symlink to just Subversion 1.1.2 (arrows in the figure
|
||||
indicate symlinks). This would be what we would obtain if we had done
|
||||
|
||||
@@ -458,7 +443,7 @@ URL.</para>
|
||||
<command>nix-channel --add</command>, e.g.,
|
||||
|
||||
<screen>
|
||||
$ nix-channel --add http://nixos.org/releases/nixpkgs/channels/nixpkgs-unstable</screen>
|
||||
$ nix-channel --add http://nixos.org/channels/nixpkgs-unstable</screen>
|
||||
|
||||
subscribes you to a channel that always contains that latest version
|
||||
of the Nix Packages collection. (Instead of
|
||||
@@ -553,7 +538,7 @@ dependencies) to a file, and then unpack that file into another Nix
|
||||
store. For example,
|
||||
|
||||
<screen>
|
||||
$ nix-store --export $(type -p firefox) > firefox.closure</screen>
|
||||
$ nix-store --export $(nix-store -qR $(type -p firefox)) > firefox.closure</screen>
|
||||
|
||||
writes the closure of Firefox to a file. You can then copy this file
|
||||
to another machine and install the closure:
|
||||
@@ -567,7 +552,7 @@ another command, e.g. to copy and install a closure directly to/on
|
||||
another machine:
|
||||
|
||||
<screen>
|
||||
$ nix-store --export $(type -p firefox) | bzip2 | \
|
||||
$ nix-store --export $(nix-store -qR $(type -p firefox)) | bzip2 | \
|
||||
ssh alice@itchy.example.org "bunzip2 | nix-store --import"</screen>
|
||||
|
||||
But note that <command>nix-copy-closure</command> is generally more
|
||||
@@ -598,7 +583,7 @@ $ nix-env -i firefox</screen>
|
||||
and Nix has to build a path that it sees is already present in
|
||||
<filename>/mnt/nix</filename>, then it will just copy from there
|
||||
instead of building it from source.</para>
|
||||
|
||||
|
||||
|
||||
</section>
|
||||
|
||||
|
||||
@@ -14,9 +14,10 @@ to the following chapters.</para>
|
||||
<listitem><para>Download a source tarball or RPM or Debian/Ubuntu
|
||||
package from <link xlink:href='http://nixos.org/'/>. Build source
|
||||
distributions using the regular sequence:
|
||||
|
||||
|
||||
<screen>
|
||||
$ tar xvfj nix-<replaceable>version</replaceable>.tar.bz2
|
||||
$ cd nix-<replaceable>version</replaceable>
|
||||
$ ./configure
|
||||
$ make
|
||||
$ make install <lineannotation>(as root)</lineannotation></screen>
|
||||
@@ -35,13 +36,13 @@ changed using
|
||||
|
||||
<listitem><para>You should add
|
||||
<filename><replaceable>prefix</replaceable>/etc/profile.d/nix.sh</filename>
|
||||
to your <filename>~/.bashrc</filename> (or some other login
|
||||
to your <filename>~/.profile</filename> (or some other login
|
||||
file).</para></listitem>
|
||||
|
||||
<listitem><para>Subscribe to the Nix Packages channel.
|
||||
|
||||
<screen>
|
||||
$ nix-channel --add http://nixos.org/releases/nixpkgs/channels/nixpkgs-unstable</screen>
|
||||
$ nix-channel --add http://nixos.org/channels/nixpkgs-unstable</screen>
|
||||
|
||||
</para></listitem>
|
||||
|
||||
@@ -69,7 +70,7 @@ libxslt-1.1.0
|
||||
</para></listitem>
|
||||
|
||||
<listitem><para>Install some packages from the channel:
|
||||
|
||||
|
||||
<screen>
|
||||
$ nix-env -i hello firefox <replaceable>...</replaceable> </screen>
|
||||
|
||||
@@ -87,7 +88,7 @@ $ firefox
|
||||
<lineannotation>(read Slashdot or something)</lineannotation></screen>
|
||||
|
||||
</para></listitem>
|
||||
|
||||
|
||||
<listitem><para>Uninstall a package:
|
||||
|
||||
<screen>
|
||||
|
||||
@@ -6,6 +6,418 @@
|
||||
|
||||
|
||||
|
||||
<!--==================================================================-->
|
||||
|
||||
<section xml:id="ssec-relnotes-1.6.0"><title>Release 1.6.0 (September 10, 2013)</title>
|
||||
|
||||
<para>In addition to the usual bug fixes, this release has several new
|
||||
features:</para>
|
||||
|
||||
<itemizedlist>
|
||||
|
||||
<listitem>
|
||||
<para>The command <command>nix-build --run-env</command> has been
|
||||
renamed to <command>nix-shell</command>.</para>
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
<para><command>nix-shell</command> now sources
|
||||
<filename>$stdenv/setup</filename> <emphasis>inside</emphasis> the
|
||||
interactive shell, rather than in a parent shell. This ensures
|
||||
that shell functions defined by <literal>stdenv</literal> can be
|
||||
used in the interactive shell.</para>
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
<para><command>nix-shell</command> has a new flag
|
||||
<option>--pure</option> to clear the environment, so you get an
|
||||
environment that more closely corresponds to the “real” Nix build.
|
||||
</para>
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
<para><command>nix-shell</command> now sets the shell prompt
|
||||
(<envar>PS1</envar>) to ensure that Nix shells are distinguishable
|
||||
from your regular shells.</para>
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
<para><command>nix-env</command> no longer requires a
|
||||
<literal>*</literal> argument to match all packages, so
|
||||
<literal>nix-env -qa</literal> is equivalent to <literal>nix-env
|
||||
-qa '*'</literal>.</para>
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
<para><command>nix-env -i</command> has a new flag
|
||||
<option>--remove-all</option> (<option>-r</option>) to remove all
|
||||
previous packages from the profile. This makes it easier to do
|
||||
declarative package management similar to NixOS’s
|
||||
<option>environment.systemPackages</option>. For instance, if you
|
||||
have a specification <filename>my-packages.nix</filename> like this:
|
||||
|
||||
<programlisting>
|
||||
with import <nixpkgs> {};
|
||||
[ thunderbird
|
||||
geeqie
|
||||
...
|
||||
]
|
||||
</programlisting>
|
||||
|
||||
then after any change to this file, you can run:
|
||||
|
||||
<screen>
|
||||
$ nix-env -f my-packages.nix -ir
|
||||
</screen>
|
||||
|
||||
to update your profile to match the specification.</para>
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
<para>The ‘<literal>with</literal>’ language construct is now more
|
||||
lazy. It only evaluates its argument if a variable might actually
|
||||
refer to an attribute in the argument. For instance, this now
|
||||
works:
|
||||
|
||||
<programlisting>
|
||||
let
|
||||
pkgs = with pkgs; { foo = "old"; bar = foo; } // overrides;
|
||||
overrides = { foo = "new"; };
|
||||
in pkgs.bar
|
||||
</programlisting>
|
||||
|
||||
This evaluates to <literal>"new"</literal>, while previously it
|
||||
gave an “infinite recursion” error.</para>
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
<para>Nix now has proper integer arithmetic operators. For
|
||||
instance, you can write <literal>x + y</literal> instead of
|
||||
<literal>builtins.add x y</literal>, or <literal>x <
|
||||
y</literal> instead of <literal>builtins.lessThan x y</literal>.
|
||||
The comparison operators also work on strings.</para>
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
<para>On 64-bit systems, Nix integers are now 64 bits rather than
|
||||
32 bits.</para>
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
<para>When using the Nix daemon, the <command>nix-daemon</command>
|
||||
worker process now runs on the same CPU as the client, on systems
|
||||
that support setting CPU affinity. This gives a significant speedup
|
||||
on some systems.</para>
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
<para>If a stack overflow occurs in the Nix evaluator, you now get
|
||||
a proper error message (rather than “Segmentation fault”) on some
|
||||
systems.</para>
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
<para>In addition to directories, you can now bind-mount regular
|
||||
files in chroots through the (now misnamed) option
|
||||
<option>build-chroot-dirs</option>.</para>
|
||||
</listitem>
|
||||
|
||||
</itemizedlist>
|
||||
|
||||
<para>This release has contributions from Domen Kožar, Eelco Dolstra,
|
||||
Florian Friesdorf, Gergely Risko, Ivan Kozik, Ludovic Courtès and Shea
|
||||
Levy.</para>
|
||||
|
||||
</section>
|
||||
|
||||
|
||||
<!--==================================================================-->
|
||||
|
||||
<section xml:id="ssec-relnotes-1.5.3"><title>Release 1.5.3 (June 17, 2013)</title>
|
||||
|
||||
<para>This is primarily a bug fix release. The following changes are
|
||||
noteworthy:</para>
|
||||
|
||||
<itemizedlist>
|
||||
|
||||
<listitem>
|
||||
<para>Yet another security bug involving hard links to files
|
||||
outside the store was fixed. This bug only affected multi-user
|
||||
installations that do not have hard link restrictions
|
||||
enabled. (NixOS is thus not vulnerable.)</para>
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
<para>The default binary cache URL has changed from
|
||||
<uri>http://nixos.org/binary-cache</uri> to
|
||||
<uri>http://cache.nixos.org</uri>. The latter is hosted on Amazon
|
||||
CloudFront (courtesy of <link
|
||||
xlink:href="http://www.logicblox.com/">LogicBlox</link>) and
|
||||
should provide better performance for users in both Europe and
|
||||
North America.</para>
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
<para>The binary cache substituter now prints a warning message if
|
||||
fetching information from the cache takes more than five seconds.
|
||||
Thus network or server problems no longer make Nix appear to just
|
||||
hang.</para>
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
<para>Stack traces now show function names, e.g.
|
||||
<screen>
|
||||
while evaluating `concatMapStrings' at `<replaceable>...</replaceable>/nixpkgs/pkgs/lib/strings.nix:18:25':
|
||||
</screen>
|
||||
Also, if a function is called with an unexpected argument, Nix
|
||||
now shows the name of the argument.
|
||||
</para>
|
||||
</listitem>
|
||||
|
||||
</itemizedlist>
|
||||
|
||||
</section>
|
||||
|
||||
|
||||
<!--==================================================================-->
|
||||
|
||||
<section xml:id="ssec-relnotes-1.5.2"><title>Release 1.5.2 (May 13, 2013)</title>
|
||||
|
||||
<para>This is primarily a bug fix release. It has contributions from
|
||||
Eelco Dolstra, Lluís Batlle i Rossell and Shea Levy.</para>
|
||||
|
||||
</section>
|
||||
|
||||
|
||||
<!--==================================================================-->
|
||||
|
||||
<section xml:id="ssec-relnotes-1.5.1"><title>Release 1.5.1 (February 28, 2013)</title>
|
||||
|
||||
<para>The bug fix to the bug fix had a bug itself, of course. But
|
||||
this time it will work for sure!</para>
|
||||
|
||||
</section>
|
||||
|
||||
|
||||
<!--==================================================================-->
|
||||
|
||||
<section xml:id="ssec-relnotes-1.5"><title>Release 1.5 (February 27, 2013)</title>
|
||||
|
||||
<para>This is a brown paper bag release to fix a regression introduced
|
||||
by the hard link security fix in 1.4.</para>
|
||||
|
||||
</section>
|
||||
|
||||
|
||||
<!--==================================================================-->
|
||||
|
||||
<section xml:id="ssec-relnotes-1.4"><title>Release 1.4 (February 26, 2013)</title>
|
||||
|
||||
<para>This release fixes a security bug in multi-user operation. It
|
||||
was possible for derivations to cause the mode of files outside of the
|
||||
Nix store to be changed to 444 (read-only but world-readable) by
|
||||
creating hard links to those files (<link
|
||||
xlink:href="https://github.com/NixOS/nix/commit/5526a282b5b44e9296e61e07d7d2626a79141ac4">details</link>).</para>
|
||||
|
||||
<para>There are also the following improvements:</para>
|
||||
|
||||
<itemizedlist>
|
||||
|
||||
<listitem><para>New built-in function:
|
||||
<function>builtins.hashString</function>.</para></listitem>
|
||||
|
||||
<listitem><para>Build logs are now stored in
|
||||
<filename>/nix/var/log/nix/drvs/<replaceable>XX</replaceable>/</filename>,
|
||||
where <replaceable>XX</replaceable> is the first two characters of
|
||||
the derivation. This is useful on machines that keep a lot of build
|
||||
logs (such as Hydra servers).</para></listitem>
|
||||
|
||||
<listitem><para>The function <function>corepkgs/fetchurl</function>
|
||||
can now make the downloaded file executable. This will allow
|
||||
getting rid of all bootstrap binaries in the Nixpkgs source
|
||||
tree.</para></listitem>
|
||||
|
||||
<listitem><para>Language change: The expression <literal>"${./path}
|
||||
..."</literal> now evaluates to a string instead of a
|
||||
path.</para></listitem>
|
||||
|
||||
</itemizedlist>
|
||||
|
||||
</section>
|
||||
|
||||
|
||||
<!--==================================================================-->
|
||||
|
||||
<section xml:id="ssec-relnotes-1.3"><title>Release 1.3 (January 4, 2013)</title>
|
||||
|
||||
<para>This is primarily a bug fix release. When this version is first
|
||||
run on Linux, it removes any immutable bits from the Nix store and
|
||||
increases the schema version of the Nix store. (The previous release
|
||||
removed support for setting the immutable bit; this release clears any
|
||||
remaining immutable bits to make certain operations more
|
||||
efficient.)</para>
|
||||
|
||||
<para>This release has contributions from Eelco Dolstra and Stuart
|
||||
Pernsteiner.</para>
|
||||
|
||||
</section>
|
||||
|
||||
|
||||
<!--==================================================================-->
|
||||
|
||||
<section xml:id="ssec-relnotes-1.2"><title>Release 1.2 (December 6, 2012)</title>
|
||||
|
||||
<para>This release has the following improvements and changes:</para>
|
||||
|
||||
<itemizedlist>
|
||||
|
||||
<listitem>
|
||||
<para>Nix has a new binary substituter mechanism: the
|
||||
<emphasis>binary cache</emphasis>. A binary cache contains
|
||||
pre-built binaries of Nix packages. Whenever Nix wants to build a
|
||||
missing Nix store path, it will check a set of binary caches to
|
||||
see if any of them has a pre-built binary of that path. The
|
||||
configuration setting <option>binary-caches</option> contains a
|
||||
list of URLs of binary caches. For instance, doing
|
||||
<screen>
|
||||
$ nix-env -i thunderbird --option binary-caches http://cache.nixos.org
|
||||
</screen>
|
||||
will install Thunderbird and its dependencies, using the available
|
||||
pre-built binaries in <uri>http://cache.nixos.org</uri>.
|
||||
The main advantage over the old “manifest”-based method of getting
|
||||
pre-built binaries is that you don’t have to worry about your
|
||||
manifest being in sync with the Nix expressions you’re installing
|
||||
from; i.e., you don’t need to run <command>nix-pull</command> to
|
||||
update your manifest. It’s also more scalable because you don’t
|
||||
need to redownload a giant manifest file every time.
|
||||
</para>
|
||||
|
||||
<para>A Nix channel can provide a binary cache URL that will be
|
||||
used automatically if you subscribe to that channel. If you use
|
||||
the Nixpkgs or NixOS channels
|
||||
(<uri>http://nixos.org/channels</uri>) you automatically get the
|
||||
cache <uri>http://cache.nixos.org</uri>.</para>
|
||||
|
||||
<para>Binary caches are created using <command>nix-push</command>.
|
||||
For details on the operation and format of binary caches, see the
|
||||
<command>nix-push</command> manpage. More details are provided in
|
||||
<link xlink:href="http://lists.science.uu.nl/pipermail/nix-dev/2012-September/009826.html">this
|
||||
nix-dev posting</link>.</para>
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
<para>Multiple output support should now be usable. A derivation
|
||||
can declare that it wants to produce multiple store paths by
|
||||
saying something like
|
||||
<programlisting>
|
||||
outputs = [ "lib" "headers" "doc" ];
|
||||
</programlisting>
|
||||
This will cause Nix to pass the intended store path of each output
|
||||
to the builder through the environment variables
|
||||
<literal>lib</literal>, <literal>headers</literal> and
|
||||
<literal>doc</literal>. Other packages can refer to a specific
|
||||
output by referring to
|
||||
<literal><replaceable>pkg</replaceable>.<replaceable>output</replaceable></literal>,
|
||||
e.g.
|
||||
<programlisting>
|
||||
buildInputs = [ pkg.lib pkg.headers ];
|
||||
</programlisting>
|
||||
If you install a package with multiple outputs using
|
||||
<command>nix-env</command>, each output path will be symlinked
|
||||
into the user environment.</para>
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
<para>Dashes are now valid as part of identifiers and attribute
|
||||
names.</para>
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
<para>The new operation <command>nix-store --repair-path</command>
|
||||
allows corrupted or missing store paths to be repaired by
|
||||
redownloading them. <command>nix-store --verify --check-contents
|
||||
--repair</command> will scan and repair all paths in the Nix
|
||||
store. Similarly, <command>nix-env</command>,
|
||||
<command>nix-build</command>, <command>nix-instantiate</command>
|
||||
and <command>nix-store --realise</command> have a
|
||||
<option>--repair</option> flag to detect and fix bad paths by
|
||||
rebuilding or redownloading them.</para>
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
<para>Nix no longer sets the immutable bit on files in the Nix
|
||||
store. Instead, the recommended way to guard the Nix store
|
||||
against accidental modification on Linux is to make it a read-only
|
||||
bind mount, like this:
|
||||
|
||||
<screen>
|
||||
$ mount --bind /nix/store /nix/store
|
||||
$ mount -o remount,ro,bind /nix/store
|
||||
</screen>
|
||||
|
||||
Nix will automatically make <filename>/nix/store</filename>
|
||||
writable as needed (using a private mount namespace) to allow
|
||||
modifications.</para>
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
<para>Store optimisation (replacing identical files in the store
|
||||
with hard links) can now be done automatically every time a path
|
||||
is added to the store. This is enabled by setting the
|
||||
configuration option <literal>auto-optimise-store</literal> to
|
||||
<literal>true</literal> (disabled by default).</para>
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
<para>Nix now supports <command>xz</command> compression for NARs
|
||||
in addition to <command>bzip2</command>. It compresses about 30%
|
||||
better on typical archives and decompresses about twice as
|
||||
fast.</para>
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
<para>Basic Nix expression evaluation profiling: setting the
|
||||
environment variable <envar>NIX_COUNT_CALLS</envar> to
|
||||
<literal>1</literal> will cause Nix to print how many times each
|
||||
primop or function was executed.</para>
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
<para>New primops: <varname>concatLists</varname>,
|
||||
<varname>elem</varname>, <varname>elemAt</varname> and
|
||||
<varname>filter</varname>.</para>
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
<para>The command <command>nix-copy-closure</command> has a new
|
||||
flag <option>--use-substitutes</option> (<option>-s</option>) to
|
||||
download missing paths on the target machine using the substitute
|
||||
mechanism.</para>
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
<para>The command <command>nix-worker</command> has been renamed
|
||||
to <command>nix-daemon</command>. Support for running the Nix
|
||||
worker in “slave” mode has been removed.</para>
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
<para>The <option>--help</option> flag of every Nix command now
|
||||
invokes <command>man</command>.</para>
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
<para>Chroot builds are now supported on systemd machines.</para>
|
||||
</listitem>
|
||||
|
||||
</itemizedlist>
|
||||
|
||||
<para>This release has contributions from Eelco Dolstra, Florian
|
||||
Friesdorf, Mats Erik Andersson and Shea Levy.</para>
|
||||
|
||||
</section>
|
||||
|
||||
|
||||
<!--==================================================================-->
|
||||
|
||||
<section xml:id="ssec-relnotes-1.1"><title>Release 1.1 (July 18, 2012)</title>
|
||||
@@ -94,6 +506,11 @@
|
||||
<citerefentry><refentrytitle>nix.conf</refentrytitle><manvolnum>5</manvolnum></citerefentry>.</para>
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
<para>When using the Nix daemon, the <option>-s</option> flag in
|
||||
<command>nix-env -qa</command> is now much faster.</para>
|
||||
</listitem>
|
||||
|
||||
</itemizedlist>
|
||||
|
||||
</section>
|
||||
@@ -1029,7 +1446,7 @@ irreversible.</para></warning>
|
||||
</para></listitem>
|
||||
|
||||
|
||||
<listitem><para>You can now unambigously specify which derivation to
|
||||
<listitem><para>You can now unambiguously specify which derivation to
|
||||
build or install in <command>nix-env</command>,
|
||||
<command>nix-instantiate</command> and <command>nix-build</command>
|
||||
using the <option>--attr</option> / <option>-A</option> flags, which
|
||||
@@ -1169,7 +1586,7 @@ irreversible.</para></warning>
|
||||
<itemizedlist>
|
||||
|
||||
<listitem><para>Make sure that the garbage collector can run
|
||||
succesfully when the disk is full
|
||||
successfully when the disk is full
|
||||
(<literal>NIX-18</literal>).</para></listitem>
|
||||
|
||||
<listitem><para><command>nix-env</command> now locks the profile
|
||||
@@ -1524,7 +1941,7 @@ $ nix-env -f .../i686-linux.nix -i -E 'x: x.firefoxWrapper'</screen>
|
||||
</itemizedlist></para></listitem>
|
||||
|
||||
<listitem><para>A hash of the contents of a store path is now stored
|
||||
in the database after a succesful build. This allows you to check
|
||||
in the database after a successful build. This allows you to check
|
||||
whether store paths have been tampered with: <command>nix-store
|
||||
--verify --check-contents</command>.</para></listitem>
|
||||
|
||||
@@ -1688,7 +2105,7 @@ $ nix-env -f .../i686-linux.nix -i -E 'x: x.firefoxWrapper'</screen>
|
||||
<itemizedlist>
|
||||
|
||||
<listitem><para>Derivations for other platforms are filtered out
|
||||
(which can be overriden using
|
||||
(which can be overridden using
|
||||
<option>--system-filter</option>).</para></listitem>
|
||||
|
||||
<listitem><para><option>--install</option> by default now
|
||||
|
||||
@@ -17,7 +17,7 @@ bug tracker</link> for a list of currently known issues.</para>
|
||||
$ nix-env -i docbook-xml
|
||||
...
|
||||
adding /nix/store/s5hyxgm62gk2...-docbook-xml-4.2
|
||||
collission between `/nix/store/s5hyxgm62gk2...-docbook-xml-4.2/xml/dtd/docbook/calstblx.dtd'
|
||||
collision between `/nix/store/s5hyxgm62gk2...-docbook-xml-4.2/xml/dtd/docbook/calstblx.dtd'
|
||||
and `/nix/store/06h377hr4b33...-docbook-xml-4.3/xml/dtd/docbook/calstblx.dtd'
|
||||
at /nix/store/...-builder.pl line 62.</screen>
|
||||
|
||||
@@ -68,7 +68,7 @@ in <filename>/nix/store</filename>, as can be seen using <command>ls
|
||||
$ ls -l /nix/store
|
||||
drwxrwxrwt 32000 nix nix 4620288 Sep 8 15:08 store</screen>
|
||||
|
||||
The <literal>ext2</literal> file system is limited to a inode link
|
||||
The <literal>ext2</literal> file system is limited to an inode link
|
||||
count of 32,000 (each subdirectory increasing the count by one).
|
||||
Furthermore, the <literal>st_nlink</literal> field of the
|
||||
<function>stat</function> system call is a 16-bit value.</para>
|
||||
|
||||
@@ -106,7 +106,7 @@ the single Nix expression in that directory
|
||||
here, the entire remainder of the file is the body of the
|
||||
function; when given the required arguments, the body should
|
||||
describe how to build an instance of the Hello package.</para>
|
||||
|
||||
|
||||
</callout>
|
||||
|
||||
<callout arearefs='ex-hello-nix-co-2'>
|
||||
@@ -190,7 +190,7 @@ perl = perl;</programlisting>
|
||||
with the same name happen to be in scope.</para>
|
||||
|
||||
</callout>
|
||||
|
||||
|
||||
</calloutlist>
|
||||
|
||||
</para>
|
||||
@@ -301,7 +301,7 @@ steps:</para>
|
||||
(<literal>make install</literal>).</para>
|
||||
|
||||
</callout>
|
||||
|
||||
|
||||
</calloutlist>
|
||||
|
||||
<para>If you are wondering about the absence of error checking on the
|
||||
@@ -321,7 +321,7 @@ error check.</para>
|
||||
...
|
||||
|
||||
rec { <co xml:id='ex-hello-composition-co-1' />
|
||||
|
||||
|
||||
hello = import ../applications/misc/hello/ex-1 <co xml:id='ex-hello-composition-co-2' /> { <co xml:id='ex-hello-composition-co-3' />
|
||||
inherit fetchurl stdenv perl;
|
||||
};
|
||||
@@ -330,10 +330,10 @@ rec { <co xml:id='ex-hello-composition-co-1' />
|
||||
inherit fetchurl stdenv;
|
||||
};
|
||||
|
||||
fetchurl = import ../build-support/fetchurl {
|
||||
fetchurl = import ../build-support/fetchurl {
|
||||
inherit stdenv; ...
|
||||
};
|
||||
|
||||
|
||||
stdenv = ...;
|
||||
|
||||
}
|
||||
@@ -476,8 +476,8 @@ that the path denoted by <envar>out</envar> is now
|
||||
will see that the path is already valid and finish immediately. If a
|
||||
build fails, either because it returns a non-zero exit code, because
|
||||
Nix or the builder are killed, or because the machine crashes, then
|
||||
the output path will not be registered as valid. If you try to build
|
||||
the derivation again, Nix will remove the output path if it exists
|
||||
the output paths will not be registered as valid. If you try to build
|
||||
the derivation again, Nix will remove the output paths if they exist
|
||||
(e.g., because the builder died half-way through <literal>make
|
||||
install</literal>) and try again. Note that there is no
|
||||
<quote>negative caching</quote>: Nix doesn't remember that a build
|
||||
@@ -543,7 +543,7 @@ genericBuild <co xml:id='ex-hello-builder2-co-3' /></programlisting>
|
||||
<calloutlist>
|
||||
|
||||
<callout arearefs='ex-hello-builder2-co-1'>
|
||||
|
||||
|
||||
<para>The <envar>buildInputs</envar> variable tells
|
||||
<filename>setup</filename> to use the indicated packages as
|
||||
<quote>inputs</quote>. This means that if a package provides a
|
||||
@@ -568,7 +568,7 @@ genericBuild <co xml:id='ex-hello-builder2-co-3' /></programlisting>
|
||||
the file <literal>$stdenv/setup</literal>.</para>
|
||||
|
||||
</callout>
|
||||
|
||||
|
||||
<callout arearefs='ex-hello-builder2-co-3'>
|
||||
|
||||
<para>The final step calls the shell function
|
||||
@@ -580,7 +580,7 @@ genericBuild <co xml:id='ex-hello-builder2-co-3' /></programlisting>
|
||||
see <xref linkend='sec-standard-environment' />.</para>
|
||||
|
||||
</callout>
|
||||
|
||||
|
||||
</calloutlist>
|
||||
|
||||
<para>Discerning readers will note that the
|
||||
@@ -617,7 +617,7 @@ Laziness means that arguments to functions are evaluated only when
|
||||
they are needed. Functional means that functions are
|
||||
<quote>normal</quote> values that can be passed around and manipulated
|
||||
in interesting ways. The language is not a full-featured, general
|
||||
purpose language. It's main job is to describe packages,
|
||||
purpose language. Its main job is to describe packages,
|
||||
compositions of packages, and the variability within
|
||||
packages.</para>
|
||||
|
||||
@@ -704,7 +704,7 @@ configureFlags = "
|
||||
the third line is indented three spaces. Thus, two spaces are
|
||||
stripped from each line, so the resulting string is
|
||||
|
||||
<programlisting>
|
||||
<programlisting>
|
||||
"This is the first line.\nThis is the second line.\n This is the third line.\n"</programlisting>
|
||||
|
||||
</para>
|
||||
@@ -714,19 +714,19 @@ configureFlags = "
|
||||
text on the initial line.</para>
|
||||
|
||||
<para>Antiquotation
|
||||
(<literal>${<replaceable>expr</replaceable>}}</literal>) is
|
||||
(<literal>${<replaceable>expr</replaceable>}</literal>) is
|
||||
supported in indented strings.</para>
|
||||
|
||||
<para>Since <literal>${</literal> and <literal>''</literal> have
|
||||
special meaning in indented strings, you need a way to quote them.
|
||||
<literal>${</literal> can be escaped by prefixing it with
|
||||
<literal>''</literal>, i.e., <literal>''${</literal>.
|
||||
<literal>''</literal> can be escaped by prefixing it with
|
||||
<literal>'</literal>, i.e., <literal>'''</literal>. Finally,
|
||||
linefeed, carriage-return and tab characters can be writted as
|
||||
<literal>''\n</literal>, <literal>''\r</literal>,
|
||||
<literal>''\t</literal>.</para>
|
||||
|
||||
<literal>''</literal> (that is, two single quotes), i.e.,
|
||||
<literal>''${</literal>. <literal>''</literal> can be escaped by
|
||||
prefixing it with <literal>'</literal>, i.e.,
|
||||
<literal>'''</literal>. Finally, linefeed, carriage-return and
|
||||
tab characters can be written as <literal>''\n</literal>,
|
||||
<literal>''\r</literal>, <literal>''\t</literal>.</para>
|
||||
|
||||
<para>Indented strings are primarily useful in that they allow
|
||||
multi-line string literals to follow the indentation of the
|
||||
enclosing Nix expression, and that less escaping is typically
|
||||
@@ -745,7 +745,7 @@ stdenv.mkDerivation {
|
||||
${if enableBar then "cp bar $out/bin" else ""}
|
||||
'';
|
||||
<replaceable>...</replaceable>
|
||||
}
|
||||
}
|
||||
</programlisting>
|
||||
|
||||
</para>
|
||||
@@ -775,13 +775,13 @@ stdenv.mkDerivation {
|
||||
absolute at parse time relative to the directory of the Nix
|
||||
expression that contained it. For instance, if a Nix expression in
|
||||
<filename>/foo/bar/bla.nix</filename> refers to
|
||||
<filename>../xyzzy/fnord.nix</filename>, the absolutised path is
|
||||
<filename>../xyzzy/fnord.nix</filename>, the absolute path is
|
||||
<filename>/foo/xyzzy/fnord.nix</filename>.</para></listitem>
|
||||
|
||||
<listitem><para><emphasis>Booleans</emphasis> with values
|
||||
<literal>true</literal> and
|
||||
<literal>false</literal>.</para></listitem>
|
||||
|
||||
|
||||
</itemizedlist>
|
||||
|
||||
</para>
|
||||
@@ -931,7 +931,7 @@ propagate attributes). This can be shortened using the
|
||||
<literal>inherit</literal> keyword. For instance,
|
||||
|
||||
<programlisting>
|
||||
let x = 123; in
|
||||
let x = 123; in
|
||||
{ inherit x;
|
||||
y = 456;
|
||||
}</programlisting>
|
||||
@@ -982,7 +982,7 @@ argument. There are three kinds of patterns:</para>
|
||||
|
||||
<itemizedlist>
|
||||
|
||||
|
||||
|
||||
<listitem><para>If a pattern is a single identifier, then the
|
||||
function matches any argument. Example:
|
||||
|
||||
@@ -1002,7 +1002,7 @@ map (concat "foo") [ "bar" "bla" "abc" ]</programlisting>
|
||||
evaluates to <literal>[ "foobar" "foobla"
|
||||
"fooabc" ]</literal>.</para></listitem>
|
||||
|
||||
|
||||
|
||||
<listitem><para>An <emphasis>attribute set pattern</emphasis> of the
|
||||
form <literal>{ name1, name2, …, nameN }</literal>
|
||||
matches an attribute set containing the listed attributes, and binds
|
||||
@@ -1016,7 +1016,7 @@ map (concat "foo") [ "bar" "bla" "abc" ]</programlisting>
|
||||
<varname>x</varname>, <varname>y</varname> and
|
||||
<varname>z</varname>. No other attributes are allowed. If you want
|
||||
to allow additional arguments, you can use an ellipsis
|
||||
(<literal>...</literal>):
|
||||
(<literal>...</literal>):
|
||||
|
||||
<programlisting>
|
||||
{ x, y, z, ... }: z + y + x</programlisting>
|
||||
@@ -1164,7 +1164,7 @@ used in the Nix expression for Subversion.</para>
|
||||
</calloutlist>
|
||||
|
||||
</simplesect>
|
||||
|
||||
|
||||
|
||||
|
||||
<simplesect><title>With-expressions</title>
|
||||
@@ -1343,7 +1343,7 @@ set, the attributes of which specify the inputs of the build.</para>
|
||||
<listitem><para>There must be an attribute named
|
||||
<varname>name</varname> whose value must be a string. This is used
|
||||
as a symbolic name for the package by <command>nix-env</command>,
|
||||
and it is appended to the hash in the output path of the
|
||||
and it is appended to the output paths of the
|
||||
derivation.</para></listitem>
|
||||
|
||||
<listitem><para>There must be an attribute named
|
||||
@@ -1358,7 +1358,7 @@ set, the attributes of which specify the inputs of the build.</para>
|
||||
|
||||
<itemizedlist>
|
||||
|
||||
<listitem><para>Strings, URIs, and integers are just passed
|
||||
<listitem><para>Strings and integers are just passed
|
||||
verbatim.</para></listitem>
|
||||
|
||||
<listitem><para>A <emphasis>path</emphasis> (e.g.,
|
||||
@@ -1369,8 +1369,8 @@ set, the attributes of which specify the inputs of the build.</para>
|
||||
should reside in the Nix store.</para></listitem>
|
||||
|
||||
<listitem><para>A <emphasis>derivation</emphasis> causes that
|
||||
derivation to be built prior to the present derivation; the
|
||||
output path is put in the environment
|
||||
derivation to be built prior to the present derivation; its
|
||||
default output path is put in the environment
|
||||
variable.</para></listitem>
|
||||
|
||||
<listitem><para>Lists of the previous types are also allowed.
|
||||
@@ -1389,14 +1389,48 @@ set, the attributes of which specify the inputs of the build.</para>
|
||||
specifies command-line arguments to be passed to the builder. It
|
||||
should be a list.</para></listitem>
|
||||
|
||||
<listitem><para>The optional attribute <varname>outputs</varname>
|
||||
specifies a list of symbolic outputs of the derivation. By default,
|
||||
a derivation produces a single output path, denoted as
|
||||
<literal>out</literal>. However, derivations can produce multiple
|
||||
output paths. This is useful because it allows outputs to be
|
||||
downloaded or garbage-collected separately. For instance, imagine a
|
||||
library package that provides a dynamic library, header files, and
|
||||
documentation. A program that links against the library doesn’t
|
||||
need the header files and documentation at runtime, and it doesn’t
|
||||
need the documentation at build time. Thus, the library package
|
||||
could specify:
|
||||
<programlisting>
|
||||
outputs = [ "lib" "headers" "doc" ];
|
||||
</programlisting>
|
||||
This will cause Nix to pass environment variables
|
||||
<literal>lib</literal>, <literal>headers</literal> and
|
||||
<literal>doc</literal> to the builder containing the intended store
|
||||
paths of each output. The builder would typically do something like
|
||||
<programlisting>
|
||||
./configure --libdir=$lib/lib --includedir=$headers/include --docdir=$doc/share/doc
|
||||
</programlisting>
|
||||
for an Autoconf-style package. You can refer to each output of a
|
||||
derivation by selecting it as an attribute, e.g.
|
||||
<programlisting>
|
||||
buildInputs = [ pkg.lib pkg.headers ];
|
||||
</programlisting>
|
||||
The first element of <varname>output</varname> determines the
|
||||
<emphasis>default output</emphasis>. Thus, you could also write
|
||||
<programlisting>
|
||||
buildInputs = [ pkg pkg.headers ];
|
||||
</programlisting>
|
||||
since <literal>pkg</literal> is equivalent to
|
||||
<literal>pkg.lib</literal>.</para></listitem>
|
||||
|
||||
</itemizedlist>
|
||||
|
||||
<para>(Note that <function>mkDerivation</function> in the standard
|
||||
<para>The function <function>mkDerivation</function> in the standard
|
||||
environment is a wrapper around <function>derivation</function> that
|
||||
adds a default value for <varname>system</varname> and always uses
|
||||
Bash as the builder, to which the supplied builder is passed as a
|
||||
command-line argument. See <xref linkend='sec-standard-environment'
|
||||
/>.)</para>
|
||||
/>.</para>
|
||||
|
||||
<para>The builder is executed as follows:
|
||||
|
||||
@@ -1440,17 +1474,19 @@ command-line argument. See <xref linkend='sec-standard-environment'
|
||||
top-level Nix store directory (typically,
|
||||
<filename>/nix/store</filename>).</para></listitem>
|
||||
|
||||
<listitem><para><envar>out</envar> is set to point to the output
|
||||
path of the derivation, which is a subdirectory of the Nix store.
|
||||
The output path is a concatenation of the cryptographic hash of
|
||||
all build inputs, and the <varname>name</varname>
|
||||
attribute.</para></listitem>
|
||||
|
||||
<listitem><para>For each output declared in
|
||||
<varname>outputs</varname>, the corresponding environment variable
|
||||
is set to point to the intended path in the Nix store for that
|
||||
output. Each output path is a concatenation of the cryptographic
|
||||
hash of all build inputs, the <varname>name</varname> attribute
|
||||
and the output name. (The output name is omitted if it’s
|
||||
<literal>out</literal>.)</para></listitem>
|
||||
|
||||
</itemizedlist>
|
||||
|
||||
</para></listitem>
|
||||
|
||||
<listitem><para>If the output path already exists, it is removed.
|
||||
<listitem><para>If an output path already exists, it is removed.
|
||||
Also, locks are acquired to prevent multiple Nix instances from
|
||||
performing the same build at the same time.</para></listitem>
|
||||
|
||||
@@ -1464,14 +1500,11 @@ command-line argument. See <xref linkend='sec-standard-environment'
|
||||
<listitem><para>The temporary directory is removed (unless the
|
||||
<option>-K</option> option was specified).</para></listitem>
|
||||
|
||||
<listitem><para>If the build was successful, Nix scans the output
|
||||
for references to the paths of the inputs. These so-called
|
||||
<emphasis>retained dependencies</emphasis> could be used when the
|
||||
output of the derivation is used (e.g., when it's executed or used
|
||||
as input to another derivation), so if we deploy the derivation, we
|
||||
should copy the retained dependencies as well. The scan is
|
||||
performed by looking for the hash parts of file names of the
|
||||
inputs.</para></listitem>
|
||||
<listitem><para>If the build was successful, Nix scans each output
|
||||
path for references to input paths by looking for the hash parts of
|
||||
the input paths. Since these are potential runtime dependencies,
|
||||
Nix registers them as dependencies of the output
|
||||
paths.</para></listitem>
|
||||
|
||||
<listitem><para>After the build, Nix sets the last-modified
|
||||
timestamp on all files in the build result to 1 (00:00:01 1/1/1970
|
||||
@@ -1497,7 +1530,7 @@ attributes.</para>
|
||||
<variablelist>
|
||||
|
||||
<varlistentry><term><varname>allowedReferences</varname></term>
|
||||
|
||||
|
||||
<listitem><para>The optional attribute
|
||||
<varname>allowedReferences</varname> specifies a list of legal
|
||||
references (dependencies) of the output of the builder. For
|
||||
@@ -1515,7 +1548,7 @@ allowedReferences = [];
|
||||
|
||||
</varlistentry>
|
||||
|
||||
|
||||
|
||||
<varlistentry><term><varname>exportReferencesGraph</varname></term>
|
||||
|
||||
<listitem><para>This attribute allows builders access to the
|
||||
@@ -1626,7 +1659,7 @@ stdenv.mkDerivation {
|
||||
outputHashMode = "flat";
|
||||
outputHashAlgo = "md5";
|
||||
outputHash = md5;
|
||||
|
||||
|
||||
inherit url;
|
||||
}
|
||||
</programlisting>
|
||||
@@ -1655,7 +1688,7 @@ stdenv.mkDerivation {
|
||||
<para>This is the default.</para></listitem>
|
||||
|
||||
</varlistentry>
|
||||
|
||||
|
||||
<varlistentry><term><literal>"recursive"</literal></term>
|
||||
|
||||
<listitem><para>The hash is computed over the NAR archive dump
|
||||
@@ -1676,10 +1709,10 @@ stdenv.mkDerivation {
|
||||
linkend="sec-nix-hash"><command>nix-hash</command> command</link>
|
||||
for information about converting to and from base-32
|
||||
notation.)</para></listitem>
|
||||
|
||||
|
||||
</varlistentry>
|
||||
|
||||
|
||||
|
||||
<varlistentry><term><varname>impureEnvVars</varname></term>
|
||||
|
||||
<listitem><para>This attribute allows you to specify a list of
|
||||
@@ -1706,7 +1739,7 @@ impureEnvVars = [ "http_proxy" "https_proxy" <replaceable>...</replaceable> ];
|
||||
|
||||
</varlistentry>
|
||||
|
||||
|
||||
|
||||
<varlistentry><term><varname>preferLocalBuild</varname></term>
|
||||
|
||||
<listitem><para>If this attribute is set to
|
||||
|
||||
@@ -78,7 +78,7 @@ The hook `nix-mode-hook' is run when Nix mode is started.
|
||||
("\\<isNull\\>" . font-lock-builtin-face)
|
||||
("[a-zA-Z][a-zA-Z0-9\\+-\\.]*:[a-zA-Z0-9%/\\?:@&=\\+\\$,_\\.!~\\*'-]+"
|
||||
. font-lock-constant-face)
|
||||
("\\<\\([a-zA-Z_][a-zA-Z0-9_'\.]*\\)[ \t]*="
|
||||
("\\<\\([a-zA-Z_][a-zA-Z0-9_'\-\.]*\\)[ \t]*="
|
||||
(1 font-lock-variable-name-face nil nil))
|
||||
("<[a-zA-Z0-9._\\+-]+\\(/[a-zA-Z0-9._\\+-]+\\)*>"
|
||||
. font-lock-constant-face)
|
||||
|
||||
@@ -4,7 +4,7 @@ After=syslog.target
|
||||
|
||||
[Service]
|
||||
Type=simple
|
||||
ExecStart=/usr/bin/nix-worker --daemon
|
||||
ExecStart=/usr/bin/nix-daemon
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
+8
-5
@@ -10,17 +10,20 @@ License: LGPLv2+
|
||||
Group: Applications/System
|
||||
%endif
|
||||
URL: http://nixos.org/
|
||||
Source0: %{name}-%{version}.tar.bz2
|
||||
Source0: %{name}-%{version}.tar.gz
|
||||
%if 0%{?el5}
|
||||
BuildRoot: %(mktemp -ud %{_tmppath}/%{name}-%{version}-%{release}-XXXXXX)
|
||||
%endif
|
||||
BuildRequires: perl(DBD::SQLite)
|
||||
BuildRequires: perl(DBI)
|
||||
BuildRequires: perl(WWW::Curl)
|
||||
BuildRequires: perl(ExtUtils::ParseXS)
|
||||
Requires: /usr/bin/perl
|
||||
Requires: curl
|
||||
Requires: perl-DBD-SQLite
|
||||
Requires: bzip2
|
||||
Requires: gzip
|
||||
Requires: xz
|
||||
BuildRequires: bzip2-devel
|
||||
BuildRequires: sqlite-devel
|
||||
|
||||
@@ -138,7 +141,7 @@ chmod -x $RPM_BUILD_ROOT%{_sysconfdir}/profile.d/nix.sh
|
||||
%if ! 0%{?rhel}
|
||||
# install systemd service descriptor
|
||||
mkdir -p $RPM_BUILD_ROOT%{_prefix}/lib/systemd/system
|
||||
cp -p misc/systemd/nix-worker.service \
|
||||
cp -p misc/systemd/nix-daemon.service \
|
||||
$RPM_BUILD_ROOT%{_prefix}/lib/systemd/system/
|
||||
%endif
|
||||
|
||||
@@ -172,8 +175,8 @@ chgrp %{nixbld_group} /nix/store
|
||||
chmod 1775 /nix/store
|
||||
%if ! 0%{?rhel}
|
||||
# Enable and start Nix worker
|
||||
systemctl enable nix-worker.service
|
||||
systemctl start nix-worker.service
|
||||
systemctl enable nix-daemon.service
|
||||
systemctl start nix-daemon.service
|
||||
%endif
|
||||
|
||||
%files
|
||||
@@ -185,7 +188,7 @@ systemctl start nix-worker.service
|
||||
%exclude %dir %{perl_vendorarch}/auto/
|
||||
%{_prefix}/libexec/*
|
||||
%if ! 0%{?rhel}
|
||||
%{_prefix}/lib/systemd/system/nix-worker.service
|
||||
%{_prefix}/lib/systemd/system/nix-daemon.service
|
||||
%endif
|
||||
%{_datadir}/emacs/site-lisp/nix-mode.el
|
||||
%{_datadir}/nix
|
||||
|
||||
+2
-1
@@ -1,4 +1,4 @@
|
||||
PERL_MODULES = lib/Nix/Store.pm lib/Nix/Manifest.pm lib/Nix/GeneratePatches.pm lib/Nix/SSH.pm lib/Nix/CopyClosure.pm lib/Nix/Config.pm.in
|
||||
PERL_MODULES = lib/Nix/Store.pm lib/Nix/Manifest.pm lib/Nix/GeneratePatches.pm lib/Nix/SSH.pm lib/Nix/CopyClosure.pm lib/Nix/Config.pm.in lib/Nix/Utils.pm
|
||||
|
||||
all: $(PERL_MODULES:.in=)
|
||||
|
||||
@@ -26,6 +26,7 @@ AM_CXXFLAGS = \
|
||||
-D_FILE_OFFSET_BITS=64
|
||||
|
||||
lib/Nix/Store.cc: lib/Nix/Store.xs
|
||||
$(INSTALL) -d lib/Nix
|
||||
xsubpp $^ -output $@
|
||||
|
||||
else
|
||||
|
||||
@@ -1,27 +1,39 @@
|
||||
package Nix::Config;
|
||||
|
||||
$version = "@version@";
|
||||
|
||||
$binDir = $ENV{"NIX_BIN_DIR"} || "@bindir@";
|
||||
$libexecDir = $ENV{"NIX_LIBEXEC_DIR"} || "@libexecdir@";
|
||||
$stateDir = $ENV{"NIX_STATE_DIR"} || "@localstatedir@/nix";
|
||||
$manifestDir = $ENV{"NIX_MANIFESTS_DIR"} || "@localstatedir@/nix/manifests";
|
||||
$logDir = $ENV{"NIX_LOG_DIR"} || "@localstatedir@/log/nix";
|
||||
$confDir = $ENV{"NIX_CONF_DIR"} || "@sysconfdir@/nix";
|
||||
$storeDir = $ENV{"NIX_STORE_DIR"} || "@storedir@";
|
||||
|
||||
$bzip2 = $ENV{"NIX_BZIP2"} || "@bzip2@";
|
||||
$bzip2 = "@bzip2@";
|
||||
$xz = "@xz@";
|
||||
$curl = "@curl@";
|
||||
|
||||
$useBindings = "@perlbindings@" eq "yes";
|
||||
|
||||
%config = ();
|
||||
|
||||
sub readConfig {
|
||||
my %config;
|
||||
my $config = "@sysconfdir@/nix/nix.conf";
|
||||
if (defined $ENV{'_NIX_OPTIONS'}) {
|
||||
foreach my $s (split '\n', $ENV{'_NIX_OPTIONS'}) {
|
||||
my ($n, $v) = split '=', $s, 2;
|
||||
$config{$n} = $v;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
my $config = "$confDir/nix.conf";
|
||||
return unless -f $config;
|
||||
|
||||
|
||||
open CONFIG, "<$config" or die "cannot open `$config'";
|
||||
while (<CONFIG>) {
|
||||
/^\s*([\w|-]+)\s*=\s*(.*)$/ or next;
|
||||
$config{$1} = $2;
|
||||
print "|$1| -> |$2|\n";
|
||||
}
|
||||
close CONFIG;
|
||||
}
|
||||
|
||||
@@ -3,39 +3,50 @@ package Nix::CopyClosure;
|
||||
use strict;
|
||||
use Nix::Config;
|
||||
use Nix::Store;
|
||||
use List::Util qw(sum);
|
||||
|
||||
|
||||
sub copyTo {
|
||||
my ($sshHost, $sshOpts, $storePaths, $compressor, $decompressor, $includeOutputs, $dryRun, $sign, $progressViewer) = @_;
|
||||
|
||||
$compressor = "$compressor |" if $compressor ne "";
|
||||
$decompressor = "$decompressor |" if $decompressor ne "";
|
||||
$progressViewer = "$progressViewer |" if $progressViewer ne "";
|
||||
my ($sshHost, $sshOpts, $storePaths, $compressor, $decompressor,
|
||||
$includeOutputs, $dryRun, $sign, $progressViewer, $useSubstitutes) = @_;
|
||||
|
||||
# Get the closure of this path.
|
||||
my @closure = reverse(topoSortPaths(computeFSClosure(0, $includeOutputs,
|
||||
map { followLinksToStorePath $_ } @{$storePaths})));
|
||||
|
||||
# Optionally use substitutes on the remote host.
|
||||
if (!$dryRun && $useSubstitutes) {
|
||||
system "ssh $sshHost @{$sshOpts} nix-store -r --ignore-unknown @closure";
|
||||
# Ignore exit status because this is just an optimisation.
|
||||
}
|
||||
|
||||
# Ask the remote host which paths are invalid. Because of limits
|
||||
# to the command line length, do this in chunks. Eventually,
|
||||
# we'll want to use ‘--from-stdin’, but we can't rely on the
|
||||
# target having this option yet.
|
||||
my @missing = ();
|
||||
my $missingSize = 0;
|
||||
while (scalar(@closure) > 0) {
|
||||
my @ps = splice(@closure, 0, 1500);
|
||||
open(READ, "set -f; ssh $sshHost @{$sshOpts} nix-store --check-validity --print-invalid @ps|");
|
||||
while (<READ>) {
|
||||
chomp;
|
||||
push @missing, $_;
|
||||
my ($deriver, $narHash, $time, $narSize, $refs) = queryPathInfo($_, 1);
|
||||
$missingSize += $narSize;
|
||||
}
|
||||
close READ or die;
|
||||
}
|
||||
|
||||
$compressor = "$compressor |" if $compressor ne "";
|
||||
$decompressor = "$decompressor |" if $decompressor ne "";
|
||||
$progressViewer = "$progressViewer -s $missingSize |" if $progressViewer ne "";
|
||||
|
||||
# Export the store paths and import them on the remote machine.
|
||||
if (scalar @missing > 0) {
|
||||
print STDERR "copying ", scalar @missing, " missing paths to ‘$sshHost’...\n";
|
||||
unless ($dryRun) {
|
||||
open SSH, "| $compressor $progressViewer ssh $sshHost @{$sshOpts} '$decompressor nix-store --import' > /dev/null" or die;
|
||||
open SSH, "| $progressViewer $compressor ssh $sshHost @{$sshOpts} '$decompressor nix-store --import' > /dev/null" or die;
|
||||
exportPaths(fileno(SSH), $sign, @missing);
|
||||
close SSH or die "copying store paths to remote machine `$sshHost' failed: $?";
|
||||
}
|
||||
|
||||
+93
-28
@@ -9,12 +9,12 @@ use Fcntl ':flock';
|
||||
use Nix::Config;
|
||||
|
||||
our @ISA = qw(Exporter);
|
||||
our @EXPORT = qw(readManifest writeManifest updateManifestDB addPatch);
|
||||
our @EXPORT = qw(readManifest writeManifest updateManifestDB addPatch deleteOldManifests parseNARInfo);
|
||||
|
||||
|
||||
sub addNAR {
|
||||
my ($narFiles, $storePath, $info) = @_;
|
||||
|
||||
|
||||
$$narFiles{$storePath} = []
|
||||
unless defined $$narFiles{$storePath};
|
||||
|
||||
@@ -24,7 +24,7 @@ sub addNAR {
|
||||
foreach my $narFile (@{$narFileList}) {
|
||||
$found = 1 if $narFile->{url} eq $info->{url};
|
||||
}
|
||||
|
||||
|
||||
push @{$narFileList}, $info if !$found;
|
||||
}
|
||||
|
||||
@@ -43,7 +43,7 @@ sub addPatch {
|
||||
$patch2->{url} eq $patch->{url} &&
|
||||
$patch2->{basePath} eq $patch->{basePath};
|
||||
}
|
||||
|
||||
|
||||
push @{$patchList}, $patch if !$found;
|
||||
|
||||
return !$found;
|
||||
@@ -68,7 +68,7 @@ sub readManifest_ {
|
||||
my $manifestVersion = 2;
|
||||
|
||||
my ($storePath, $url, $hash, $size, $basePath, $baseHash, $patchType);
|
||||
my ($narHash, $narSize, $references, $deriver, $copyFrom, $system);
|
||||
my ($narHash, $narSize, $references, $deriver, $copyFrom, $system, $compressionType);
|
||||
|
||||
while (<MANIFEST>) {
|
||||
chomp;
|
||||
@@ -93,10 +93,11 @@ sub readManifest_ {
|
||||
undef $system;
|
||||
$references = "";
|
||||
$deriver = "";
|
||||
}
|
||||
$compressionType = "bzip2";
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
|
||||
if (/^\}$/) {
|
||||
$inside = 0;
|
||||
|
||||
@@ -107,6 +108,7 @@ sub readManifest_ {
|
||||
, references => $references
|
||||
, deriver => $deriver
|
||||
, system => $system
|
||||
, compressionType => $compressionType
|
||||
});
|
||||
}
|
||||
|
||||
@@ -120,11 +122,12 @@ sub readManifest_ {
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
elsif (/^\s*StorePath:\s*(\/\S+)\s*$/) { $storePath = $1; }
|
||||
elsif (/^\s*CopyFrom:\s*(\/\S+)\s*$/) { $copyFrom = $1; }
|
||||
elsif (/^\s*Hash:\s*(\S+)\s*$/) { $hash = $1; }
|
||||
elsif (/^\s*URL:\s*(\S+)\s*$/) { $url = $1; }
|
||||
elsif (/^\s*Compression:\s*(\S+)\s*$/) { $compressionType = $1; }
|
||||
elsif (/^\s*Size:\s*(\d+)\s*$/) { $size = $1; }
|
||||
elsif (/^\s*BasePath:\s*(\/\S+)\s*$/) { $basePath = $1; }
|
||||
elsif (/^\s*BaseHash:\s*(\S+)\s*$/) { $baseHash = $1; }
|
||||
@@ -172,6 +175,7 @@ sub writeManifest {
|
||||
print MANIFEST "{\n";
|
||||
print MANIFEST " StorePath: $storePath\n";
|
||||
print MANIFEST " NarURL: $narFile->{url}\n";
|
||||
print MANIFEST " Compression: $narFile->{compressionType}\n";
|
||||
print MANIFEST " Hash: $narFile->{hash}\n" if defined $narFile->{hash};
|
||||
print MANIFEST " Size: $narFile->{size}\n" if defined $narFile->{size};
|
||||
print MANIFEST " NarHash: $narFile->{narHash}\n";
|
||||
@@ -184,7 +188,7 @@ sub writeManifest {
|
||||
print MANIFEST "}\n";
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
foreach my $storePath (sort (keys %{$patches})) {
|
||||
my $patchList = $$patches{$storePath};
|
||||
foreach my $patch (@{$patchList}) {
|
||||
@@ -201,8 +205,8 @@ sub writeManifest {
|
||||
print MANIFEST "}\n";
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
close MANIFEST;
|
||||
|
||||
rename("$manifest.tmp", $manifest)
|
||||
@@ -211,11 +215,11 @@ sub writeManifest {
|
||||
|
||||
# Create a bzipped manifest.
|
||||
unless (defined $noCompress) {
|
||||
system("$Nix::Config::bzip2 < $manifest > $manifest.bz2.tmp") == 0
|
||||
or die "cannot compress manifest";
|
||||
system("$Nix::Config::bzip2 < $manifest > $manifest.bz2.tmp") == 0
|
||||
or die "cannot compress manifest";
|
||||
|
||||
rename("$manifest.bz2.tmp", "$manifest.bz2")
|
||||
or die "cannot rename $manifest.bz2.tmp: $!";
|
||||
rename("$manifest.bz2.tmp", "$manifest.bz2")
|
||||
or die "cannot rename $manifest.bz2.tmp: $!";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -223,9 +227,13 @@ sub writeManifest {
|
||||
sub updateManifestDB {
|
||||
my $manifestDir = $Nix::Config::manifestDir;
|
||||
|
||||
my @manifests = glob "$manifestDir/*.nixmanifest";
|
||||
return undef if scalar @manifests == 0;
|
||||
|
||||
mkpath($manifestDir);
|
||||
|
||||
my $dbPath = "$manifestDir/cache.sqlite";
|
||||
|
||||
unlink "$manifestDir/cache.sqlite"; # remove obsolete cache
|
||||
my $dbPath = "$manifestDir/cache-v2.sqlite";
|
||||
|
||||
# Open/create the database.
|
||||
our $dbh = DBI->connect("dbi:SQLite:dbname=$dbPath", "", "")
|
||||
@@ -245,13 +253,14 @@ sub updateManifestDB {
|
||||
timestamp integer not null
|
||||
);
|
||||
EOF
|
||||
|
||||
|
||||
$dbh->do(<<EOF);
|
||||
create table if not exists NARs (
|
||||
id integer primary key autoincrement not null,
|
||||
manifest integer not null,
|
||||
storePath text not null,
|
||||
url text not null,
|
||||
compressionType text not null,
|
||||
hash text,
|
||||
size integer,
|
||||
narHash text,
|
||||
@@ -292,8 +301,8 @@ EOF
|
||||
flock(MAINLOCK, LOCK_EX) or die;
|
||||
|
||||
our $insertNAR = $dbh->prepare(
|
||||
"insert into NARs(manifest, storePath, url, hash, size, narHash, " .
|
||||
"narSize, refs, deriver, system) values (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)") or die;
|
||||
"insert into NARs(manifest, storePath, url, compressionType, hash, size, narHash, " .
|
||||
"narSize, refs, deriver, system) values (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)") or die;
|
||||
|
||||
our $insertPatch = $dbh->prepare(
|
||||
"insert into Patches(manifest, storePath, basePath, baseHash, url, hash, " .
|
||||
@@ -304,8 +313,8 @@ EOF
|
||||
# Read each manifest in $manifestDir and add it to the database,
|
||||
# unless we've already done so on a previous run.
|
||||
my %seen;
|
||||
|
||||
for my $manifestLink (glob "$manifestDir/*.nixmanifest") {
|
||||
|
||||
for my $manifestLink (@manifests) {
|
||||
my $manifest = Cwd::abs_path($manifestLink);
|
||||
next unless -f $manifest;
|
||||
my $timestamp = lstat($manifest)->mtime;
|
||||
@@ -316,9 +325,9 @@ EOF
|
||||
{}, $manifest, $timestamp)} == 1;
|
||||
|
||||
print STDERR "caching $manifest...\n";
|
||||
|
||||
|
||||
$dbh->do("delete from Manifests where path = ?", {}, $manifest);
|
||||
|
||||
|
||||
$dbh->do("insert into Manifests(path, timestamp) values (?, ?)",
|
||||
{}, $manifest, $timestamp);
|
||||
|
||||
@@ -327,11 +336,11 @@ EOF
|
||||
sub addNARToDB {
|
||||
my ($storePath, $narFile) = @_;
|
||||
$insertNAR->execute(
|
||||
$id, $storePath, $narFile->{url}, $narFile->{hash}, $narFile->{size},
|
||||
$narFile->{narHash}, $narFile->{narSize}, $narFile->{references},
|
||||
$id, $storePath, $narFile->{url}, $narFile->{compressionType}, $narFile->{hash},
|
||||
$narFile->{size}, $narFile->{narHash}, $narFile->{narSize}, $narFile->{references},
|
||||
$narFile->{deriver}, $narFile->{system});
|
||||
};
|
||||
|
||||
|
||||
sub addPatchToDB {
|
||||
my ($storePath, $patch) = @_;
|
||||
$insertPatch->execute(
|
||||
@@ -341,7 +350,7 @@ EOF
|
||||
};
|
||||
|
||||
my $version = readManifest_($manifest, \&addNARToDB, \&addPatchToDB);
|
||||
|
||||
|
||||
if ($version < 3) {
|
||||
die "you have an old-style or corrupt manifest `$manifestLink'; please delete it\n";
|
||||
}
|
||||
@@ -364,4 +373,60 @@ EOF
|
||||
}
|
||||
|
||||
|
||||
|
||||
# Delete all old manifests downloaded from a given URL.
|
||||
sub deleteOldManifests {
|
||||
my ($url, $curUrlFile) = @_;
|
||||
for my $urlFile (glob "$Nix::Config::manifestDir/*.url") {
|
||||
next if defined $curUrlFile && $urlFile eq $curUrlFile;
|
||||
open URL, "<$urlFile" or die;
|
||||
my $url2 = <URL>;
|
||||
chomp $url2;
|
||||
close URL;
|
||||
next unless $url eq $url2;
|
||||
my $base = $urlFile; $base =~ s/.url$//;
|
||||
unlink "${base}.url";
|
||||
unlink "${base}.nixmanifest";
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
# Parse a NAR info file.
|
||||
sub parseNARInfo {
|
||||
my ($storePath, $content) = @_;
|
||||
|
||||
my ($storePath2, $url, $fileHash, $fileSize, $narHash, $narSize, $deriver, $system);
|
||||
my $compression = "bzip2";
|
||||
my @refs;
|
||||
|
||||
foreach my $line (split "\n", $content) {
|
||||
return undef unless $line =~ /^(.*): (.*)$/;
|
||||
if ($1 eq "StorePath") { $storePath2 = $2; }
|
||||
elsif ($1 eq "URL") { $url = $2; }
|
||||
elsif ($1 eq "Compression") { $compression = $2; }
|
||||
elsif ($1 eq "FileHash") { $fileHash = $2; }
|
||||
elsif ($1 eq "FileSize") { $fileSize = int($2); }
|
||||
elsif ($1 eq "NarHash") { $narHash = $2; }
|
||||
elsif ($1 eq "NarSize") { $narSize = int($2); }
|
||||
elsif ($1 eq "References") { @refs = split / /, $2; }
|
||||
elsif ($1 eq "Deriver") { $deriver = $2; }
|
||||
elsif ($1 eq "System") { $system = $2; }
|
||||
}
|
||||
|
||||
return undef if $storePath ne $storePath2 || !defined $url || !defined $narHash;
|
||||
|
||||
return
|
||||
{ url => $url
|
||||
, compression => $compression
|
||||
, fileHash => $fileHash
|
||||
, fileSize => $fileSize
|
||||
, narHash => $narHash
|
||||
, narSize => $narSize
|
||||
, refs => [ @refs ]
|
||||
, deriver => $deriver
|
||||
, system => $system
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
return 1;
|
||||
|
||||
@@ -44,6 +44,7 @@ sub closeSSHConnection {
|
||||
if ($sshStarted) {
|
||||
system("ssh $sshHost @sshOpts -O exit 2> /dev/null") == 0
|
||||
or warn "unable to stop SSH master: $?";
|
||||
$sshStarted = 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+17
-17
@@ -42,50 +42,50 @@ if ($Nix::Config::useBindings) {
|
||||
use File::Temp;
|
||||
use Fcntl qw/F_SETFD/;
|
||||
|
||||
sub hashFile {
|
||||
*hashFile = sub {
|
||||
my ($algo, $base32, $path) = @_;
|
||||
my $res = backtick("$Nix::Config::binDir/nix-hash", "--flat", $path, "--type", $algo, $base32 ? "--base32" : ());
|
||||
chomp $res;
|
||||
return $res;
|
||||
}
|
||||
|
||||
sub hashPath {
|
||||
};
|
||||
|
||||
*hashPath = sub {
|
||||
my ($algo, $base32, $path) = @_;
|
||||
my $res = backtick("$Nix::Config::binDir/nix-hash", $path, "--type", $algo, $base32 ? "--base32" : ());
|
||||
chomp $res;
|
||||
return $res;
|
||||
}
|
||||
|
||||
sub hashString {
|
||||
};
|
||||
|
||||
*hashString = sub {
|
||||
my ($algo, $base32, $s) = @_;
|
||||
my $fh = File::Temp->new();
|
||||
print $fh $s;
|
||||
my $res = backtick("$Nix::Config::binDir/nix-hash", $fh->filename, "--type", $algo, $base32 ? "--base32" : ());
|
||||
chomp $res;
|
||||
return $res;
|
||||
}
|
||||
|
||||
sub addToStore {
|
||||
};
|
||||
|
||||
*addToStore = sub {
|
||||
my ($srcPath, $recursive, $algo) = @_;
|
||||
die "not implemented" if $recursive || $algo ne "sha256";
|
||||
my $res = backtick("$Nix::Config::binDir/nix-store", "--add", $srcPath);
|
||||
chomp $res;
|
||||
return $res;
|
||||
}
|
||||
|
||||
sub isValidPath {
|
||||
};
|
||||
|
||||
*isValidPath = sub {
|
||||
my ($path) = @_;
|
||||
my $res = backtick("$Nix::Config::binDir/nix-store", "--check-validity", "--print-invalid", $path);
|
||||
chomp $res;
|
||||
return $res ne $path;
|
||||
}
|
||||
|
||||
sub queryPathHash {
|
||||
};
|
||||
|
||||
*queryPathHash = sub {
|
||||
my ($path) = @_;
|
||||
my $res = backtick("$Nix::Config::binDir/nix-store", "--query", "--hash", $path);
|
||||
chomp $res;
|
||||
return $res;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
1;
|
||||
|
||||
+14
-10
@@ -15,11 +15,12 @@
|
||||
using namespace nix;
|
||||
|
||||
|
||||
void doInit()
|
||||
void doInit()
|
||||
{
|
||||
if (!store) {
|
||||
try {
|
||||
setDefaultsFromEnvironment();
|
||||
settings.processEnvironment();
|
||||
settings.lockCPU = false;
|
||||
store = openStore();
|
||||
} catch (Error & e) {
|
||||
croak(e.what());
|
||||
@@ -237,32 +238,35 @@ SV * derivationFromPath(char * drvPath)
|
||||
doInit();
|
||||
Derivation drv = derivationFromPath(*store, drvPath);
|
||||
hash = newHV();
|
||||
|
||||
/* TODO: handle drv.outputs */
|
||||
|
||||
|
||||
HV * outputs = newHV();
|
||||
for (DerivationOutputs::iterator i = drv.outputs.begin(); i != drv.outputs.end(); ++i)
|
||||
hv_store(outputs, i->first.c_str(), i->first.size(), newSVpv(i->second.path.c_str(), 0), 0);
|
||||
hv_stores(hash, "outputs", newRV((SV *) outputs));
|
||||
|
||||
AV * inputDrvs = newAV();
|
||||
for (DerivationInputs::iterator i = drv.inputDrvs.begin(); i != drv.inputDrvs.end(); ++i)
|
||||
av_push(inputDrvs, newSVpv(i->first.c_str(), 0)); // !!! ignores i->second
|
||||
hv_stores(hash, "inputDrvs", newRV((SV *) inputDrvs));
|
||||
|
||||
|
||||
AV * inputSrcs = newAV();
|
||||
for (PathSet::iterator i = drv.inputSrcs.begin(); i != drv.inputSrcs.end(); ++i)
|
||||
av_push(inputSrcs, newSVpv(i->c_str(), 0));
|
||||
hv_stores(hash, "inputSrcs", newRV((SV *) inputSrcs));
|
||||
|
||||
|
||||
hv_stores(hash, "platform", newSVpv(drv.platform.c_str(), 0));
|
||||
hv_stores(hash, "builder", newSVpv(drv.builder.c_str(), 0));
|
||||
|
||||
|
||||
AV * args = newAV();
|
||||
for (Strings::iterator i = drv.args.begin(); i != drv.args.end(); ++i)
|
||||
av_push(args, newSVpv(i->c_str(), 0));
|
||||
hv_stores(hash, "args", newRV((SV *) args));
|
||||
|
||||
|
||||
HV * env = newHV();
|
||||
for (StringPairs::iterator i = drv.env.begin(); i != drv.env.end(); ++i)
|
||||
hv_store(env, i->first.c_str(), i->first.size(), newSVpv(i->second.c_str(), 0), 0);
|
||||
hv_stores(hash, "env", newRV((SV *) env));
|
||||
|
||||
|
||||
RETVAL = newRV_noinc((SV *)hash);
|
||||
} catch (Error & e) {
|
||||
croak(e.what());
|
||||
|
||||
@@ -0,0 +1,38 @@
|
||||
package Nix::Utils;
|
||||
|
||||
our @ISA = qw(Exporter);
|
||||
our @EXPORT = qw(checkURL uniq writeFile readFile);
|
||||
|
||||
$urlRE = "(?: [a-zA-Z][a-zA-Z0-9\+\-\.]*\:[a-zA-Z0-9\%\/\?\:\@\&\=\+\$\,\-\_\.\!\~\*]+ )";
|
||||
|
||||
sub checkURL {
|
||||
my ($url) = @_;
|
||||
die "invalid URL ‘$url’\n" unless $url =~ /^ $urlRE $ /x;
|
||||
}
|
||||
|
||||
sub uniq {
|
||||
my %seen;
|
||||
my @res;
|
||||
foreach my $name (@_) {
|
||||
next if $seen{$name};
|
||||
$seen{$name} = 1;
|
||||
push @res, $name;
|
||||
}
|
||||
return @res;
|
||||
}
|
||||
|
||||
sub writeFile {
|
||||
my ($fn, $s) = @_;
|
||||
open TMP, ">$fn" or die "cannot create file `$fn': $!";
|
||||
print TMP "$s" or die;
|
||||
close TMP or die;
|
||||
}
|
||||
|
||||
sub readFile {
|
||||
local $/ = undef;
|
||||
my ($fn) = @_;
|
||||
open TMP, "<$fn" or die "cannot open file `$fn': $!";
|
||||
my $s = <TMP>;
|
||||
close TMP or die;
|
||||
return $s;
|
||||
}
|
||||
+58
-32
@@ -1,15 +1,19 @@
|
||||
{ nixpkgs ? <nixpkgs>
|
||||
, nix ? { outPath = ./.; revCount = 1234; shortRev = "abcdef"; }
|
||||
{ nix ? { outPath = ./.; revCount = 1234; shortRev = "abcdef"; }
|
||||
, officialRelease ? false
|
||||
}:
|
||||
|
||||
let
|
||||
|
||||
pkgs = import <nixpkgs> {};
|
||||
|
||||
systems = [ "x86_64-linux" "i686-linux" "x86_64-darwin" "x86_64-freebsd" "i686-freebsd" "i686-cygwin" "i686-solaris" ];
|
||||
|
||||
|
||||
jobs = rec {
|
||||
|
||||
|
||||
tarball =
|
||||
with import nixpkgs {};
|
||||
with pkgs;
|
||||
|
||||
releaseTools.sourceTarball {
|
||||
name = "nix-tarball";
|
||||
@@ -19,7 +23,7 @@ let
|
||||
inherit officialRelease;
|
||||
|
||||
buildInputs =
|
||||
[ curl bison25 flex2535 perl libxml2 libxslt w3m bzip2
|
||||
[ curl bison flex2535 perl libxml2 libxslt w3m bzip2
|
||||
tetex dblatex nukeReferences pkgconfig sqlite git
|
||||
];
|
||||
|
||||
@@ -29,6 +33,7 @@ let
|
||||
--with-xml-flags=--nonet
|
||||
--with-dbi=${perlPackages.DBI}/${perl.libPrefix}
|
||||
--with-dbd-sqlite=${perlPackages.DBDSQLite}/${perl.libPrefix}
|
||||
--with-www-curl=${perlPackages.WWWCurl}/${perl.libPrefix}
|
||||
'';
|
||||
|
||||
postUnpack = ''
|
||||
@@ -41,9 +46,18 @@ let
|
||||
export VARTEXFONTS=$TMPDIR/texfonts
|
||||
'';
|
||||
|
||||
distPhase =
|
||||
''
|
||||
runHook preDist
|
||||
make dist-gzip
|
||||
make dist-xz
|
||||
mkdir -p $out/tarballs
|
||||
cp *.tar.* $out/tarballs
|
||||
'';
|
||||
|
||||
preDist = ''
|
||||
make -C doc/manual install prefix=$out
|
||||
|
||||
|
||||
make -C doc/manual manual.pdf prefix=$out
|
||||
cp doc/manual/manual.pdf $out/manual.pdf
|
||||
|
||||
@@ -54,7 +68,7 @@ let
|
||||
# to Windows and Macs, so there should be no Linux binaries
|
||||
# in the closure).
|
||||
nuke-refs $out/manual.pdf
|
||||
|
||||
|
||||
echo "doc manual $out/share/doc/nix/manual" >> $out/nix-support/hydra-build-products
|
||||
echo "doc-pdf manual $out/manual.pdf" >> $out/nix-support/hydra-build-products
|
||||
echo "doc release-notes $out/share/doc/nix/release-notes" >> $out/nix-support/hydra-build-products
|
||||
@@ -62,10 +76,9 @@ let
|
||||
};
|
||||
|
||||
|
||||
build =
|
||||
{ system ? "x86_64-linux" }:
|
||||
build = pkgs.lib.genAttrs systems (system:
|
||||
|
||||
with import nixpkgs { inherit system; };
|
||||
with import <nixpkgs> { inherit system; };
|
||||
|
||||
releaseTools.nixBuild {
|
||||
name = "nix";
|
||||
@@ -77,21 +90,27 @@ let
|
||||
--disable-init-state
|
||||
--with-dbi=${perlPackages.DBI}/${perl.libPrefix}
|
||||
--with-dbd-sqlite=${perlPackages.DBDSQLite}/${perl.libPrefix}
|
||||
--with-www-curl=${perlPackages.WWWCurl}/${perl.libPrefix}
|
||||
--enable-gc
|
||||
--sysconfdir=/etc
|
||||
'';
|
||||
|
||||
enableParallelBuilding = true;
|
||||
|
||||
makeFlags = "profiledir=$(out)/etc/profile.d";
|
||||
|
||||
installFlags = "sysconfdir=$(out)/etc";
|
||||
|
||||
doInstallCheck = true;
|
||||
};
|
||||
});
|
||||
|
||||
binaryTarball =
|
||||
{ system ? "x86_64-linux" }:
|
||||
|
||||
with import nixpkgs { inherit system; };
|
||||
binaryTarball = pkgs.lib.genAttrs systems (system:
|
||||
|
||||
with import <nixpkgs> { inherit system; };
|
||||
|
||||
let
|
||||
toplevel = build { inherit system; };
|
||||
toplevel = builtins.getAttr system jobs.build;
|
||||
version = toplevel.src.version;
|
||||
in
|
||||
|
||||
@@ -114,11 +133,11 @@ let
|
||||
--transform "s,$TMPDIR/install,/usr/bin/nix-finish-install," \
|
||||
--transform "s,$TMPDIR/reginfo,/nix/store/reginfo," \
|
||||
$TMPDIR/install $TMPDIR/reginfo $storePaths
|
||||
'';
|
||||
'');
|
||||
|
||||
|
||||
coverage =
|
||||
with import nixpkgs { system = "x86_64-linux"; };
|
||||
with import <nixpkgs> { system = "x86_64-linux"; };
|
||||
|
||||
releaseTools.coverageAnalysis {
|
||||
name = "nix-build";
|
||||
@@ -134,12 +153,13 @@ let
|
||||
--disable-init-state
|
||||
--with-dbi=${perlPackages.DBI}/${perl.libPrefix}
|
||||
--with-dbd-sqlite=${perlPackages.DBDSQLite}/${perl.libPrefix}
|
||||
--with-www-curl=${perlPackages.WWWCurl}/${perl.libPrefix}
|
||||
'';
|
||||
|
||||
dontInstall = false;
|
||||
|
||||
doInstallCheck = true;
|
||||
|
||||
|
||||
lcovFilter = [ "*/boost/*" "*-tab.*" ];
|
||||
|
||||
# We call `dot', and even though we just use it to
|
||||
@@ -148,16 +168,18 @@ let
|
||||
FONTCONFIG_FILE = texFunctions.fontsConf;
|
||||
};
|
||||
|
||||
|
||||
rpm_fedora13i386 = makeRPM_i686 (diskImageFuns: diskImageFuns.fedora13i386) 50;
|
||||
rpm_fedora13x86_64 = makeRPM_x86_64 (diskImageFunsFun: diskImageFunsFun.fedora13x86_64) 50;
|
||||
|
||||
rpm_fedora16i386 = makeRPM_i686 (diskImageFuns: diskImageFuns.fedora16i386) 50;
|
||||
rpm_fedora16x86_64 = makeRPM_x86_64 (diskImageFunsFun: diskImageFunsFun.fedora16x86_64) 50;
|
||||
rpm_fedora18i386 = makeRPM_i686 (diskImageFuns: diskImageFuns.fedora18i386) 60;
|
||||
rpm_fedora18x86_64 = makeRPM_x86_64 (diskImageFunsFun: diskImageFunsFun.fedora18x86_64) 60;
|
||||
|
||||
|
||||
|
||||
deb_debian60i386 = makeDeb_i686 (diskImageFuns: diskImageFuns.debian60i386) 50;
|
||||
deb_debian60x86_64 = makeDeb_x86_64 (diskImageFunsFun: diskImageFunsFun.debian60x86_64) 50;
|
||||
|
||||
deb_debian70i386 = makeDeb_i686 (diskImageFuns: diskImageFuns.debian70i386) 60;
|
||||
deb_debian70x86_64 = makeDeb_x86_64 (diskImageFunsFun: diskImageFunsFun.debian70x86_64) 60;
|
||||
|
||||
deb_ubuntu1004i386 = makeDeb_i686 (diskImageFuns: diskImageFuns.ubuntu1004i386) 50;
|
||||
deb_ubuntu1004x86_64 = makeDeb_x86_64 (diskImageFuns: diskImageFuns.ubuntu1004x86_64) 50;
|
||||
deb_ubuntu1010i386 = makeDeb_i686 (diskImageFuns: diskImageFuns.ubuntu1010i386) 50;
|
||||
@@ -166,15 +188,19 @@ let
|
||||
deb_ubuntu1110x86_64 = makeDeb_x86_64 (diskImageFuns: diskImageFuns.ubuntu1110x86_64) 60;
|
||||
deb_ubuntu1204i386 = makeDeb_i686 (diskImageFuns: diskImageFuns.ubuntu1204i386) 60;
|
||||
deb_ubuntu1204x86_64 = makeDeb_x86_64 (diskImageFuns: diskImageFuns.ubuntu1204x86_64) 60;
|
||||
deb_ubuntu1210i386 = makeDeb_i686 (diskImageFuns: diskImageFuns.ubuntu1210i386) 70;
|
||||
deb_ubuntu1210x86_64 = makeDeb_x86_64 (diskImageFuns: diskImageFuns.ubuntu1210x86_64) 70;
|
||||
deb_ubuntu1304i386 = makeDeb_i686 (diskImageFuns: diskImageFuns.ubuntu1304i386) 80;
|
||||
deb_ubuntu1304x86_64 = makeDeb_x86_64 (diskImageFuns: diskImageFuns.ubuntu1304x86_64) 80;
|
||||
|
||||
|
||||
# System tests.
|
||||
tests.remote_builds = (import ./tests/remote-builds.nix rec {
|
||||
nix = build { inherit system; }; system = "x86_64-linux";
|
||||
nix = build.x86_64-linux; system = "x86_64-linux";
|
||||
}).test;
|
||||
|
||||
tests.nix_copy_closure = (import ./tests/nix-copy-closure.nix rec {
|
||||
nix = build { inherit system; }; system = "x86_64-linux";
|
||||
nix = build.x86_64-linux; system = "x86_64-linux";
|
||||
}).test;
|
||||
|
||||
};
|
||||
@@ -183,16 +209,16 @@ let
|
||||
makeRPM_i686 = makeRPM "i686-linux";
|
||||
makeRPM_x86_64 = makeRPM "x86_64-linux";
|
||||
|
||||
makeRPM =
|
||||
makeRPM =
|
||||
system: diskImageFun: prio:
|
||||
|
||||
with import nixpkgs { inherit system; };
|
||||
with import <nixpkgs> { inherit system; };
|
||||
|
||||
releaseTools.rpmBuild rec {
|
||||
name = "nix-rpm-${diskImage.name}";
|
||||
name = "nix-rpm";
|
||||
src = jobs.tarball;
|
||||
diskImage = (diskImageFun vmTools.diskImageFuns)
|
||||
{ extraPackages = [ "perl-DBD-SQLite" "perl-devel" "sqlite" "sqlite-devel" "bzip2-devel" "emacs" ]; };
|
||||
{ extraPackages = [ "perl-DBD-SQLite" "perl-devel" "sqlite" "sqlite-devel" "bzip2-devel" "emacs" "perl-WWW-Curl" ]; };
|
||||
memSize = 1024;
|
||||
meta.schedulingPriority = prio;
|
||||
postRPMInstall = "cd /tmp/rpmout/BUILD/nix-* && make installcheck";
|
||||
@@ -201,21 +227,21 @@ let
|
||||
|
||||
makeDeb_i686 = makeDeb "i686-linux";
|
||||
makeDeb_x86_64 = makeDeb "x86_64-linux";
|
||||
|
||||
|
||||
makeDeb =
|
||||
system: diskImageFun: prio:
|
||||
|
||||
with import nixpkgs { inherit system; };
|
||||
with import <nixpkgs> { inherit system; };
|
||||
|
||||
releaseTools.debBuild {
|
||||
name = "nix-deb";
|
||||
src = jobs.tarball;
|
||||
diskImage = (diskImageFun vmTools.diskImageFuns)
|
||||
{ extraPackages = [ "libdbd-sqlite3-perl" "libsqlite3-dev" "libbz2-dev" ]; };
|
||||
{ extraPackages = [ "libdbd-sqlite3-perl" "libsqlite3-dev" "libbz2-dev" "libwww-curl-perl" ]; };
|
||||
memSize = 1024;
|
||||
meta.schedulingPriority = prio;
|
||||
configureFlags = "--sysconfdir=/etc";
|
||||
debRequires = [ "curl" "libdbd-sqlite3-perl" "libsqlite3-0" "libbz2-1.0" ];
|
||||
debRequires = [ "curl" "libdbd-sqlite3-perl" "libsqlite3-0" "libbz2-1.0" "bzip2" "xz-utils" "libwww-curl-perl" ];
|
||||
doInstallCheck = true;
|
||||
};
|
||||
|
||||
|
||||
+7
-6
@@ -7,18 +7,18 @@ noinst_SCRIPTS = nix-profile.sh \
|
||||
find-runtime-roots.pl build-remote.pl nix-reduce-build \
|
||||
copy-from-other-stores.pl nix-http-export.cgi
|
||||
|
||||
nix-pull nix-push: download-using-manifests.pl
|
||||
profiledir = $(sysconfdir)/profile.d
|
||||
|
||||
install-exec-local: download-using-manifests.pl copy-from-other-stores.pl find-runtime-roots.pl
|
||||
$(INSTALL) -d $(DESTDIR)$(sysconfdir)/profile.d
|
||||
$(INSTALL_DATA) nix-profile.sh $(DESTDIR)$(sysconfdir)/profile.d/nix.sh
|
||||
install-exec-local: download-using-manifests.pl copy-from-other-stores.pl download-from-binary-cache.pl find-runtime-roots.pl
|
||||
$(INSTALL) -d $(DESTDIR)$(profiledir)
|
||||
$(INSTALL_DATA) nix-profile.sh $(DESTDIR)$(profiledir)/nix.sh
|
||||
$(INSTALL) -d $(DESTDIR)$(libexecdir)/nix
|
||||
$(INSTALL_PROGRAM) find-runtime-roots.pl $(DESTDIR)$(libexecdir)/nix
|
||||
$(INSTALL_PROGRAM) build-remote.pl $(DESTDIR)$(libexecdir)/nix
|
||||
$(INSTALL) -d $(DESTDIR)$(libexecdir)/nix/substituters
|
||||
$(INSTALL_PROGRAM) download-using-manifests.pl $(DESTDIR)$(libexecdir)/nix/substituters
|
||||
$(INSTALL_PROGRAM) copy-from-other-stores.pl $(DESTDIR)$(libexecdir)/nix/substituters
|
||||
$(INSTALL_PROGRAM) download-using-manifests.pl copy-from-other-stores.pl download-from-binary-cache.pl $(DESTDIR)$(libexecdir)/nix/substituters
|
||||
$(INSTALL) -d $(DESTDIR)$(sysconfdir)/nix
|
||||
ln -sf nix-build $(DESTDIR)$(bindir)/nix-shell
|
||||
|
||||
include ../substitute.mk
|
||||
|
||||
@@ -29,6 +29,7 @@ EXTRA_DIST = nix-collect-garbage.in \
|
||||
nix-build.in \
|
||||
download-using-manifests.pl.in \
|
||||
copy-from-other-stores.pl.in \
|
||||
download-from-binary-cache.pl.in \
|
||||
nix-copy-closure.in \
|
||||
find-runtime-roots.pl.in \
|
||||
build-remote.pl.in \
|
||||
|
||||
+28
-23
@@ -6,6 +6,7 @@ use IO::Handle;
|
||||
use Nix::Config;
|
||||
use Nix::SSH qw/sshOpts openSSHConnection/;
|
||||
use Nix::CopyClosure;
|
||||
use Nix::Store;
|
||||
no warnings('once');
|
||||
|
||||
|
||||
@@ -18,7 +19,7 @@ no warnings('once');
|
||||
# less than the maximum load for that machine, we try to get an
|
||||
# exclusive lock on $currentLoad/$machine-$slot (without blocking).
|
||||
# If we get such a lock, we send "accept" to the caller. Otherwise,
|
||||
# we send "postpone" and exit.
|
||||
# we send "postpone" and exit.
|
||||
# - We release the exclusive lock on $currentLoad/main-lock.
|
||||
# - We perform the build on $neededSystem.
|
||||
# - We release the exclusive lock on $currentLoad/$machine-$slot.
|
||||
@@ -45,8 +46,7 @@ sub all { $_ || return 0 for @_; 1 }
|
||||
# Initialisation.
|
||||
my $loadIncreased = 0;
|
||||
|
||||
my ($localSystem, $maxSilentTime, $printBuildTrace) = @ARGV;
|
||||
$maxSilentTime = 0 unless defined $maxSilentTime;
|
||||
my ($localSystem, $maxSilentTime, $printBuildTrace, $buildTimeout) = @ARGV;
|
||||
|
||||
my $currentLoad = $ENV{"NIX_CURRENT_LOAD"};
|
||||
my $conf = $ENV{"NIX_REMOTE_SYSTEMS"};
|
||||
@@ -102,14 +102,14 @@ REQ: while (1) {
|
||||
sendReply "decline";
|
||||
next;
|
||||
}
|
||||
|
||||
|
||||
# Acquire the exclusive lock on $currentLoad/main-lock.
|
||||
mkdir $currentLoad, 0777 or die unless -d $currentLoad;
|
||||
my $mainLock = "$currentLoad/main-lock";
|
||||
sysopen MAINLOCK, "$mainLock", O_RDWR|O_CREAT, 0600 or die;
|
||||
flock(MAINLOCK, LOCK_EX) or die;
|
||||
|
||||
|
||||
|
||||
|
||||
while (1) {
|
||||
# Find all machine that can execute this build, i.e., that
|
||||
# support builds for the given platform and features, and are
|
||||
@@ -141,7 +141,7 @@ REQ: while (1) {
|
||||
close $slotLock;
|
||||
$slot++;
|
||||
}
|
||||
|
||||
|
||||
push @available, { machine => $cur, load => $load, free => $free }
|
||||
if $load < $cur->{maxJobs};
|
||||
}
|
||||
@@ -160,7 +160,7 @@ REQ: while (1) {
|
||||
if ($rightType && !$canBuildLocally) {
|
||||
sendReply "postpone";
|
||||
} else {
|
||||
sendReply "decline";
|
||||
sendReply "decline";
|
||||
}
|
||||
close MAINLOCK;
|
||||
next REQ;
|
||||
@@ -182,9 +182,9 @@ REQ: while (1) {
|
||||
|
||||
|
||||
# Select the best available machine and lock a free slot.
|
||||
my $selected = $available[0];
|
||||
my $selected = $available[0];
|
||||
my $machine = $selected->{machine};
|
||||
|
||||
|
||||
$slotLock = openSlotLock($machine, $selected->{free});
|
||||
flock($slotLock, LOCK_EX | LOCK_NB) or die;
|
||||
utime undef, undef, $slotLock;
|
||||
@@ -195,9 +195,13 @@ REQ: while (1) {
|
||||
# Connect to the selected machine.
|
||||
@sshOpts = ("-i", $machine->{sshKeys}, "-x");
|
||||
$hostName = $machine->{hostName};
|
||||
last REQ if openSSHConnection $hostName;
|
||||
|
||||
warn "unable to open SSH connection to $hostName, trying other available machines...\n";
|
||||
if (openSSHConnection($hostName)) {
|
||||
last REQ if system("ssh $hostName @sshOpts nix-builds-inhibited < /dev/null > /dev/null 2>&1") != 0;
|
||||
warn "machine `$hostName' is refusing builds, trying other available machines...\n";
|
||||
closeSSHConnection;
|
||||
} else {
|
||||
warn "unable to open SSH connection to `$hostName', trying other available machines...\n";
|
||||
}
|
||||
$machine->{enabled} = 0;
|
||||
}
|
||||
}
|
||||
@@ -209,7 +213,6 @@ my @inputs = split /\s/, readline(STDIN);
|
||||
my @outputs = split /\s/, readline(STDIN);
|
||||
|
||||
|
||||
print STDERR "building `$drvPath' on `$hostName'\n";
|
||||
print STDERR "@ build-remote $drvPath $hostName\n" if $printBuildTrace;
|
||||
|
||||
|
||||
@@ -257,7 +260,10 @@ close UPLOADLOCK;
|
||||
|
||||
|
||||
# Perform the build.
|
||||
my $buildFlags = "--max-silent-time $maxSilentTime --fallback --add-root $rootsDir/\$PPID.out --option verbosity 0";
|
||||
my $buildFlags =
|
||||
"--max-silent-time $maxSilentTime --option build-timeout $buildTimeout"
|
||||
. " --fallback --add-root $rootsDir/\$PPID.out --quiet"
|
||||
. " --option build-keep-log false";
|
||||
|
||||
# We let the remote side kill its process group when the connection is
|
||||
# closed unexpectedly. This is necessary to ensure that no processes
|
||||
@@ -266,8 +272,9 @@ my $buildFlags = "--max-silent-time $maxSilentTime --fallback --add-root $rootsD
|
||||
# is interrupted unless the `-tt' flag is used to force a pseudo-tty,
|
||||
# in which case every child receives SIGHUP; however, `-tt' doesn't
|
||||
# work on some platforms when connection sharing is used.)
|
||||
print STDERR "building `$drvPath' on `$hostName'\n";
|
||||
pipe STDIN, DUMMY; # make sure we have a readable STDIN
|
||||
if (system("exec ssh $hostName @sshOpts '(read; kill -INT -\$\$) <&0 & nix-store -r $drvPath $buildFlags > /dev/null' 2>&4") != 0) {
|
||||
if (system("exec ssh $hostName @sshOpts '(read; kill -INT -\$\$) <&0 & exec nix-store -r $drvPath $buildFlags > /dev/null' 2>&4") != 0) {
|
||||
# Note that if we get exit code 100 from `nix-store -r', it
|
||||
# denotes a permanent build failure (as opposed to an SSH problem
|
||||
# or a temporary Nix problem). We propagate this to the caller to
|
||||
@@ -283,13 +290,11 @@ if (system("exec ssh $hostName @sshOpts '(read; kill -INT -\$\$) <&0 & nix-store
|
||||
|
||||
|
||||
# Copy the output from the build machine.
|
||||
foreach my $output (@outputs) {
|
||||
my $maybeSignRemote = "";
|
||||
$maybeSignRemote = "--sign" if $UID != 0;
|
||||
|
||||
system("exec ssh $hostName @sshOpts 'nix-store --export $maybeSignRemote $output'" .
|
||||
"| NIX_HELD_LOCKS=$output @bindir@/nix-store --import > /dev/null") == 0
|
||||
or die "cannot copy $output from $hostName: $?";
|
||||
my @outputs2 = grep { !isValidPath($_) } @outputs;
|
||||
if (scalar @outputs2 > 0) {
|
||||
system("exec ssh $hostName @sshOpts 'nix-store --export @outputs2'" .
|
||||
"| NIX_HELD_LOCKS='@outputs2' @bindir@/nix-store --import > /dev/null") == 0
|
||||
or die("cannot copy paths " . join(", ", @outputs) . " from `$hostName': $?");
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -16,6 +16,9 @@ foreach my $dir (@remoteStoresAll) {
|
||||
push @remoteStores, glob($dir);
|
||||
}
|
||||
|
||||
exit if scalar @remoteStores == 0;
|
||||
print "\n";
|
||||
|
||||
|
||||
$ENV{"NIX_REMOTE"} = "";
|
||||
|
||||
@@ -27,7 +30,7 @@ sub findStorePath {
|
||||
next unless -e $sourcePath || -l $sourcePath;
|
||||
$ENV{"NIX_DB_DIR"} = "$store/var/nix/db";
|
||||
return ($store, $sourcePath) if
|
||||
system("@bindir@/nix-store --check-validity $storePath") == 0;
|
||||
system("$binDir/nix-store --check-validity $storePath") == 0;
|
||||
}
|
||||
return undef;
|
||||
}
|
||||
@@ -36,42 +39,45 @@ sub findStorePath {
|
||||
if ($ARGV[0] eq "--query") {
|
||||
|
||||
while (<STDIN>) {
|
||||
my $cmd = $_; chomp $cmd;
|
||||
chomp;
|
||||
my ($cmd, @args) = split " ", $_;
|
||||
|
||||
if ($cmd eq "have") {
|
||||
my $storePath = <STDIN>; chomp $storePath;
|
||||
print STDOUT (defined findStorePath($storePath) ? "1\n" : "0\n");
|
||||
foreach my $storePath (@args) {
|
||||
print "$storePath\n" if defined findStorePath($storePath);
|
||||
}
|
||||
print "\n";
|
||||
}
|
||||
|
||||
elsif ($cmd eq "info") {
|
||||
my $storePath = <STDIN>; chomp $storePath;
|
||||
my ($store, $sourcePath) = findStorePath($storePath);
|
||||
if (!defined $store) {
|
||||
foreach my $storePath (@args) {
|
||||
my ($store, $sourcePath) = findStorePath($storePath);
|
||||
next unless defined $store;
|
||||
|
||||
$ENV{"NIX_DB_DIR"} = "$store/var/nix/db";
|
||||
|
||||
my $deriver = `$binDir/nix-store --query --deriver $storePath`;
|
||||
die "cannot query deriver of `$storePath'" if $? != 0;
|
||||
chomp $deriver;
|
||||
$deriver = "" if $deriver eq "unknown-deriver";
|
||||
|
||||
my @references = split "\n",
|
||||
`$binDir/nix-store --query --references $storePath`;
|
||||
die "cannot query references of `$storePath'" if $? != 0;
|
||||
|
||||
my $narSize = `$binDir/nix-store --query --size $storePath`;
|
||||
die "cannot query size of `$storePath'" if $? != 0;
|
||||
chomp $narSize;
|
||||
|
||||
print "$storePath\n";
|
||||
print "$deriver\n";
|
||||
print scalar @references, "\n";
|
||||
print "$_\n" foreach @references;
|
||||
print "0\n";
|
||||
next; # not an error
|
||||
print "$narSize\n";
|
||||
}
|
||||
print "1\n";
|
||||
|
||||
$ENV{"NIX_DB_DIR"} = "$store/var/nix/db";
|
||||
|
||||
my $deriver = `@bindir@/nix-store --query --deriver $storePath`;
|
||||
die "cannot query deriver of `$storePath'" if $? != 0;
|
||||
chomp $deriver;
|
||||
$deriver = "" if $deriver eq "unknown-deriver";
|
||||
|
||||
my @references = split "\n",
|
||||
`@bindir@/nix-store --query --references $storePath`;
|
||||
die "cannot query references of `$storePath'" if $? != 0;
|
||||
|
||||
my $narSize = `@bindir@/nix-store --query --size $storePath`;
|
||||
die "cannot query size of `$storePath'" if $? != 0;
|
||||
chomp $narSize;
|
||||
|
||||
print "$deriver\n";
|
||||
print scalar @references, "\n";
|
||||
print "$_\n" foreach @references;
|
||||
print "$narSize\n";
|
||||
print "$narSize\n";
|
||||
print "\n";
|
||||
}
|
||||
|
||||
else { die "unknown command `$cmd'"; }
|
||||
@@ -80,13 +86,15 @@ if ($ARGV[0] eq "--query") {
|
||||
|
||||
|
||||
elsif ($ARGV[0] eq "--substitute") {
|
||||
die unless scalar @ARGV == 2;
|
||||
die unless scalar @ARGV == 3;
|
||||
my $storePath = $ARGV[1];
|
||||
my $destPath = $ARGV[2];
|
||||
my ($store, $sourcePath) = findStorePath $storePath;
|
||||
die unless $store;
|
||||
print "\n*** Copying `$storePath' from `$sourcePath'\n\n";
|
||||
system("$binDir/nix-store --dump $sourcePath | $binDir/nix-store --restore $storePath") == 0
|
||||
print STDERR "\n*** Copying `$storePath' from `$sourcePath'\n\n";
|
||||
system("$binDir/nix-store --dump $sourcePath | $binDir/nix-store --restore $destPath") == 0
|
||||
or die "cannot copy `$sourcePath' to `$storePath'";
|
||||
print "\n"; # no hash to verify
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,588 @@
|
||||
#! @perl@ -w @perlFlags@
|
||||
|
||||
use DBI;
|
||||
use File::Basename;
|
||||
use IO::Select;
|
||||
use Nix::Config;
|
||||
use Nix::Store;
|
||||
use Nix::Utils;
|
||||
use Nix::Manifest;
|
||||
use WWW::Curl::Easy;
|
||||
use WWW::Curl::Multi;
|
||||
use strict;
|
||||
|
||||
|
||||
Nix::Config::readConfig;
|
||||
|
||||
my @caches;
|
||||
my $gotCaches = 0;
|
||||
|
||||
my $maxParallelRequests = int($Nix::Config::config{"binary-caches-parallel-connections"} // 150);
|
||||
$maxParallelRequests = 1 if $maxParallelRequests < 1;
|
||||
|
||||
my $ttlNegative = 24 * 3600; # when to purge negative lookups from the database
|
||||
my $ttlNegativeUse = 3600; # how long negative lookups are valid for non-"have" lookups
|
||||
my $didExpiration = 0;
|
||||
|
||||
my $showAfter = 5; # show that we're waiting for a request after this many seconds
|
||||
|
||||
my $debug = ($Nix::Config::config{"debug-subst"} // "") eq 1 || ($Nix::Config::config{"untrusted-debug-subst"} // "") eq 1;
|
||||
|
||||
my $cacheFileURLs = ($ENV{"_NIX_CACHE_FILE_URLS"} // "") eq 1; # for testing
|
||||
|
||||
my ($dbh, $queryCache, $insertNAR, $queryNAR, $insertNARExistence, $queryNARExistence, $expireNARExistence);
|
||||
|
||||
my $curlm = WWW::Curl::Multi->new;
|
||||
my $activeRequests = 0;
|
||||
my $curlIdCount = 1;
|
||||
my %requests;
|
||||
my %scheduled;
|
||||
my $caBundle = $ENV{"CURL_CA_BUNDLE"} // $ENV{"OPENSSL_X509_CERT_FILE"};
|
||||
|
||||
my $userName = getpwuid($<) or die "cannot figure out user name";
|
||||
|
||||
|
||||
sub addRequest {
|
||||
my ($storePath, $url, $head) = @_;
|
||||
|
||||
my $curl = WWW::Curl::Easy->new;
|
||||
my $curlId = $curlIdCount++;
|
||||
$requests{$curlId} = { storePath => $storePath, url => $url, handle => $curl, content => "", type => $head ? "HEAD" : "GET"
|
||||
, shown => 0, started => time() };
|
||||
|
||||
$curl->setopt(CURLOPT_PRIVATE, $curlId);
|
||||
$curl->setopt(CURLOPT_URL, $url);
|
||||
open (my $fh, ">", \$requests{$curlId}->{content});
|
||||
$curl->setopt(CURLOPT_WRITEDATA, $fh);
|
||||
$curl->setopt(CURLOPT_FOLLOWLOCATION, 1);
|
||||
$curl->setopt(CURLOPT_CAINFO, $caBundle) if defined $caBundle;
|
||||
$curl->setopt(CURLOPT_USERAGENT, "Nix/$Nix::Config::version");
|
||||
$curl->setopt(CURLOPT_NOBODY, 1) if $head;
|
||||
$curl->setopt(CURLOPT_FAILONERROR, 1);
|
||||
$curl->setopt(CURLOPT_TIMEOUT, int($ENV{"NIX_CONNECT_TIMEOUT"} // 0));
|
||||
|
||||
if ($activeRequests >= $maxParallelRequests) {
|
||||
$scheduled{$curlId} = 1;
|
||||
} else {
|
||||
$curlm->add_handle($curl);
|
||||
$activeRequests++;
|
||||
}
|
||||
|
||||
return $requests{$curlId};
|
||||
}
|
||||
|
||||
|
||||
sub processRequests {
|
||||
while ($activeRequests) {
|
||||
my ($rfds, $wfds, $efds) = $curlm->fdset();
|
||||
#print STDERR "R = @{$rfds}, W = @{$wfds}, E = @{$efds}\n";
|
||||
|
||||
# Sleep until we can read or write some data.
|
||||
if (scalar @{$rfds} + scalar @{$wfds} + scalar @{$efds} > 0) {
|
||||
IO::Select->select(IO::Select->new(@{$rfds}), IO::Select->new(@{$wfds}), IO::Select->new(@{$efds}), 1.0);
|
||||
}
|
||||
|
||||
if ($curlm->perform() != $activeRequests) {
|
||||
while (my ($id, $result) = $curlm->info_read) {
|
||||
if ($id) {
|
||||
my $request = $requests{$id} or die;
|
||||
my $handle = $request->{handle};
|
||||
$request->{result} = $result;
|
||||
$request->{httpStatus} = $handle->getinfo(CURLINFO_RESPONSE_CODE);
|
||||
|
||||
print STDERR "$request->{type} on $request->{url} [$request->{result}, $request->{httpStatus}]\n" if $debug;
|
||||
|
||||
$activeRequests--;
|
||||
delete $request->{handle};
|
||||
|
||||
if (scalar(keys %scheduled) > 0) {
|
||||
my $id2 = (keys %scheduled)[0];
|
||||
$curlm->add_handle($requests{$id2}->{handle});
|
||||
$activeRequests++;
|
||||
delete $scheduled{$id2};
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
my $time = time();
|
||||
while (my ($key, $request) = each %requests) {
|
||||
next unless defined $request->{handle};
|
||||
next if $request->{shown};
|
||||
if ($time > $request->{started} + $showAfter) {
|
||||
print STDERR "still waiting for ‘$request->{url}’ after $showAfter seconds...\n";
|
||||
$request->{shown} = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
sub initCache {
|
||||
my $dbPath = "$Nix::Config::stateDir/binary-cache-v2.sqlite";
|
||||
|
||||
unlink "$Nix::Config::stateDir/binary-cache-v1.sqlite";
|
||||
|
||||
# Open/create the database.
|
||||
$dbh = DBI->connect("dbi:SQLite:dbname=$dbPath", "", "")
|
||||
or die "cannot open database `$dbPath'";
|
||||
$dbh->{RaiseError} = 1;
|
||||
$dbh->{PrintError} = 0;
|
||||
|
||||
$dbh->sqlite_busy_timeout(60 * 60 * 1000);
|
||||
|
||||
$dbh->do("pragma synchronous = off"); # we can always reproduce the cache
|
||||
$dbh->do("pragma journal_mode = truncate");
|
||||
|
||||
# Initialise the database schema, if necessary.
|
||||
$dbh->do(<<EOF);
|
||||
create table if not exists BinaryCaches (
|
||||
id integer primary key autoincrement not null,
|
||||
url text unique not null,
|
||||
timestamp integer not null,
|
||||
storeDir text not null,
|
||||
wantMassQuery integer not null,
|
||||
priority integer not null
|
||||
);
|
||||
EOF
|
||||
|
||||
$dbh->do(<<EOF);
|
||||
create table if not exists NARs (
|
||||
cache integer not null,
|
||||
storePath text not null,
|
||||
url text not null,
|
||||
compression text not null,
|
||||
fileHash text,
|
||||
fileSize integer,
|
||||
narHash text,
|
||||
narSize integer,
|
||||
refs text,
|
||||
deriver text,
|
||||
system text,
|
||||
timestamp integer not null,
|
||||
primary key (cache, storePath),
|
||||
foreign key (cache) references BinaryCaches(id) on delete cascade
|
||||
);
|
||||
EOF
|
||||
|
||||
$dbh->do(<<EOF);
|
||||
create table if not exists NARExistence (
|
||||
cache integer not null,
|
||||
storePath text not null,
|
||||
exist integer not null,
|
||||
timestamp integer not null,
|
||||
primary key (cache, storePath),
|
||||
foreign key (cache) references BinaryCaches(id) on delete cascade
|
||||
);
|
||||
EOF
|
||||
|
||||
$dbh->do("create index if not exists NARExistenceByExistTimestamp on NARExistence (exist, timestamp)");
|
||||
|
||||
$queryCache = $dbh->prepare("select id, storeDir, wantMassQuery, priority from BinaryCaches where url = ?") or die;
|
||||
|
||||
$insertNAR = $dbh->prepare(
|
||||
"insert or replace into NARs(cache, storePath, url, compression, fileHash, fileSize, narHash, " .
|
||||
"narSize, refs, deriver, system, timestamp) values (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)") or die;
|
||||
|
||||
$queryNAR = $dbh->prepare("select * from NARs where cache = ? and storePath = ?") or die;
|
||||
|
||||
$insertNARExistence = $dbh->prepare(
|
||||
"insert or replace into NARExistence(cache, storePath, exist, timestamp) values (?, ?, ?, ?)") or die;
|
||||
|
||||
$queryNARExistence = $dbh->prepare("select exist, timestamp from NARExistence where cache = ? and storePath = ?") or die;
|
||||
|
||||
$expireNARExistence = $dbh->prepare("delete from NARExistence where exist = ? and timestamp < ?") or die;
|
||||
}
|
||||
|
||||
|
||||
sub getAvailableCaches {
|
||||
return if $gotCaches;
|
||||
$gotCaches = 1;
|
||||
|
||||
sub strToList {
|
||||
my ($s) = @_;
|
||||
return map { s/\/+$//; $_ } split(/ /, $s);
|
||||
}
|
||||
|
||||
my @urls = strToList($Nix::Config::config{"binary-caches"} //
|
||||
($Nix::Config::storeDir eq "/nix/store" ? "http://cache.nixos.org" : ""));
|
||||
|
||||
my $urlsFiles = $Nix::Config::config{"binary-cache-files"}
|
||||
// "$Nix::Config::stateDir/profiles/per-user/$userName/channels/binary-caches/*";
|
||||
foreach my $urlFile (glob $urlsFiles) {
|
||||
next unless -f $urlFile;
|
||||
open FILE, "<$urlFile" or die "cannot open ‘$urlFile’\n";
|
||||
my $url = <FILE>; chomp $url;
|
||||
close FILE;
|
||||
push @urls, strToList($url);
|
||||
}
|
||||
|
||||
push @urls, strToList($Nix::Config::config{"extra-binary-caches"} // "");
|
||||
|
||||
# Allow Nix daemon users to override the binary caches to a subset
|
||||
# of those listed in the config file. Note that ‘untrusted-*’
|
||||
# denotes options passed by the client.
|
||||
my @trustedUrls = uniq(@urls, strToList($Nix::Config::config{"trusted-binary-caches"} // ""));
|
||||
|
||||
if (defined $Nix::Config::config{"untrusted-binary-caches"}) {
|
||||
my @untrustedUrls = strToList $Nix::Config::config{"untrusted-binary-caches"};
|
||||
@urls = ();
|
||||
foreach my $url (@untrustedUrls) {
|
||||
die "binary cache ‘$url’ is not trusted (please add it to ‘trusted-binary-caches’ [@trustedUrls] in $Nix::Config::confDir/nix.conf)\n"
|
||||
unless scalar(grep { $url eq $_ } @trustedUrls) > 0;
|
||||
push @urls, $url;
|
||||
}
|
||||
}
|
||||
|
||||
my @untrustedUrls = strToList $Nix::Config::config{"untrusted-extra-binary-caches"} // "";
|
||||
foreach my $url (@untrustedUrls) {
|
||||
next unless scalar(grep { $url eq $_ } @trustedUrls) > 0;
|
||||
push @urls, $url;
|
||||
}
|
||||
|
||||
foreach my $url (uniq @urls) {
|
||||
|
||||
# FIXME: not atomic.
|
||||
$queryCache->execute($url);
|
||||
my $res = $queryCache->fetchrow_hashref();
|
||||
if (defined $res) {
|
||||
next if $res->{storeDir} ne $Nix::Config::storeDir;
|
||||
push @caches, { id => $res->{id}, url => $url, wantMassQuery => $res->{wantMassQuery}, priority => $res->{priority} };
|
||||
next;
|
||||
}
|
||||
|
||||
# Get the cache info file.
|
||||
my $request = addRequest(undef, $url . "/nix-cache-info");
|
||||
processRequests;
|
||||
|
||||
if ($request->{result} != 0) {
|
||||
print STDERR "could not download ‘$request->{url}’ (" .
|
||||
($request->{result} != 0 ? "Curl error $request->{result}" : "HTTP status $request->{httpStatus}") . ")\n";
|
||||
next;
|
||||
}
|
||||
|
||||
my $storeDir = "/nix/store";
|
||||
my $wantMassQuery = 0;
|
||||
my $priority = 50;
|
||||
foreach my $line (split "\n", $request->{content}) {
|
||||
unless ($line =~ /^(.*): (.*)$/) {
|
||||
print STDERR "bad cache info file ‘$request->{url}’\n";
|
||||
return undef;
|
||||
}
|
||||
if ($1 eq "StoreDir") { $storeDir = $2; }
|
||||
elsif ($1 eq "WantMassQuery") { $wantMassQuery = int($2); }
|
||||
elsif ($1 eq "Priority") { $priority = int($2); }
|
||||
}
|
||||
|
||||
$dbh->do("insert or replace into BinaryCaches(url, timestamp, storeDir, wantMassQuery, priority) values (?, ?, ?, ?, ?)",
|
||||
{}, $url, time(), $storeDir, $wantMassQuery, $priority);
|
||||
$queryCache->execute($url);
|
||||
$res = $queryCache->fetchrow_hashref() or die;
|
||||
next if $storeDir ne $Nix::Config::storeDir;
|
||||
push @caches, { id => $res->{id}, url => $url, wantMassQuery => $wantMassQuery, priority => $priority };
|
||||
}
|
||||
|
||||
@caches = sort { $a->{priority} <=> $b->{priority} } @caches;
|
||||
|
||||
expireNegative();
|
||||
}
|
||||
|
||||
|
||||
sub shouldCache {
|
||||
my ($url) = @_;
|
||||
return $cacheFileURLs || $url !~ /^file:/;
|
||||
}
|
||||
|
||||
|
||||
sub processNARInfo {
|
||||
my ($storePath, $cache, $request) = @_;
|
||||
|
||||
if ($request->{result} != 0) {
|
||||
if ($request->{result} != 37 && $request->{httpStatus} != 404 && $request->{httpStatus} != 403) {
|
||||
print STDERR "could not download ‘$request->{url}’ (" .
|
||||
($request->{result} != 0 ? "Curl error $request->{result}" : "HTTP status $request->{httpStatus}") . ")\n";
|
||||
} else {
|
||||
$insertNARExistence->execute($cache->{id}, basename($storePath), 0, time())
|
||||
if shouldCache $request->{url};
|
||||
}
|
||||
return undef;
|
||||
}
|
||||
|
||||
my $narInfo = parseNARInfo($storePath, $request->{content});
|
||||
return undef unless defined $narInfo;
|
||||
|
||||
# Cache the result.
|
||||
$insertNAR->execute(
|
||||
$cache->{id}, basename($storePath), $narInfo->{url}, $narInfo->{compression},
|
||||
$narInfo->{fileHash}, $narInfo->{fileSize}, $narInfo->{narHash}, $narInfo->{narSize},
|
||||
join(" ", @{$narInfo->{refs}}), $narInfo->{deriver}, $narInfo->{system}, time())
|
||||
if shouldCache $request->{url};
|
||||
|
||||
return $narInfo;
|
||||
}
|
||||
|
||||
|
||||
sub getCachedInfoFrom {
|
||||
my ($storePath, $cache) = @_;
|
||||
|
||||
$queryNAR->execute($cache->{id}, basename($storePath));
|
||||
my $res = $queryNAR->fetchrow_hashref();
|
||||
return undef unless defined $res;
|
||||
|
||||
return
|
||||
{ url => $res->{url}
|
||||
, compression => $res->{compression}
|
||||
, fileHash => $res->{fileHash}
|
||||
, fileSize => $res->{fileSize}
|
||||
, narHash => $res->{narHash}
|
||||
, narSize => $res->{narSize}
|
||||
, refs => [ split " ", $res->{refs} ]
|
||||
, deriver => $res->{deriver}
|
||||
} if defined $res;
|
||||
}
|
||||
|
||||
|
||||
sub negativeHit {
|
||||
my ($storePath, $cache) = @_;
|
||||
$queryNARExistence->execute($cache->{id}, basename($storePath));
|
||||
my $res = $queryNARExistence->fetchrow_hashref();
|
||||
return defined $res && $res->{exist} == 0 && time() - $res->{timestamp} < $ttlNegativeUse;
|
||||
}
|
||||
|
||||
|
||||
sub positiveHit {
|
||||
my ($storePath, $cache) = @_;
|
||||
return 1 if defined getCachedInfoFrom($storePath, $cache);
|
||||
$queryNARExistence->execute($cache->{id}, basename($storePath));
|
||||
my $res = $queryNARExistence->fetchrow_hashref();
|
||||
return defined $res && $res->{exist} == 1;
|
||||
}
|
||||
|
||||
|
||||
sub expireNegative {
|
||||
return if $didExpiration;
|
||||
$didExpiration = 1;
|
||||
my $time = time();
|
||||
# Round up to the next multiple of the TTL to ensure that we do
|
||||
# expiration only once per time interval. E.g. if $ttlNegative ==
|
||||
# 3600, we expire entries at most once per hour. This is
|
||||
# presumably faster than expiring a few entries per request (and
|
||||
# thus doing a transaction).
|
||||
my $limit = (int($time / $ttlNegative) - 1) * $ttlNegative;
|
||||
$expireNARExistence->execute($limit, 0);
|
||||
print STDERR "expired ", $expireNARExistence->rows, " negative entries\n" if $debug;
|
||||
}
|
||||
|
||||
|
||||
sub printInfo {
|
||||
my ($storePath, $info) = @_;
|
||||
print "$storePath\n";
|
||||
print $info->{deriver} ? "$Nix::Config::storeDir/$info->{deriver}" : "", "\n";
|
||||
print scalar @{$info->{refs}}, "\n";
|
||||
print "$Nix::Config::storeDir/$_\n" foreach @{$info->{refs}};
|
||||
print $info->{fileSize} || 0, "\n";
|
||||
print $info->{narSize} || 0, "\n";
|
||||
}
|
||||
|
||||
|
||||
sub infoUrl {
|
||||
my ($binaryCacheUrl, $storePath) = @_;
|
||||
my $pathHash = substr(basename($storePath), 0, 32);
|
||||
my $infoUrl = "$binaryCacheUrl/$pathHash.narinfo";
|
||||
}
|
||||
|
||||
|
||||
sub printInfoParallel {
|
||||
my @paths = @_;
|
||||
|
||||
# First print all paths for which we have cached info.
|
||||
my @left;
|
||||
foreach my $storePath (@paths) {
|
||||
my $found = 0;
|
||||
foreach my $cache (@caches) {
|
||||
my $info = getCachedInfoFrom($storePath, $cache);
|
||||
if (defined $info) {
|
||||
printInfo($storePath, $info);
|
||||
$found = 1;
|
||||
last;
|
||||
}
|
||||
}
|
||||
push @left, $storePath if !$found;
|
||||
}
|
||||
|
||||
return if scalar @left == 0;
|
||||
|
||||
foreach my $cache (@caches) {
|
||||
|
||||
my @left2;
|
||||
%requests = ();
|
||||
foreach my $storePath (@left) {
|
||||
if (negativeHit($storePath, $cache)) {
|
||||
push @left2, $storePath;
|
||||
next;
|
||||
}
|
||||
addRequest($storePath, infoUrl($cache->{url}, $storePath));
|
||||
}
|
||||
|
||||
processRequests;
|
||||
|
||||
foreach my $request (values %requests) {
|
||||
my $info = processNARInfo($request->{storePath}, $cache, $request);
|
||||
if (defined $info) {
|
||||
printInfo($request->{storePath}, $info);
|
||||
} else {
|
||||
push @left2, $request->{storePath};
|
||||
}
|
||||
}
|
||||
|
||||
@left = @left2;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
sub printSubstitutablePaths {
|
||||
my @paths = @_;
|
||||
|
||||
# First look for paths that have cached info.
|
||||
my @left;
|
||||
foreach my $storePath (@paths) {
|
||||
my $found = 0;
|
||||
foreach my $cache (@caches) {
|
||||
next unless $cache->{wantMassQuery};
|
||||
if (positiveHit($storePath, $cache)) {
|
||||
print "$storePath\n";
|
||||
$found = 1;
|
||||
last;
|
||||
}
|
||||
}
|
||||
push @left, $storePath if !$found;
|
||||
}
|
||||
|
||||
return if scalar @left == 0;
|
||||
|
||||
# For remaining paths, do HEAD requests.
|
||||
foreach my $cache (@caches) {
|
||||
next unless $cache->{wantMassQuery};
|
||||
my @left2;
|
||||
%requests = ();
|
||||
foreach my $storePath (@left) {
|
||||
if (negativeHit($storePath, $cache)) {
|
||||
push @left2, $storePath;
|
||||
next;
|
||||
}
|
||||
addRequest($storePath, infoUrl($cache->{url}, $storePath), 1);
|
||||
}
|
||||
|
||||
processRequests;
|
||||
|
||||
foreach my $request (values %requests) {
|
||||
if ($request->{result} != 0) {
|
||||
if ($request->{result} != 37 && $request->{httpStatus} != 404 && $request->{httpStatus} != 403) {
|
||||
print STDERR "could not check ‘$request->{url}’ (" .
|
||||
($request->{result} != 0 ? "Curl error $request->{result}" : "HTTP status $request->{httpStatus}") . ")\n";
|
||||
} else {
|
||||
$insertNARExistence->execute($cache->{id}, basename($request->{storePath}), 0, time())
|
||||
if shouldCache $request->{url};
|
||||
}
|
||||
push @left2, $request->{storePath};
|
||||
} else {
|
||||
$insertNARExistence->execute($cache->{id}, basename($request->{storePath}), 1, time())
|
||||
if shouldCache $request->{url};
|
||||
print "$request->{storePath}\n";
|
||||
}
|
||||
}
|
||||
|
||||
@left = @left2;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
sub downloadBinary {
|
||||
my ($storePath, $destPath) = @_;
|
||||
|
||||
foreach my $cache (@caches) {
|
||||
my $info = getCachedInfoFrom($storePath, $cache);
|
||||
|
||||
unless (defined $info) {
|
||||
next if negativeHit($storePath, $cache);
|
||||
my $request = addRequest($storePath, infoUrl($cache->{url}, $storePath));
|
||||
processRequests;
|
||||
$info = processNARInfo($storePath, $cache, $request);
|
||||
}
|
||||
|
||||
next unless defined $info;
|
||||
|
||||
my $decompressor;
|
||||
if ($info->{compression} eq "bzip2") { $decompressor = "| $Nix::Config::bzip2 -d"; }
|
||||
elsif ($info->{compression} eq "xz") { $decompressor = "| $Nix::Config::xz -d"; }
|
||||
elsif ($info->{compression} eq "none") { $decompressor = ""; }
|
||||
else {
|
||||
print STDERR "unknown compression method ‘$info->{compression}’\n";
|
||||
next;
|
||||
}
|
||||
my $url = "$cache->{url}/$info->{url}"; # FIXME: handle non-relative URLs
|
||||
print STDERR "\n*** Downloading ‘$url’ to ‘$storePath’...\n";
|
||||
checkURL $url;
|
||||
if (system("$Nix::Config::curl --fail --location --insecure '$url' $decompressor | $Nix::Config::binDir/nix-store --restore $destPath") != 0) {
|
||||
warn "download of `$url' failed" . ($! ? ": $!" : "") . "\n";
|
||||
next;
|
||||
}
|
||||
|
||||
# Tell Nix about the expected hash so it can verify it.
|
||||
print "$info->{narHash}\n";
|
||||
|
||||
print STDERR "\n";
|
||||
return;
|
||||
}
|
||||
|
||||
print STDERR "could not download ‘$storePath’ from any binary cache\n";
|
||||
exit 1;
|
||||
}
|
||||
|
||||
|
||||
# Bail out right away if binary caches are disabled.
|
||||
exit 0 if
|
||||
($Nix::Config::config{"use-binary-caches"} // "true") eq "false" ||
|
||||
($Nix::Config::config{"untrusted-use-binary-caches"} // "true") eq "false";
|
||||
print "\n";
|
||||
flush STDOUT;
|
||||
|
||||
initCache();
|
||||
|
||||
|
||||
if ($ARGV[0] eq "--query") {
|
||||
|
||||
while (<STDIN>) {
|
||||
getAvailableCaches;
|
||||
chomp;
|
||||
my ($cmd, @args) = split " ", $_;
|
||||
|
||||
if ($cmd eq "have") {
|
||||
print STDERR "checking binary caches for existence of @args\n" if $debug;
|
||||
printSubstitutablePaths(@args);
|
||||
print "\n";
|
||||
}
|
||||
|
||||
elsif ($cmd eq "info") {
|
||||
print STDERR "checking binary caches for info on @args\n" if $debug;
|
||||
printInfoParallel(@args);
|
||||
print "\n";
|
||||
}
|
||||
|
||||
else { die "unknown command `$cmd'"; }
|
||||
|
||||
flush STDOUT;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
elsif ($ARGV[0] eq "--substitute") {
|
||||
my $storePath = $ARGV[1] or die;
|
||||
my $destPath = $ARGV[2] or die;
|
||||
getAvailableCaches;
|
||||
downloadBinary($storePath, $destPath);
|
||||
}
|
||||
|
||||
else {
|
||||
die;
|
||||
}
|
||||
@@ -4,6 +4,7 @@ use strict;
|
||||
use Nix::Config;
|
||||
use Nix::Manifest;
|
||||
use Nix::Store;
|
||||
use Nix::Utils;
|
||||
use POSIX qw(strftime);
|
||||
use File::Temp qw(tempdir);
|
||||
|
||||
@@ -15,9 +16,14 @@ my $logFile = "$Nix::Config::logDir/downloads";
|
||||
# estimating the expected download size.
|
||||
my $fast = 1;
|
||||
|
||||
# ‘--insecure’ is fine because Nix verifies the hash of the result.
|
||||
my $curl = "$Nix::Config::curl --fail --location --insecure";
|
||||
|
||||
|
||||
# Open the manifest cache and update it if necessary.
|
||||
my $dbh = updateManifestDB();
|
||||
exit 0 unless defined $dbh; # exit if there are no manifests
|
||||
print "\n";
|
||||
|
||||
|
||||
# $hashCache->{$algo}->{$path} yields the $algo-hash of $path.
|
||||
@@ -38,7 +44,7 @@ sub parseHash {
|
||||
# given path.
|
||||
sub computeSmallestDownload {
|
||||
my $targetPath = shift;
|
||||
|
||||
|
||||
# Build a graph of all store paths that might contribute to the
|
||||
# construction of $targetPath, and the special node "start". The
|
||||
# edges are either patch operations, or downloads of full NAR
|
||||
@@ -93,7 +99,7 @@ sub computeSmallestDownload {
|
||||
my $patchList = $dbh->selectall_arrayref(
|
||||
"select * from Patches where storePath = ?",
|
||||
{ Slice => {} }, $u);
|
||||
|
||||
|
||||
foreach my $patch (@{$patchList}) {
|
||||
if (isValidPath($patch->{basePath})) {
|
||||
my ($baseHashAlgo, $baseHash) = parseHash $patch->{baseHash};
|
||||
@@ -106,7 +112,7 @@ sub computeSmallestDownload {
|
||||
$hash =~ s/.*://;
|
||||
$hashCache->{$baseHashAlgo}->{$patch->{basePath}} = $hash;
|
||||
}
|
||||
|
||||
|
||||
next if $hash ne $baseHash;
|
||||
}
|
||||
push @queue, $patch->{basePath};
|
||||
@@ -117,7 +123,7 @@ sub computeSmallestDownload {
|
||||
my $narFileList = $dbh->selectall_arrayref(
|
||||
"select * from NARs where storePath = ?",
|
||||
{ Slice => {} }, $u);
|
||||
|
||||
|
||||
foreach my $narFile (@{$narFileList}) {
|
||||
# !!! how to handle files whose size is not known in advance?
|
||||
# For now, assume some arbitrary size (1 GB).
|
||||
@@ -173,58 +179,56 @@ sub computeSmallestDownload {
|
||||
if ($ARGV[0] eq "--query") {
|
||||
|
||||
while (<STDIN>) {
|
||||
my $cmd = $_; chomp $cmd;
|
||||
chomp;
|
||||
my ($cmd, @args) = split " ", $_;
|
||||
|
||||
if ($cmd eq "have") {
|
||||
my $storePath = <STDIN>; chomp $storePath;
|
||||
print STDOUT (
|
||||
scalar @{$dbh->selectcol_arrayref("select 1 from NARs where storePath = ?", {}, $storePath)} > 0
|
||||
? "1\n" : "0\n");
|
||||
foreach my $storePath (@args) {
|
||||
print "$storePath\n" if scalar @{$dbh->selectcol_arrayref("select 1 from NARs where storePath = ?", {}, $storePath)} > 0;
|
||||
}
|
||||
print "\n";
|
||||
}
|
||||
|
||||
elsif ($cmd eq "info") {
|
||||
my $storePath = <STDIN>; chomp $storePath;
|
||||
foreach my $storePath (@args) {
|
||||
|
||||
my $infos = $dbh->selectall_arrayref(
|
||||
"select * from NARs where storePath = ?",
|
||||
{ Slice => {} }, $storePath);
|
||||
|
||||
my $info;
|
||||
if (scalar @{$infos} > 0) {
|
||||
$info = @{$infos}[0];
|
||||
}
|
||||
else {
|
||||
print "0\n";
|
||||
next; # not an error
|
||||
}
|
||||
my $infos = $dbh->selectall_arrayref(
|
||||
"select * from NARs where storePath = ?",
|
||||
{ Slice => {} }, $storePath);
|
||||
|
||||
print "1\n";
|
||||
print "$info->{deriver}\n";
|
||||
my @references = split " ", $info->{refs};
|
||||
print scalar @references, "\n";
|
||||
print "$_\n" foreach @references;
|
||||
next unless scalar @{$infos} > 0;
|
||||
my $info = @{$infos}[0];
|
||||
|
||||
my @path = computeSmallestDownload $storePath;
|
||||
print "$storePath\n";
|
||||
print "$info->{deriver}\n";
|
||||
my @references = split " ", $info->{refs};
|
||||
print scalar @references, "\n";
|
||||
print "$_\n" foreach @references;
|
||||
|
||||
my $downloadSize = 0;
|
||||
while (scalar @path > 0) {
|
||||
my $edge = pop @path;
|
||||
my $u = $edge->{start};
|
||||
my $v = $edge->{end};
|
||||
if ($edge->{type} eq "patch") {
|
||||
$downloadSize += $edge->{info}->{size} || 0;
|
||||
}
|
||||
elsif ($edge->{type} eq "narfile") {
|
||||
$downloadSize += $edge->{info}->{size} || 0;
|
||||
my @path = computeSmallestDownload $storePath;
|
||||
|
||||
my $downloadSize = 0;
|
||||
while (scalar @path > 0) {
|
||||
my $edge = pop @path;
|
||||
my $u = $edge->{start};
|
||||
my $v = $edge->{end};
|
||||
if ($edge->{type} eq "patch") {
|
||||
$downloadSize += $edge->{info}->{size} || 0;
|
||||
}
|
||||
elsif ($edge->{type} eq "narfile") {
|
||||
$downloadSize += $edge->{info}->{size} || 0;
|
||||
}
|
||||
}
|
||||
|
||||
print "$downloadSize\n";
|
||||
|
||||
my $narSize = $info->{narSize} || 0;
|
||||
print "$narSize\n";
|
||||
}
|
||||
|
||||
print "$downloadSize\n";
|
||||
|
||||
my $narSize = $info->{narSize} || 0;
|
||||
print "$narSize\n";
|
||||
print "\n";
|
||||
}
|
||||
|
||||
|
||||
else { die "unknown command `$cmd'"; }
|
||||
}
|
||||
|
||||
@@ -236,8 +240,9 @@ elsif ($ARGV[0] ne "--substitute") {
|
||||
}
|
||||
|
||||
|
||||
die unless scalar @ARGV == 2;
|
||||
die unless scalar @ARGV == 3;
|
||||
my $targetPath = $ARGV[1];
|
||||
my $destPath = $ARGV[2];
|
||||
$fast = 0;
|
||||
|
||||
|
||||
@@ -273,16 +278,6 @@ $dbh->disconnect;
|
||||
my $curStep = 1;
|
||||
my $maxStep = scalar @path;
|
||||
|
||||
sub downloadFile {
|
||||
my $url = shift;
|
||||
$ENV{"PRINT_PATH"} = 1;
|
||||
$ENV{"QUIET"} = 1;
|
||||
my ($hash, $path) = `$Nix::Config::binDir/nix-prefetch-url '$url'`;
|
||||
die "download of `$url' failed" . ($! ? ": $!" : "") . "\n" unless $? == 0;
|
||||
chomp $path;
|
||||
return $path;
|
||||
}
|
||||
|
||||
my $finalNarHash;
|
||||
|
||||
while (scalar @path > 0) {
|
||||
@@ -314,13 +309,16 @@ while (scalar @path > 0) {
|
||||
|
||||
# Download the patch.
|
||||
print STDERR " downloading patch...\n";
|
||||
my $patchPath = downloadFile "$patch->{url}";
|
||||
my $patchPath = "$tmpDir/patch";
|
||||
checkURL $patch->{url};
|
||||
system("$curl '$patch->{url}' -o $patchPath") == 0
|
||||
or die "cannot download patch `$patch->{url}'\n";
|
||||
|
||||
# Apply the patch to the NAR archive produced in step 1 (for
|
||||
# the already present path) or a later step (for patch sequences).
|
||||
print STDERR " applying patch...\n";
|
||||
system("$Nix::Config::libexecDir/bspatch $tmpNar $tmpNar2 $patchPath") == 0
|
||||
or die "cannot apply patch `$patchPath' to $tmpNar";
|
||||
or die "cannot apply patch `$patchPath' to $tmpNar\n";
|
||||
|
||||
if ($curStep < $maxStep) {
|
||||
# The archive will be used as the base of the next patch.
|
||||
@@ -329,8 +327,8 @@ while (scalar @path > 0) {
|
||||
# This was the last patch. Unpack the final NAR archive
|
||||
# into the target path.
|
||||
print STDERR " unpacking patched archive...\n";
|
||||
system("$Nix::Config::binDir/nix-store --restore $v < $tmpNar2") == 0
|
||||
or die "cannot unpack $tmpNar2 into `$v'";
|
||||
system("$Nix::Config::binDir/nix-store --restore $destPath < $tmpNar2") == 0
|
||||
or die "cannot unpack $tmpNar2 to `$v'\n";
|
||||
}
|
||||
|
||||
$finalNarHash = $patch->{narHash};
|
||||
@@ -338,24 +336,27 @@ while (scalar @path > 0) {
|
||||
|
||||
elsif ($edge->{type} eq "narfile") {
|
||||
my $narFile = $edge->{info};
|
||||
print STDERR "downloading `$narFile->{url}' into `$v'\n";
|
||||
print STDERR "downloading `$narFile->{url}' to `$v'\n";
|
||||
|
||||
my $size = $narFile->{size} || -1;
|
||||
print LOGFILE "$$ narfile $narFile->{url} $size $v\n";
|
||||
|
||||
# Download the archive.
|
||||
print STDERR " downloading archive...\n";
|
||||
my $narFilePath = downloadFile "$narFile->{url}";
|
||||
|
||||
checkURL $narFile->{url};
|
||||
|
||||
my $decompressor =
|
||||
$narFile->{compressionType} eq "bzip2" ? "| $Nix::Config::bzip2 -d" :
|
||||
$narFile->{compressionType} eq "xz" ? "| $Nix::Config::xz -d" :
|
||||
$narFile->{compressionType} eq "none" ? "" :
|
||||
die "unknown compression type `$narFile->{compressionType}'";
|
||||
|
||||
if ($curStep < $maxStep) {
|
||||
# The archive will be used a base to a patch.
|
||||
system("$Nix::Config::bzip2 -d < '$narFilePath' > $tmpNar") == 0
|
||||
or die "cannot unpack `$narFilePath' into `$v'";
|
||||
system("$curl '$narFile->{url}' $decompressor > $tmpNar") == 0
|
||||
or die "cannot download and unpack `$narFile->{url}' to `$v'\n";
|
||||
} else {
|
||||
# Unpack the archive into the target path.
|
||||
print STDERR " unpacking archive...\n";
|
||||
system("$Nix::Config::bzip2 -d < '$narFilePath' | $Nix::Config::binDir/nix-store --restore '$v'") == 0
|
||||
or die "cannot unpack `$narFilePath' into `$v'";
|
||||
# Unpack the archive to the target path.
|
||||
system("$curl '$narFile->{url}' $decompressor | $Nix::Config::binDir/nix-store --restore '$destPath'") == 0
|
||||
or die "cannot download and unpack `$narFile->{url}' to `$v'\n";
|
||||
}
|
||||
|
||||
$finalNarHash = $narFile->{narHash};
|
||||
@@ -365,21 +366,10 @@ while (scalar @path > 0) {
|
||||
}
|
||||
|
||||
|
||||
# Make sure that the hash declared in the manifest matches what we
|
||||
# downloaded and unpacked.
|
||||
|
||||
if (defined $finalNarHash) {
|
||||
my ($hashAlgo, $hash) = parseHash $finalNarHash;
|
||||
|
||||
# The hash in the manifest can be either in base-16 or base-32.
|
||||
# Handle both.
|
||||
my $hash2 = hashPath($hashAlgo, $hashAlgo eq "sha256" && length($hash) != 64, $targetPath);
|
||||
|
||||
die "hash mismatch in downloaded path $targetPath; expected $hash, got $hash2\n"
|
||||
if $hash ne $hash2;
|
||||
} else {
|
||||
die "cannot check integrity of the downloaded path since its hash is not known\n";
|
||||
}
|
||||
# Tell Nix about the expected hash so it can verify it.
|
||||
die "cannot check integrity of the downloaded path since its hash is not known\n"
|
||||
unless defined $finalNarHash;
|
||||
print "$finalNarHash\n";
|
||||
|
||||
|
||||
print STDERR "\n";
|
||||
|
||||
@@ -1,22 +1,22 @@
|
||||
#! @perl@ -w
|
||||
#! @perl@ -w @perlFlags@
|
||||
|
||||
use strict;
|
||||
|
||||
my $procDir = "/proc";
|
||||
use Nix::Utils;
|
||||
use Nix::Config;
|
||||
|
||||
|
||||
sub readProc {
|
||||
return unless -d $procDir;
|
||||
return unless -d "/proc";
|
||||
|
||||
opendir DIR, "/proc" or return;
|
||||
|
||||
opendir DIR, $procDir or return;
|
||||
|
||||
foreach my $name (readdir DIR) {
|
||||
next unless $name =~ /^\d+$/;
|
||||
|
||||
my $process = "$procDir/$name";
|
||||
my $process = "/proc/$name";
|
||||
|
||||
#print STDERR "=== $process\n";
|
||||
|
||||
|
||||
my $target;
|
||||
print "$target\n" if $target = readlink "$process/exe";
|
||||
print "$target\n" if $target = readlink "$process/cwd";
|
||||
@@ -36,6 +36,13 @@ sub readProc {
|
||||
}
|
||||
close MAP;
|
||||
}
|
||||
|
||||
# Get all store paths that appear in the environment of this process.
|
||||
eval {
|
||||
my $env = Nix::Utils::readFile "$process/environ";
|
||||
my @matches = $env =~ /\Q$Nix::Config::storeDir\E\/[0-9a-z]+[0-9a-zA-Z\+\-\._\?=]*/g;
|
||||
print "$_\n" foreach @matches;
|
||||
}
|
||||
}
|
||||
|
||||
closedir DIR;
|
||||
@@ -58,18 +65,15 @@ readProc;
|
||||
lsof;
|
||||
|
||||
|
||||
sub readFile {
|
||||
my $path = shift;
|
||||
if (-e $path) {
|
||||
if (open FILE, "$path") {
|
||||
while (<FILE>) {
|
||||
print;
|
||||
}
|
||||
close FILE;
|
||||
}
|
||||
sub printFile {
|
||||
my ($fn) = @_;
|
||||
if (-e $fn) {
|
||||
print Nix::Utils::readFile($fn), "\n";
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
# This is rather NixOS-specific, so it probably shouldn't be here.
|
||||
readFile "/proc/sys/kernel/modprobe";
|
||||
readFile "/proc/sys/kernel/fbsplash";
|
||||
printFile "/proc/sys/kernel/modprobe";
|
||||
printFile "/proc/sys/kernel/fbsplash";
|
||||
printFile "/proc/sys/kernel/poweroff_cmd";
|
||||
|
||||
+89
-50
@@ -3,23 +3,27 @@
|
||||
use strict;
|
||||
use Nix::Config;
|
||||
use Nix::Store;
|
||||
use Nix::Utils;
|
||||
use File::Temp qw(tempdir);
|
||||
|
||||
|
||||
my $dryRun = 0;
|
||||
my $verbose = 0;
|
||||
my $runEnv = 0;
|
||||
my $runEnv = $0 =~ /nix-shell$/;
|
||||
my $pure = 0;
|
||||
|
||||
my @instArgs = ();
|
||||
my @buildArgs = ();
|
||||
my @exprs = ();
|
||||
|
||||
my $shell = $ENV{SHELL} || "/bin/sh";
|
||||
my $envCommand = "p=\$PATH; source \$stdenv/setup; PATH=\$PATH:\$p; exec $shell";
|
||||
my $envCommand = ""; # interactive shell
|
||||
my @envExclude = ();
|
||||
|
||||
my $myName = $runEnv ? "nix-shell" : "nix-build";
|
||||
|
||||
my $tmpDir = tempdir("nix-build.XXXXXX", CLEANUP => 1, TMPDIR => 1)
|
||||
|
||||
my $tmpDir = tempdir("$myName.XXXXXX", CLEANUP => 1, TMPDIR => 1)
|
||||
or die "cannot create a temporary directory";
|
||||
|
||||
my $outLink = "./result";
|
||||
@@ -33,29 +37,12 @@ for (my $n = 0; $n < scalar @ARGV; $n++) {
|
||||
my $arg = $ARGV[$n];
|
||||
|
||||
if ($arg eq "--help") {
|
||||
print STDERR <<EOF;
|
||||
Usage: nix-build [OPTION]... [FILE]...
|
||||
exec "man $myName" or die;
|
||||
}
|
||||
|
||||
`nix-build' builds the given Nix expressions (which
|
||||
default to ./default.nix if none are given). A symlink called
|
||||
`result' is placed in the current directory.
|
||||
|
||||
Flags:
|
||||
--add-drv-link: create a symlink `derivation' to the store derivation
|
||||
--drv-link NAME: create symlink NAME instead of `derivation'
|
||||
--no-out-link: do not create the `result' symlink
|
||||
--out-link / -o NAME: create symlink NAME instead of `result'
|
||||
--attr / -A ATTR: select a specific attribute from the Nix expression
|
||||
|
||||
--run-env: build dependencies of the specified derivation, then start a
|
||||
shell with the environment of the derivation
|
||||
--command: command to run with `--run-env'
|
||||
--exclude: regexp specifying dependencies to be excluded by `--run-env'
|
||||
|
||||
Any additional flags are passed to `nix-store'.
|
||||
EOF
|
||||
elsif ($arg eq "--version") {
|
||||
print "$myName (Nix) $Nix::Config::version\n";
|
||||
exit 0;
|
||||
# '` hack
|
||||
}
|
||||
|
||||
elsif ($arg eq "--add-drv-link") {
|
||||
@@ -104,7 +91,7 @@ EOF
|
||||
$n += 2;
|
||||
}
|
||||
|
||||
elsif ($arg eq "--max-jobs" or $arg eq "-j" or $arg eq "--max-silent-time" or $arg eq "--log-type" or $arg eq "--cores") {
|
||||
elsif ($arg eq "--max-jobs" or $arg eq "-j" or $arg eq "--max-silent-time" or $arg eq "--log-type" or $arg eq "--cores" or $arg eq "--timeout") {
|
||||
$n++;
|
||||
die "$0: `$arg' requires an argument\n" unless $n < scalar @ARGV;
|
||||
push @buildArgs, ($arg, $ARGV[$n]);
|
||||
@@ -114,30 +101,34 @@ EOF
|
||||
push @buildArgs, "--dry-run";
|
||||
$dryRun = 1;
|
||||
}
|
||||
|
||||
|
||||
elsif ($arg eq "--show-trace") {
|
||||
push @instArgs, $arg;
|
||||
}
|
||||
|
||||
|
||||
elsif ($arg eq "-") {
|
||||
@exprs = ("-");
|
||||
}
|
||||
|
||||
elsif ($arg eq "--verbose" or substr($arg, 0, 2) eq "-v") {
|
||||
push @buildArgs, $arg;
|
||||
push @instArgs, $arg;
|
||||
$verbose = 1;
|
||||
}
|
||||
|
||||
elsif ($arg eq "--quiet") {
|
||||
|
||||
elsif ($arg eq "--quiet" || $arg eq "--repair") {
|
||||
push @buildArgs, $arg;
|
||||
push @instArgs, $arg;
|
||||
}
|
||||
|
||||
elsif ($arg eq "--run-env") {
|
||||
|
||||
elsif ($arg eq "--run-env") { # obsolete
|
||||
$runEnv = 1;
|
||||
}
|
||||
|
||||
|
||||
elsif ($arg eq "--command") {
|
||||
$n++;
|
||||
die "$0: `$arg' requires an argument\n" unless $n < scalar @ARGV;
|
||||
$envCommand = $ARGV[$n];
|
||||
$envCommand = "$ARGV[$n]\nexit $!";
|
||||
}
|
||||
|
||||
elsif ($arg eq "--exclude") {
|
||||
@@ -146,6 +137,10 @@ EOF
|
||||
push @envExclude, $ARGV[$n];
|
||||
}
|
||||
|
||||
elsif ($arg eq "--pure") {
|
||||
$pure = 1;
|
||||
}
|
||||
|
||||
elsif (substr($arg, 0, 1) eq "-") {
|
||||
push @buildArgs, $arg;
|
||||
}
|
||||
@@ -162,17 +157,22 @@ foreach my $expr (@exprs) {
|
||||
|
||||
# Instantiate.
|
||||
my @drvPaths;
|
||||
# !!! would prefer the perl 5.8.0 pipe open feature here.
|
||||
my $pid = open(DRVPATHS, "-|") || exec "$Nix::Config::binDir/nix-instantiate", "--add-root", $drvLink, "--indirect", @instArgs, $expr;
|
||||
while (<DRVPATHS>) {chomp; push @drvPaths, $_;}
|
||||
if (!close DRVPATHS) {
|
||||
die "nix-instantiate killed by signal " . ($? & 127) . "\n" if ($? & 127);
|
||||
exit 1;
|
||||
if ($expr !~ /^\/.*\.drv$/) {
|
||||
# !!! would prefer the perl 5.8.0 pipe open feature here.
|
||||
my $pid = open(DRVPATHS, "-|") || exec "$Nix::Config::binDir/nix-instantiate", "--add-root", $drvLink, "--indirect", @instArgs, $expr;
|
||||
while (<DRVPATHS>) {chomp; push @drvPaths, $_;}
|
||||
if (!close DRVPATHS) {
|
||||
die "nix-instantiate killed by signal " . ($? & 127) . "\n" if ($? & 127);
|
||||
exit 1;
|
||||
}
|
||||
} else {
|
||||
push @drvPaths, $expr;
|
||||
}
|
||||
|
||||
if ($runEnv) {
|
||||
die "$0: ‘--run-env’ requires a single derivation\n" if scalar @drvPaths != 1;
|
||||
my $drvPath = readlink $drvPaths[0] or die "cannot read symlink `$drvPaths[0]'";
|
||||
die "$0: a single derivation is required\n" if scalar @drvPaths != 1;
|
||||
my $drvPath = $drvPaths[0];
|
||||
$drvPath = readlink $drvPath or die "cannot read symlink `$drvPath'" if -l $drvPath;
|
||||
my $drv = derivationFromPath($drvPath);
|
||||
|
||||
# Build or fetch all dependencies of the derivation.
|
||||
@@ -181,28 +181,67 @@ foreach my $expr (@exprs) {
|
||||
or die "$0: failed to build all dependencies\n";
|
||||
|
||||
# Set the environment.
|
||||
$ENV{'NIX_BUILD_TOP'} = $ENV{'TMPDIR'} || "/tmp";
|
||||
foreach (keys %{$drv->{env}}) {
|
||||
$ENV{$_} = $drv->{env}->{$_};
|
||||
if ($pure) {
|
||||
foreach my $name (keys %ENV) {
|
||||
next if $name eq "HOME" || $name eq "USER" || $name eq "LOGNAME" || $name eq "DISPLAY" || $name eq "PATH";
|
||||
delete $ENV{$name};
|
||||
}
|
||||
# NixOS hack: prevent /etc/bashrc from sourcing /etc/profile.
|
||||
$ENV{'__ETC_PROFILE_SOURCED'} = 1;
|
||||
}
|
||||
$ENV{'NIX_BUILD_TOP'} = $ENV{'TMPDIR'} = $ENV{'TEMPDIR'} = $ENV{'TMP'} = $ENV{'TEMP'} = $ENV{'TMPDIR'} // "/tmp";
|
||||
$ENV{'NIX_STORE'} = $Nix::Config::storeDir;
|
||||
$ENV{$_} = $drv->{env}->{$_} foreach keys %{$drv->{env}};
|
||||
|
||||
$ENV{'IN_NIX_SHELL'} = 1;
|
||||
|
||||
# Run a shell using the derivation's environment. For
|
||||
# convenience, source $stdenv/setup to setup additional
|
||||
# environment variables. Also don't lose the current $PATH
|
||||
# directories.
|
||||
exec($ENV{SHELL}, "-c", $envCommand);
|
||||
# environment variables and shell functions. Also don't lose
|
||||
# the current $PATH directories.
|
||||
my $rcfile = "$tmpDir/rc";
|
||||
writeFile(
|
||||
$rcfile,
|
||||
'[ -e ~/.bashrc ] && source ~/.bashrc; ' .
|
||||
($pure ? '' : 'p=$PATH; ' ) .
|
||||
'dontAddDisableDepTrack=1; ' .
|
||||
'[ -e $stdenv/setup ] && source $stdenv/setup; ' .
|
||||
($pure ? '' : 'PATH=$PATH:$p; ') .
|
||||
'set +e; ' .
|
||||
'PS1="\n\[\033[1;32m\][nix-shell:\w]$\[\033[0m\] "; ' .
|
||||
'unset NIX_ENFORCE_PURITY; ' .
|
||||
$envCommand);
|
||||
exec($ENV{NIX_BUILD_SHELL} // "bash", "--rcfile", $rcfile);
|
||||
die;
|
||||
}
|
||||
|
||||
# Ugly hackery to make "nix-build -A foo.all" produce symlinks
|
||||
# ./result, ./result-dev, and so on, rather than ./result,
|
||||
# ./result-2-dev, and so on. This combines multiple derivation
|
||||
# paths into one "/nix/store/drv-path!out1,out2,..." argument.
|
||||
my $prevDrvPath = "";
|
||||
my @drvPaths2;
|
||||
foreach my $drvPath (@drvPaths) {
|
||||
my $target = readlink $drvPath or die "cannot read symlink `$drvPath'";
|
||||
my $p = $drvPath; my $output = "out";
|
||||
if ($drvPath =~ /(.*)!(.*)/) {
|
||||
$p = $1; $output = $2;
|
||||
} else {
|
||||
$p = $drvPath;
|
||||
}
|
||||
my $target = readlink $p or die "cannot read symlink `$p'";
|
||||
print STDERR "derivation is $target\n" if $verbose;
|
||||
if ($target eq $prevDrvPath) {
|
||||
push @drvPaths2, (pop @drvPaths2) . "," . $output;
|
||||
} else {
|
||||
push @drvPaths2, $target . "!" . $output;
|
||||
$prevDrvPath = $target;
|
||||
}
|
||||
}
|
||||
|
||||
# Build.
|
||||
my @outPaths;
|
||||
$pid = open(OUTPATHS, "-|") || exec "$Nix::Config::binDir/nix-store", "--add-root", $outLink, "--indirect", "-r",
|
||||
@buildArgs, @drvPaths;
|
||||
my $pid = open(OUTPATHS, "-|") || exec "$Nix::Config::binDir/nix-store", "--add-root", $outLink, "--indirect", "-r",
|
||||
@buildArgs, @drvPaths2;
|
||||
while (<OUTPATHS>) {chomp; push @outPaths, $_;}
|
||||
if (!close OUTPATHS) {
|
||||
die "nix-store killed by signal " . ($? & 127) . "\n" if ($? & 127);
|
||||
|
||||
+46
-37
@@ -4,6 +4,9 @@ use strict;
|
||||
use File::Basename;
|
||||
use File::Path qw(mkpath);
|
||||
use Nix::Config;
|
||||
use Nix::Manifest;
|
||||
|
||||
Nix::Config::readConfig;
|
||||
|
||||
my $manifestDir = $Nix::Config::manifestDir;
|
||||
|
||||
@@ -22,7 +25,7 @@ my $nixDefExpr = "$home/.nix-defexpr";
|
||||
my $userName = getpwuid($<) or die "cannot figure out user name";
|
||||
my $profile = "$Nix::Config::stateDir/profiles/per-user/$userName/channels";
|
||||
mkpath(dirname $profile, 0, 0755);
|
||||
|
||||
|
||||
my %channels;
|
||||
|
||||
|
||||
@@ -65,11 +68,13 @@ sub addChannel {
|
||||
sub removeChannel {
|
||||
my ($name) = @_;
|
||||
readChannels;
|
||||
my $url = $channels{$name};
|
||||
deleteOldManifests($url . "/MANIFEST", undef) if defined $url;
|
||||
delete $channels{$name};
|
||||
writeChannels;
|
||||
|
||||
system("$Nix::Config::binDir/nix-env --profile '$profile' -e '$name'") == 0
|
||||
or die "cannot remove channel `$name'";
|
||||
or die "cannot remove channel `$name'\n";
|
||||
}
|
||||
|
||||
|
||||
@@ -77,20 +82,14 @@ sub removeChannel {
|
||||
# channels.
|
||||
sub update {
|
||||
my @channelNames = @_;
|
||||
|
||||
|
||||
readChannels;
|
||||
|
||||
# Create the manifests directory if it doesn't exist.
|
||||
mkdir $manifestDir, 0755 unless -e $manifestDir;
|
||||
|
||||
# Do we have write permission to the manifests directory?
|
||||
die "$0: you do not have write permission to `$manifestDir'!\n" unless -W $manifestDir;
|
||||
|
||||
# Download each channel.
|
||||
my $exprs = "";
|
||||
foreach my $name (keys %channels) {
|
||||
next if scalar @channelNames > 0 && ! grep { $_ eq $name } @{channelNames};
|
||||
|
||||
|
||||
my $url = $channels{$name};
|
||||
my $origUrl = "$url/MANIFEST";
|
||||
|
||||
@@ -101,17 +100,34 @@ sub update {
|
||||
die "$0: unable to check `$url'\n" if $? != 0;
|
||||
$headers =~ s/\r//g;
|
||||
$url = $1 if $headers =~ /^Location:\s*(.*)\s*$/m;
|
||||
|
||||
# Pull the channel manifest.
|
||||
$ENV{'NIX_ORIG_URL'} = $origUrl;
|
||||
system("$Nix::Config::binDir/nix-pull", "--skip-wrong-store", "$url/MANIFEST") == 0
|
||||
or die "cannot pull manifest from `$url'\n";
|
||||
|
||||
# Check if the channel advertises a binary cache.
|
||||
my $binaryCacheURL = `$Nix::Config::curl --silent '$url'/binary-cache-url`;
|
||||
my $extraAttrs = "";
|
||||
my $getManifest = ($Nix::Config::config{"force-manifest"} // "false") eq "true";
|
||||
if ($? == 0 && $binaryCacheURL ne "") {
|
||||
$extraAttrs .= "binaryCacheURL = \"$binaryCacheURL\"; ";
|
||||
deleteOldManifests($origUrl, undef);
|
||||
} else {
|
||||
$getManifest = 1;
|
||||
}
|
||||
|
||||
if ($getManifest) {
|
||||
# No binary cache, so pull the channel manifest.
|
||||
mkdir $manifestDir, 0755 unless -e $manifestDir;
|
||||
die "$0: you do not have write permission to `$manifestDir'!\n" unless -W $manifestDir;
|
||||
$ENV{'NIX_ORIG_URL'} = $origUrl;
|
||||
system("$Nix::Config::binDir/nix-pull", "--skip-wrong-store", "$url/MANIFEST") == 0
|
||||
or die "cannot pull manifest from `$url'\n";
|
||||
}
|
||||
|
||||
# Download the channel tarball.
|
||||
my $fullURL = "$url/nixexprs.tar.bz2";
|
||||
my $fullURL = "$url/nixexprs.tar.xz";
|
||||
system("$Nix::Config::curl --fail --silent --head '$fullURL' > /dev/null") == 0 or
|
||||
$fullURL = "$url/nixexprs.tar.bz2";
|
||||
print STDERR "downloading Nix expressions from `$fullURL'...\n";
|
||||
my ($hash, $path) = `PRINT_PATH=1 QUIET=1 $Nix::Config::binDir/nix-prefetch-url '$fullURL'`;
|
||||
die "cannot fetch `$fullURL'" if $? != 0;
|
||||
die "cannot fetch `$fullURL'\n" if $? != 0;
|
||||
chomp $path;
|
||||
|
||||
# If the URL contains a version number, append it to the name
|
||||
@@ -120,7 +136,7 @@ sub update {
|
||||
my $cname = $name;
|
||||
$cname .= $1 if basename($url) =~ /(-\d.*)$/;
|
||||
|
||||
$exprs .= "'f: f { name = \"$cname\"; channelName = \"$name\"; src = builtins.storePath \"$path\"; }' ";
|
||||
$exprs .= "'f: f { name = \"$cname\"; channelName = \"$name\"; src = builtins.storePath \"$path\"; $extraAttrs }' ";
|
||||
}
|
||||
|
||||
# Unpack the channel tarballs into the Nix store and install them
|
||||
@@ -139,26 +155,14 @@ sub update {
|
||||
}
|
||||
|
||||
|
||||
sub usageError {
|
||||
print STDERR <<EOF;
|
||||
Usage:
|
||||
nix-channel --add URL [CHANNEL-NAME]
|
||||
nix-channel --remove CHANNEL-NAME
|
||||
nix-channel --list
|
||||
nix-channel --update [CHANNEL-NAME...]
|
||||
EOF
|
||||
exit 1;
|
||||
}
|
||||
|
||||
|
||||
usageError if scalar @ARGV == 0;
|
||||
die "$0: argument expected\n" if scalar @ARGV == 0;
|
||||
|
||||
|
||||
while (scalar @ARGV) {
|
||||
my $arg = shift @ARGV;
|
||||
|
||||
if ($arg eq "--add") {
|
||||
usageError if scalar @ARGV < 1 || scalar @ARGV > 2;
|
||||
die "$0: `--add' requires one or two arguments\n" if scalar @ARGV < 1 || scalar @ARGV > 2;
|
||||
my $url = shift @ARGV;
|
||||
my $name = shift @ARGV;
|
||||
unless (defined $name) {
|
||||
@@ -171,13 +175,13 @@ while (scalar @ARGV) {
|
||||
}
|
||||
|
||||
if ($arg eq "--remove") {
|
||||
usageError if scalar @ARGV != 1;
|
||||
die "$0: `--remove' requires one argument\n" if scalar @ARGV != 1;
|
||||
removeChannel(shift @ARGV);
|
||||
last;
|
||||
}
|
||||
|
||||
if ($arg eq "--list") {
|
||||
usageError if scalar @ARGV != 0;
|
||||
die "$0: `--list' requires one argument\n" if scalar @ARGV != 0;
|
||||
readChannels;
|
||||
foreach my $name (keys %channels) {
|
||||
print "$name $channels{$name}\n";
|
||||
@@ -189,12 +193,17 @@ while (scalar @ARGV) {
|
||||
update(@ARGV);
|
||||
last;
|
||||
}
|
||||
|
||||
|
||||
elsif ($arg eq "--help") {
|
||||
usageError;
|
||||
exec "man nix-channel" or die;
|
||||
}
|
||||
|
||||
elsif ($arg eq "--version") {
|
||||
print "nix-channel (Nix) $Nix::Config::version\n";
|
||||
exit 0;
|
||||
}
|
||||
|
||||
else {
|
||||
die "unknown argument `$arg'; try `--help'";
|
||||
die "unknown argument `$arg'; try `--help'\n";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,10 +9,15 @@ my $profilesDir = "@localstatedir@/nix/profiles";
|
||||
# Process the command line arguments.
|
||||
my @args = ();
|
||||
my $removeOld = 0;
|
||||
my $dryRun = 0;
|
||||
|
||||
for my $arg (@ARGV) {
|
||||
if ($arg eq "--delete-old" || $arg eq "-d") {
|
||||
$removeOld = 1;
|
||||
} elsif ($arg eq "--dry-run") {
|
||||
$dryRun = 1;
|
||||
} elsif ($arg eq "--help") {
|
||||
exec "man nix-collect-garbage" or die;
|
||||
} else {
|
||||
push @args, $arg;
|
||||
}
|
||||
@@ -35,13 +40,13 @@ sub removeOldGenerations {
|
||||
$name = $dir . "/" . $name;
|
||||
if (-l $name && (readlink($name) =~ /link/)) {
|
||||
print STDERR "removing old generations of profile $name\n";
|
||||
system("$Nix::Config::binDir/nix-env", "-p", $name, "--delete-generations", "old");
|
||||
system("$Nix::Config::binDir/nix-env", "-p", $name, "--delete-generations", "old", $dryRun ? "--dry-run" : ());
|
||||
}
|
||||
elsif (! -l $name && -d $name) {
|
||||
removeOldGenerations $name;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
closedir $dh or die;
|
||||
}
|
||||
|
||||
@@ -49,4 +54,4 @@ removeOldGenerations $profilesDir if $removeOld;
|
||||
|
||||
|
||||
# Run the actual garbage collector.
|
||||
exec "$Nix::Config::binDir/nix-store", "--gc", @args;
|
||||
exec "$Nix::Config::binDir/nix-store", "--gc", @args unless $dryRun;
|
||||
|
||||
+28
-14
@@ -4,6 +4,7 @@ use Nix::SSH;
|
||||
use Nix::Config;
|
||||
use Nix::Store;
|
||||
use Nix::CopyClosure;
|
||||
use List::Util qw(sum);
|
||||
|
||||
|
||||
if (scalar @ARGV < 1) {
|
||||
@@ -17,19 +18,14 @@ EOF
|
||||
|
||||
# Get the target host.
|
||||
my $sshHost;
|
||||
|
||||
my $sign = 0;
|
||||
|
||||
my $compressor = "";
|
||||
my $decompressor = "";
|
||||
|
||||
my $progressViewer = "";
|
||||
|
||||
my $toMode = 1;
|
||||
|
||||
my $includeOutputs = 0;
|
||||
|
||||
my $dryRun = 0;
|
||||
my $useSubstitutes = 0;
|
||||
|
||||
|
||||
# !!! Copied from nix-pack-closure, should put this in a module.
|
||||
@@ -37,8 +33,11 @@ my @storePaths = ();
|
||||
|
||||
while (@ARGV) {
|
||||
my $arg = shift @ARGV;
|
||||
|
||||
if ($arg eq "--sign") {
|
||||
|
||||
if ($arg eq "--help") {
|
||||
exec "man nix-copy-closure" or die;
|
||||
}
|
||||
elsif ($arg eq "--sign") {
|
||||
$sign = 1;
|
||||
}
|
||||
elsif ($arg eq "--gzip") {
|
||||
@@ -68,6 +67,9 @@ while (@ARGV) {
|
||||
elsif ($arg eq "--dry-run") {
|
||||
$dryRun = 1;
|
||||
}
|
||||
elsif ($arg eq "--use-substitutes" || $arg eq "-s") {
|
||||
$useSubstitutes = 1;
|
||||
}
|
||||
elsif (!defined $sshHost) {
|
||||
$sshHost = $arg;
|
||||
}
|
||||
@@ -76,12 +78,16 @@ while (@ARGV) {
|
||||
}
|
||||
}
|
||||
|
||||
die "$0: you did not specify a host name\n" unless defined $sshHost;
|
||||
|
||||
|
||||
openSSHConnection $sshHost or die "$0: unable to start SSH\n";
|
||||
|
||||
|
||||
if ($toMode) { # Copy TO the remote machine.
|
||||
Nix::CopyClosure::copyTo($sshHost, [ @sshOpts ], [ @storePaths ], $compressor, $decompressor, $includeOutputs, $dryRun, $sign, $progressViewer);
|
||||
Nix::CopyClosure::copyTo(
|
||||
$sshHost, [ @sshOpts ], [ @storePaths ], $compressor, $decompressor,
|
||||
$includeOutputs, $dryRun, $sign, $progressViewer, $useSubstitutes);
|
||||
}
|
||||
|
||||
else { # Copy FROM the remote machine.
|
||||
@@ -92,7 +98,7 @@ else { # Copy FROM the remote machine.
|
||||
my $extraOpts = $includeOutputs ? "--include-outputs" : "";
|
||||
my $pid = open(READ,
|
||||
"set -f; ssh @sshOpts $sshHost nix-store --query --requisites $extraOpts @storePaths|") or die;
|
||||
|
||||
|
||||
while (<READ>) {
|
||||
chomp;
|
||||
die "bad: $_" unless /^\//;
|
||||
@@ -101,15 +107,23 @@ else { # Copy FROM the remote machine.
|
||||
|
||||
close READ or die "nix-store on remote machine `$sshHost' failed: $?";
|
||||
|
||||
my $missingSize = 0;
|
||||
if ($progressViewer ne "") {
|
||||
$missingSize = sum (split ' ', `set -f; ssh @sshOpts $sshHost nix-store -q --size @missing`) or die;
|
||||
}
|
||||
|
||||
# Export the store paths on the remote machine and import them locally.
|
||||
if (scalar @missing > 0) {
|
||||
print STDERR "copying ", scalar @missing, " missing paths from ‘$sshHost’...\n";
|
||||
$compressor = "| $compressor" if $compressor ne "";
|
||||
$decompressor = "$decompressor |" if $decompressor ne "";
|
||||
$progressViewer = "$progressViewer |" if $progressViewer ne "";
|
||||
unless ($dryRun) {
|
||||
if ($useSubstitutes) {
|
||||
system "$Nix::Config::binDir/nix-store -r --ignore-unknown @missing";
|
||||
}
|
||||
$compressor = "| $compressor" if $compressor ne "";
|
||||
$decompressor = "$decompressor |" if $decompressor ne "";
|
||||
$progressViewer = "$progressViewer -s $missingSize |" if $progressViewer ne "";
|
||||
my $extraOpts = $sign ? "--sign" : "";
|
||||
system("set -f; ssh $sshHost @sshOpts 'nix-store --export $extraOpts @missing $compressor' | $progressViewer $decompressor $Nix::Config::binDir/nix-store --import > /dev/null") == 0
|
||||
system("set -f; ssh $sshHost @sshOpts 'nix-store --export $extraOpts @missing $compressor' | $decompressor $progressViewer $Nix::Config::binDir/nix-store --import > /dev/null") == 0
|
||||
or die "copying store paths from remote machine `$sshHost' failed: $?";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,27 +3,11 @@
|
||||
use strict;
|
||||
use File::Temp qw(tempdir);
|
||||
use Nix::Config;
|
||||
|
||||
|
||||
sub usageError {
|
||||
print STDERR <<EOF;
|
||||
Usage: nix-install-package (FILE | --url URL)
|
||||
|
||||
Install a Nix Package (.nixpkg) either directly from FILE or by
|
||||
downloading it from URL.
|
||||
|
||||
Flags:
|
||||
--profile / -p LINK: install into the specified profile
|
||||
--non-interactive: don't run inside a new terminal
|
||||
EOF
|
||||
; # '
|
||||
exit 1;
|
||||
}
|
||||
use Nix::Utils;
|
||||
|
||||
|
||||
# Parse the command line arguments.
|
||||
my @args = @ARGV;
|
||||
usageError if scalar @args == 0;
|
||||
|
||||
my $source;
|
||||
my $fromURL = 0;
|
||||
@@ -33,14 +17,14 @@ my $interactive = 1;
|
||||
while (scalar @args) {
|
||||
my $arg = shift @args;
|
||||
if ($arg eq "--help") {
|
||||
usageError;
|
||||
exec "man nix-install-package" or die;
|
||||
}
|
||||
elsif ($arg eq "--url") {
|
||||
$fromURL = 1;
|
||||
}
|
||||
elsif ($arg eq "--profile" || $arg eq "-p") {
|
||||
my $profile = shift @args;
|
||||
usageError if !defined $profile;
|
||||
die "$0: `--profile' requires an argument\n" if !defined $profile;
|
||||
push @extraNixEnvArgs, "-p", $profile;
|
||||
}
|
||||
elsif ($arg eq "--non-interactive") {
|
||||
@@ -51,7 +35,7 @@ while (scalar @args) {
|
||||
}
|
||||
}
|
||||
|
||||
usageError unless defined $source;
|
||||
die "$0: please specify a .nixpkg file or URL\n" unless defined $source;
|
||||
|
||||
|
||||
# Re-execute in a terminal, if necessary, so that if we're executed
|
||||
@@ -72,7 +56,7 @@ my $tmpDir = tempdir("nix-install-package.XXXXXX", CLEANUP => 1, TMPDIR => 1)
|
||||
|
||||
sub barf {
|
||||
my $msg = shift;
|
||||
print "$msg\n";
|
||||
print "\nInstallation failed: $msg\n";
|
||||
<STDIN> if $interactive;
|
||||
exit 1;
|
||||
}
|
||||
@@ -92,7 +76,6 @@ open PKGFILE, "<$pkgFile" or barf "cannot open `$pkgFile': $!";
|
||||
my $contents = <PKGFILE>;
|
||||
close PKGFILE;
|
||||
|
||||
my $urlRE = "(?: [a-zA-Z][a-zA-Z0-9\+\-\.]*\:[a-zA-Z0-9\%\/\?\:\@\&\=\+\$\,\-\_\.\!\~\*\']+ )";
|
||||
my $nameRE = "(?: [A-Za-z0-9\+\-\.\_\?\=]+ )"; # see checkStoreName()
|
||||
my $systemRE = "(?: [A-Za-z0-9\+\-\_]+ )";
|
||||
my $pathRE = "(?: \/ [\/A-Za-z0-9\+\-\.\_\?\=]* )";
|
||||
@@ -101,7 +84,7 @@ my $pathRE = "(?: \/ [\/A-Za-z0-9\+\-\.\_\?\=]* )";
|
||||
# store path. We'll let nix-env do that.
|
||||
|
||||
$contents =~
|
||||
/ ^ \s* (\S+) \s+ ($urlRE) \s+ ($nameRE) \s+ ($systemRE) \s+ ($pathRE) \s+ ($pathRE) /x
|
||||
/ ^ \s* (\S+) \s+ ($Nix::Utils::urlRE) \s+ ($nameRE) \s+ ($systemRE) \s+ ($pathRE) \s+ ($pathRE) ( \s+ ($Nix::Utils::urlRE) )? /x
|
||||
or barf "invalid package contents";
|
||||
my $version = $1;
|
||||
my $manifestURL = $2;
|
||||
@@ -109,6 +92,7 @@ my $drvName = $3;
|
||||
my $system = $4;
|
||||
my $drvPath = $5;
|
||||
my $outPath = $6;
|
||||
my $binaryCacheURL = $8;
|
||||
|
||||
barf "invalid package version `$version'" unless $version eq "NIXPKG1";
|
||||
|
||||
@@ -122,17 +106,25 @@ if ($interactive) {
|
||||
}
|
||||
|
||||
|
||||
# Store the manifest in the temporary directory so that we don't
|
||||
# pollute /nix/var/nix/manifests. This also requires that we don't
|
||||
# use the Nix daemon (because otherwise download-using-manifests won't
|
||||
# see our NIX_MANIFESTS_DIRS environment variable).
|
||||
$ENV{NIX_MANIFESTS_DIR} = $tmpDir;
|
||||
$ENV{NIX_REMOTE} = "";
|
||||
if (defined $binaryCacheURL) {
|
||||
|
||||
push @extraNixEnvArgs, "--option", "binary-caches", $binaryCacheURL;
|
||||
|
||||
print "\nPulling manifests...\n";
|
||||
system("$Nix::Config::binDir/nix-pull", $manifestURL) == 0
|
||||
or barf "nix-pull failed: $?";
|
||||
} else {
|
||||
|
||||
# Store the manifest in the temporary directory so that we don't
|
||||
# pollute /nix/var/nix/manifests. This also requires that we
|
||||
# don't use the Nix daemon (because otherwise
|
||||
# download-using-manifests won't see our NIX_MANIFESTS_DIRS
|
||||
# environment variable).
|
||||
$ENV{NIX_MANIFESTS_DIR} = $tmpDir;
|
||||
$ENV{NIX_REMOTE} = "";
|
||||
|
||||
print "\nPulling manifests...\n";
|
||||
system("$Nix::Config::binDir/nix-pull", $manifestURL) == 0
|
||||
or barf "nix-pull failed: $?";
|
||||
|
||||
}
|
||||
|
||||
|
||||
print "\nInstalling package...\n";
|
||||
|
||||
+21
-19
@@ -6,12 +6,30 @@ use File::Temp qw(tempdir);
|
||||
use File::stat;
|
||||
use Nix::Store;
|
||||
use Nix::Config;
|
||||
use Nix::Utils;
|
||||
|
||||
my $url = shift;
|
||||
my $expHash = shift;
|
||||
my $hashType = $ENV{'NIX_HASH_ALGO'} || "sha256";
|
||||
my $hashType = $ENV{'NIX_HASH_ALGO'} || "sha256"; # obsolete
|
||||
my $cacheDir = $ENV{'NIX_DOWNLOAD_CACHE'};
|
||||
|
||||
my @args;
|
||||
my $arg;
|
||||
while ($arg = shift) {
|
||||
if ($arg eq "--help") {
|
||||
exec "man nix-prefetch-url" or die;
|
||||
} elsif ($arg eq "--type") {
|
||||
$hashType = shift;
|
||||
die "$0: `$arg' requires an argument\n" unless defined $hashType;
|
||||
} elsif (substr($arg, 0, 1) eq "-") {
|
||||
die "$0: unknown flag `$arg'\n";
|
||||
} else {
|
||||
push @args, $arg;
|
||||
}
|
||||
}
|
||||
|
||||
my $url = $args[0];
|
||||
my $expHash = $args[1];
|
||||
|
||||
|
||||
if (!defined $url || $url eq "") {
|
||||
print STDERR <<EOF
|
||||
Usage: nix-prefetch-url URL [EXPECTED-HASH]
|
||||
@@ -20,22 +38,6 @@ EOF
|
||||
exit 1;
|
||||
}
|
||||
|
||||
sub writeFile {
|
||||
my ($fn, $s) = @_;
|
||||
open TMP, ">$fn" or die;
|
||||
print TMP "$s" or die;
|
||||
close TMP or die;
|
||||
}
|
||||
|
||||
sub readFile {
|
||||
local $/ = undef;
|
||||
my ($fn) = @_;
|
||||
open TMP, "<$fn" or die;
|
||||
my $s = <TMP>;
|
||||
close TMP or die;
|
||||
return $s;
|
||||
}
|
||||
|
||||
my $tmpDir = tempdir("nix-prefetch-url.XXXXXX", CLEANUP => 1, TMPDIR => 1)
|
||||
or die "cannot create a temporary directory";
|
||||
|
||||
|
||||
+11
-44
@@ -1,50 +1,17 @@
|
||||
export NIX_USER_PROFILE_DIR=@localstatedir@/nix/profiles/per-user/$USER
|
||||
export NIX_PROFILES="@localstatedir@/nix/profiles/default $HOME/.nix-profile"
|
||||
if test -n "$HOME"; then
|
||||
NIX_LINK="$HOME/.nix-profile"
|
||||
|
||||
# Set up the per-user profile.
|
||||
mkdir -m 0755 -p $NIX_USER_PROFILE_DIR
|
||||
if test "$(stat --printf '%u' $NIX_USER_PROFILE_DIR)" != "$(id -u)"; then
|
||||
echo "WARNING: bad ownership on $NIX_USER_PROFILE_DIR" >&2
|
||||
fi
|
||||
|
||||
if ! test -L $HOME/.nix-profile; then
|
||||
echo "creating $HOME/.nix-profile" >&2
|
||||
if test "$USER" != root; then
|
||||
@coreutils@/ln -s $NIX_USER_PROFILE_DIR/profile $HOME/.nix-profile
|
||||
else
|
||||
# Root installs in the system-wide profile by default.
|
||||
@coreutils@/ln -s /nix/var/nix/profiles/default $HOME/.nix-profile
|
||||
# Set the default profile.
|
||||
if ! [ -L "$NIX_LINK" ]; then
|
||||
echo "creating $NIX_LINK" >&2
|
||||
_NIX_DEF_LINK=@localstatedir@/nix/profiles/default
|
||||
@coreutils@/ln -s "$_NIX_DEF_LINK" "$NIX_LINK"
|
||||
fi
|
||||
fi
|
||||
|
||||
export PATH="$HOME/.nix-profile/bin:$PATH"
|
||||
|
||||
# Subscribe the root user to the NixOS channel by default.
|
||||
if [ "$USER" = root -a ! -e $HOME/.nix-channels ]; then
|
||||
echo "http://nixos.org/releases/nixos/channels/nixos-unstable nixos" > $HOME/.nix-channels
|
||||
fi
|
||||
|
||||
# Create the per-user garbage collector roots directory.
|
||||
NIX_USER_GCROOTS_DIR=@localstatedir@/nix/gcroots/per-user/$USER
|
||||
mkdir -m 0755 -p $NIX_USER_GCROOTS_DIR
|
||||
if test "$(stat --printf '%u' $NIX_USER_GCROOTS_DIR)" != "$(id -u)"; then
|
||||
echo "WARNING: bad ownership on $NIX_USER_GCROOTS_DIR" >&2
|
||||
fi
|
||||
|
||||
# Set up a default Nix expression from which to install stuff.
|
||||
if [ ! -e $HOME/.nix-defexpr -o -L $HOME/.nix-defexpr ]; then
|
||||
echo "creating $HOME/.nix-defexpr" >&2
|
||||
rm -f $HOME/.nix-defexpr
|
||||
mkdir $HOME/.nix-defexpr
|
||||
if [ "$USER" != root ]; then
|
||||
@coreutils@/ln -s @localstatedir@/nix/profiles/per-user/root/channels $HOME/.nix-defexpr/channels_root
|
||||
# Subscribe the root user to the Nixpkgs channel by default.
|
||||
if [ ! -e $HOME/.nix-channels ]; then
|
||||
echo "http://nixos.org/channels/nixpkgs-unstable nixpkgs" > $HOME/.nix-channels
|
||||
fi
|
||||
fi
|
||||
|
||||
# Set up secure multi-user builds: non-root users build through the
|
||||
# Nix daemon.
|
||||
if test "$USER" != root; then
|
||||
export NIX_REMOTE=daemon
|
||||
else
|
||||
unset NIX_REMOTE
|
||||
export PATH=$NIX_LINK/bin:$PATH
|
||||
fi
|
||||
|
||||
+6
-15
@@ -76,31 +76,22 @@ sub processURL {
|
||||
open URL, ">$urlFile" or die "cannot create `$urlFile'";
|
||||
print URL $origUrl;
|
||||
close URL;
|
||||
|
||||
|
||||
my $finalPath = "$manifestDir/$baseName-$hash.nixmanifest";
|
||||
|
||||
unlink $finalPath if -e $finalPath;
|
||||
|
||||
|
||||
symlink("$manifest", "$finalPath")
|
||||
or die "cannot link `$finalPath to `$manifest'";
|
||||
|
||||
# Delete all old manifests downloaded from this URL.
|
||||
for my $urlFile2 (glob "$manifestDir/*.url") {
|
||||
next if $urlFile eq $urlFile2;
|
||||
open URL, "<$urlFile2" or die;
|
||||
my $url2 = <URL>;
|
||||
chomp $url2;
|
||||
close URL;
|
||||
next unless $origUrl eq $url2;
|
||||
my $base = $urlFile2; $base =~ s/.url$//;
|
||||
unlink "${base}.url";
|
||||
unlink "${base}.nixmanifest";
|
||||
}
|
||||
deleteOldManifests($origUrl, $urlFile);
|
||||
}
|
||||
|
||||
while (@ARGV) {
|
||||
my $url = shift @ARGV;
|
||||
if ($url eq "--skip-wrong-store") {
|
||||
if ($url eq "--help") {
|
||||
exec "man nix-pull" or die;
|
||||
} elsif ($url eq "--skip-wrong-store") {
|
||||
# No-op, no longer supported.
|
||||
} else {
|
||||
processURL $url;
|
||||
|
||||
+189
-195
@@ -1,85 +1,85 @@
|
||||
#! @perl@ -w @perlFlags@
|
||||
|
||||
use strict;
|
||||
use File::Basename;
|
||||
use File::Temp qw(tempdir);
|
||||
use File::Path qw(mkpath);
|
||||
use File::stat;
|
||||
use File::Copy;
|
||||
use Nix::Config;
|
||||
use Nix::Store;
|
||||
use Nix::Manifest;
|
||||
|
||||
my $hashAlgo = "sha256";
|
||||
use Nix::Utils;
|
||||
|
||||
my $tmpDir = tempdir("nix-push.XXXXXX", CLEANUP => 1, TMPDIR => 1)
|
||||
or die "cannot create a temporary directory";
|
||||
|
||||
my $nixExpr = "$tmpDir/create-nars.nix";
|
||||
my $manifest = "$tmpDir/MANIFEST";
|
||||
|
||||
my $curl = "$Nix::Config::curl --fail --silent";
|
||||
my $extraCurlFlags = ${ENV{'CURL_FLAGS'}};
|
||||
$curl = "$curl $extraCurlFlags" if defined $extraCurlFlags;
|
||||
|
||||
|
||||
# Parse the command line.
|
||||
my $localCopy;
|
||||
my $localArchivesDir;
|
||||
my $localManifestFile;
|
||||
my $compressionType = "xz";
|
||||
my $force = 0;
|
||||
my $destDir;
|
||||
my $writeManifest = 0;
|
||||
my $manifestPath;
|
||||
my $archivesURL;
|
||||
my $link = 0;
|
||||
my @roots;
|
||||
|
||||
my $targetArchivesUrl;
|
||||
for (my $n = 0; $n < scalar @ARGV; $n++) {
|
||||
my $arg = $ARGV[$n];
|
||||
|
||||
my $archivesPutURL;
|
||||
my $archivesGetURL;
|
||||
my $manifestPutURL;
|
||||
|
||||
sub showSyntax {
|
||||
print STDERR <<EOF
|
||||
Usage: nix-push --copy ARCHIVES_DIR MANIFEST_FILE PATHS...
|
||||
or: nix-push ARCHIVES_PUT_URL ARCHIVES_GET_URL MANIFEST_PUT_URL PATHS...
|
||||
|
||||
`nix-push' copies or uploads the closure of PATHS to the given
|
||||
destination.
|
||||
EOF
|
||||
; # `
|
||||
exit 1;
|
||||
}
|
||||
|
||||
showSyntax if scalar @ARGV < 1;
|
||||
|
||||
if ($ARGV[0] eq "--copy") {
|
||||
showSyntax if scalar @ARGV < 3;
|
||||
$localCopy = 1;
|
||||
shift @ARGV;
|
||||
$localArchivesDir = shift @ARGV;
|
||||
$localManifestFile = shift @ARGV;
|
||||
if ($ARGV[0] eq "--target") {
|
||||
shift @ARGV;
|
||||
$targetArchivesUrl = shift @ARGV;
|
||||
}
|
||||
else {
|
||||
$targetArchivesUrl = "file://$localArchivesDir";
|
||||
if ($arg eq "--help") {
|
||||
exec "man nix-push" or die;
|
||||
} elsif ($arg eq "--bzip2") {
|
||||
$compressionType = "bzip2";
|
||||
} elsif ($arg eq "--none") {
|
||||
$compressionType = "none";
|
||||
} elsif ($arg eq "--force") {
|
||||
$force = 1;
|
||||
} elsif ($arg eq "--dest") {
|
||||
$n++;
|
||||
die "$0: `$arg' requires an argument\n" unless $n < scalar @ARGV;
|
||||
$destDir = $ARGV[$n];
|
||||
mkpath($destDir, 0, 0755);
|
||||
} elsif ($arg eq "--manifest") {
|
||||
$writeManifest = 1;
|
||||
} elsif ($arg eq "--manifest-path") {
|
||||
$n++;
|
||||
die "$0: `$arg' requires an argument\n" unless $n < scalar @ARGV;
|
||||
$manifestPath = $ARGV[$n];
|
||||
$writeManifest = 1;
|
||||
mkpath(dirname($manifestPath), 0, 0755);
|
||||
} elsif ($arg eq "--url-prefix") {
|
||||
$n++;
|
||||
die "$0: `$arg' requires an argument\n" unless $n < scalar @ARGV;
|
||||
$archivesURL = $ARGV[$n];
|
||||
} elsif ($arg eq "--link") {
|
||||
$link = 1;
|
||||
} elsif (substr($arg, 0, 1) eq "-") {
|
||||
die "$0: unknown flag `$arg'\n";
|
||||
} else {
|
||||
push @roots, $arg;
|
||||
}
|
||||
}
|
||||
else {
|
||||
showSyntax if scalar @ARGV < 3;
|
||||
$localCopy = 0;
|
||||
$archivesPutURL = shift @ARGV;
|
||||
$archivesGetURL = shift @ARGV;
|
||||
$manifestPutURL = shift @ARGV;
|
||||
}
|
||||
|
||||
die "$0: please specify a destination directory\n" if !defined $destDir;
|
||||
|
||||
$archivesURL = "file://$destDir" unless defined $archivesURL;
|
||||
|
||||
|
||||
# From the given store paths, determine the set of requisite store
|
||||
# paths, i.e, the paths required to realise them.
|
||||
my %storePaths;
|
||||
|
||||
foreach my $path (@ARGV) {
|
||||
die unless $path =~ /^\//;
|
||||
|
||||
# Get all paths referenced by the normalisation of the given
|
||||
foreach my $path (@roots) {
|
||||
# Get all paths referenced by the normalisation of the given
|
||||
# Nix expression.
|
||||
my $pid = open(READ,
|
||||
"$Nix::Config::binDir/nix-store --query --requisites --force-realise " .
|
||||
"--include-outputs '$path'|") or die;
|
||||
|
||||
|
||||
while (<READ>) {
|
||||
chomp;
|
||||
die "bad: $_" unless /^\//;
|
||||
@@ -92,19 +92,50 @@ foreach my $path (@ARGV) {
|
||||
my @storePaths = keys %storePaths;
|
||||
|
||||
|
||||
# For each path, create a Nix expression that turns the path into
|
||||
# a Nix archive.
|
||||
# Don't create archives for files that are already in the binary cache.
|
||||
my @storePaths2;
|
||||
my %narFiles;
|
||||
foreach my $storePath (@storePaths) {
|
||||
my $pathHash = substr(basename($storePath), 0, 32);
|
||||
my $narInfoFile = "$destDir/$pathHash.narinfo";
|
||||
if (-e $narInfoFile) {
|
||||
my $narInfo = parseNARInfo($storePath, readFile($narInfoFile));
|
||||
my $narFile = "$destDir/$narInfo->{url}";
|
||||
if (-e $narFile) {
|
||||
print STDERR "skipping existing $storePath\n";
|
||||
# Add the NAR info to $narFiles if we're writing a
|
||||
# manifest.
|
||||
$narFiles{$storePath} = [
|
||||
{ url => ("$archivesURL/" . basename $narInfo->{url})
|
||||
, hash => $narInfo->{fileHash}
|
||||
, size => $narInfo->{fileSize}
|
||||
, compressionType => $narInfo->{compression}
|
||||
, narHash => $narInfo->{narHash}
|
||||
, narSize => $narInfo->{narSize}
|
||||
, references => join(" ", map { "$Nix::Config::storeDir/$_" } @{$narInfo->{refs}})
|
||||
, deriver => $narInfo->{deriver} ? "$Nix::Config::storeDir/$narInfo->{deriver}" : undef
|
||||
}
|
||||
] if $writeManifest;
|
||||
next;
|
||||
}
|
||||
}
|
||||
push @storePaths2, $storePath;
|
||||
}
|
||||
|
||||
|
||||
# Create a list of Nix derivations that turn each path into a Nix
|
||||
# archive.
|
||||
open NIX, ">$nixExpr";
|
||||
print NIX "[";
|
||||
|
||||
foreach my $storePath (@storePaths) {
|
||||
foreach my $storePath (@storePaths2) {
|
||||
die unless ($storePath =~ /\/[0-9a-z]{32}[^\"\\\$]*$/);
|
||||
|
||||
# Construct a Nix expression that creates a Nix archive.
|
||||
my $nixexpr =
|
||||
my $nixexpr =
|
||||
"(import <nix/nar.nix> " .
|
||||
"{ storePath = builtins.storePath \"$storePath\"; hashAlgo = \"$hashAlgo\"; }) ";
|
||||
|
||||
"{ storePath = builtins.storePath \"$storePath\"; hashAlgo = \"sha256\"; compressionType = \"$compressionType\"; }) ";
|
||||
|
||||
print NIX $nixexpr;
|
||||
}
|
||||
|
||||
@@ -112,172 +143,135 @@ print NIX "]";
|
||||
close NIX;
|
||||
|
||||
|
||||
# Instantiate store derivations from the Nix expression.
|
||||
my @storeExprs;
|
||||
print STDERR "instantiating store derivations...\n";
|
||||
my $pid = open(READ, "$Nix::Config::binDir/nix-instantiate $nixExpr|")
|
||||
or die "cannot run nix-instantiate";
|
||||
# Build the Nix expression.
|
||||
print STDERR "building compressed archives...\n";
|
||||
my @narPaths;
|
||||
my $pid = open(READ, "$Nix::Config::binDir/nix-build $nixExpr -o $tmpDir/result |")
|
||||
or die "cannot run nix-build";
|
||||
while (<READ>) {
|
||||
chomp;
|
||||
die unless /^\//;
|
||||
push @storeExprs, $_;
|
||||
push @narPaths, $_;
|
||||
}
|
||||
close READ or die "nix-instantiate failed: $?";
|
||||
close READ or die "nix-build failed: $?";
|
||||
|
||||
|
||||
# Build the derivations.
|
||||
print STDERR "creating archives...\n";
|
||||
|
||||
my @narPaths;
|
||||
|
||||
my @tmp = @storeExprs;
|
||||
while (scalar @tmp > 0) {
|
||||
my $n = scalar @tmp;
|
||||
if ($n > 256) { $n = 256 };
|
||||
my @tmp2 = @tmp[0..$n - 1];
|
||||
@tmp = @tmp[$n..scalar @tmp - 1];
|
||||
|
||||
my $pid = open(READ, "$Nix::Config::binDir/nix-store --realise @tmp2|")
|
||||
or die "cannot run nix-store";
|
||||
while (<READ>) {
|
||||
chomp;
|
||||
die unless (/^\//);
|
||||
push @narPaths, "$_";
|
||||
}
|
||||
close READ or die "nix-store failed: $?";
|
||||
# Write the cache info file.
|
||||
my $cacheInfoFile = "$destDir/nix-cache-info";
|
||||
if (! -e $cacheInfoFile) {
|
||||
open FILE, ">$cacheInfoFile" or die "cannot create $cacheInfoFile: $!";
|
||||
print FILE "StoreDir: $Nix::Config::storeDir\n";
|
||||
print FILE "WantMassQuery: 0\n"; # by default, don't hit this cache for "nix-env -qas"
|
||||
close FILE;
|
||||
}
|
||||
|
||||
|
||||
# Create the manifest.
|
||||
print STDERR "creating manifest...\n";
|
||||
# Copy the archives and the corresponding NAR info files.
|
||||
print STDERR "copying archives...\n";
|
||||
|
||||
my %narFiles;
|
||||
my %patches;
|
||||
my $totalNarSize = 0;
|
||||
my $totalCompressedSize = 0;
|
||||
|
||||
my @narArchives;
|
||||
for (my $n = 0; $n < scalar @storePaths; $n++) {
|
||||
my $storePath = $storePaths[$n];
|
||||
for (my $n = 0; $n < scalar @storePaths2; $n++) {
|
||||
my $storePath = $storePaths2[$n];
|
||||
my $narDir = $narPaths[$n];
|
||||
|
||||
$storePath =~ /\/([^\/]*)$/;
|
||||
my $basename = $1;
|
||||
defined $basename or die;
|
||||
my $baseName = basename $storePath;
|
||||
|
||||
open HASH, "$narDir/narbz2-hash" or die "cannot open narbz2-hash";
|
||||
my $narbz2Hash = <HASH>;
|
||||
chomp $narbz2Hash;
|
||||
$narbz2Hash =~ /^[0-9a-z]+$/ or die "invalid hash";
|
||||
close HASH;
|
||||
|
||||
my $narName = "$narbz2Hash.nar.bz2";
|
||||
|
||||
my $narFile = "$narDir/$narName";
|
||||
(-f $narFile) or die "narfile for $storePath not found";
|
||||
push @narArchives, $narFile;
|
||||
|
||||
my $narbz2Size = stat($narFile)->size;
|
||||
|
||||
my $references = `$Nix::Config::binDir/nix-store --query --references '$storePath'`;
|
||||
die "cannot query references for `$storePath'" if $? != 0;
|
||||
$references = join(" ", split(" ", $references));
|
||||
|
||||
my $deriver = `$Nix::Config::binDir/nix-store --query --deriver '$storePath'`;
|
||||
die "cannot query deriver for `$storePath'" if $? != 0;
|
||||
chomp $deriver;
|
||||
$deriver = "" if $deriver eq "unknown-deriver";
|
||||
|
||||
my $narHash = `$Nix::Config::binDir/nix-store --query --hash '$storePath'`;
|
||||
die "cannot query hash for `$storePath'" if $? != 0;
|
||||
chomp $narHash;
|
||||
# Get info about the store path.
|
||||
my ($deriver, $narHash, $time, $narSize, $refs) = queryPathInfo($storePath, 1);
|
||||
|
||||
# In some exceptional cases (such as VM tests that use the Nix
|
||||
# store of the host), the database doesn't contain the hash. So
|
||||
# compute it.
|
||||
if ($narHash =~ /^sha256:0*$/) {
|
||||
$narHash = `$Nix::Config::binDir/nix-hash --type sha256 --base32 '$storePath'`;
|
||||
die "cannot hash `$storePath'" if $? != 0;
|
||||
my $nar = "$tmpDir/nar";
|
||||
system("$Nix::Config::binDir/nix-store --dump $storePath > $nar") == 0
|
||||
or die "cannot dump $storePath\n";
|
||||
$narHash = `$Nix::Config::binDir/nix-hash --type sha256 --base32 --flat $nar`;
|
||||
die "cannot hash `$nar'" if $? != 0;
|
||||
chomp $narHash;
|
||||
$narHash = "sha256:$narHash";
|
||||
$narSize = stat("$nar")->size;
|
||||
unlink $nar or die;
|
||||
}
|
||||
|
||||
my $narSize = `$Nix::Config::binDir/nix-store --query --size '$storePath'`;
|
||||
die "cannot query size for `$storePath'" if $? != 0;
|
||||
chomp $narSize;
|
||||
$totalNarSize += $narSize;
|
||||
|
||||
my $url;
|
||||
if ($localCopy) {
|
||||
$url = "$targetArchivesUrl/$narName";
|
||||
} else {
|
||||
$url = "$archivesGetURL/$narName";
|
||||
# Get info about the compressed NAR.
|
||||
open HASH, "$narDir/nar-compressed-hash" or die "cannot open nar-compressed-hash";
|
||||
my $compressedHash = <HASH>;
|
||||
chomp $compressedHash;
|
||||
$compressedHash =~ /^[0-9a-z]+$/ or die "invalid hash";
|
||||
close HASH;
|
||||
|
||||
my $narName = "$compressedHash.nar" . ($compressionType eq "xz" ? ".xz" : $compressionType eq "bzip2" ? ".bz2" : "");
|
||||
|
||||
my $narFile = "$narDir/$narName";
|
||||
(-f $narFile) or die "NAR file for $storePath not found";
|
||||
|
||||
my $compressedSize = stat($narFile)->size;
|
||||
$totalCompressedSize += $compressedSize;
|
||||
|
||||
printf STDERR "%s [%.2f MiB, %.1f%%]\n", $storePath,
|
||||
$compressedSize / (1024 * 1024), $compressedSize / $narSize * 100;
|
||||
|
||||
# Copy the compressed NAR.
|
||||
my $dst = "$destDir/$narName";
|
||||
if (! -f $dst) {
|
||||
my $tmp = "$destDir/.tmp.$$.$narName";
|
||||
if ($link) {
|
||||
link($narFile, $tmp) or die "cannot link $tmp to $narFile: $!\n";
|
||||
} else {
|
||||
copy($narFile, $tmp) or die "cannot copy $narFile to $tmp: $!\n";
|
||||
}
|
||||
rename($tmp, $dst) or die "cannot rename $tmp to $dst: $!\n";
|
||||
}
|
||||
|
||||
# Write the info file.
|
||||
my $info;
|
||||
$info .= "StorePath: $storePath\n";
|
||||
$info .= "URL: $narName\n";
|
||||
$info .= "Compression: $compressionType\n";
|
||||
$info .= "FileHash: sha256:$compressedHash\n";
|
||||
$info .= "FileSize: $compressedSize\n";
|
||||
$info .= "NarHash: $narHash\n";
|
||||
$info .= "NarSize: $narSize\n";
|
||||
$info .= "References: " . join(" ", map { basename $_ } @{$refs}) . "\n";
|
||||
if (defined $deriver) {
|
||||
$info .= "Deriver: " . basename $deriver . "\n";
|
||||
if (isValidPath($deriver)) {
|
||||
my $drv = derivationFromPath($deriver);
|
||||
$info .= "System: $drv->{platform}\n";
|
||||
}
|
||||
}
|
||||
|
||||
my $pathHash = substr(basename($storePath), 0, 32);
|
||||
|
||||
$dst = "$destDir/$pathHash.narinfo";
|
||||
if ($force || ! -f $dst) {
|
||||
my $tmp = "$destDir/.tmp.$$.$pathHash.narinfo";
|
||||
open INFO, ">$tmp" or die;
|
||||
print INFO "$info" or die;
|
||||
close INFO or die;
|
||||
rename($tmp, $dst) or die "cannot rename $tmp to $dst: $!\n";
|
||||
}
|
||||
|
||||
$narFiles{$storePath} = [
|
||||
{ url => $url
|
||||
, hash => "$hashAlgo:$narbz2Hash"
|
||||
, size => $narbz2Size
|
||||
{ url => "$archivesURL/$narName"
|
||||
, hash => "sha256:$compressedHash"
|
||||
, size => $compressedSize
|
||||
, compressionType => $compressionType
|
||||
, narHash => "$narHash"
|
||||
, narSize => $narSize
|
||||
, references => $references
|
||||
, references => join(" ", @{$refs})
|
||||
, deriver => $deriver
|
||||
}
|
||||
];
|
||||
] if $writeManifest;
|
||||
}
|
||||
|
||||
writeManifest $manifest, \%narFiles, \%patches;
|
||||
printf STDERR "total compressed size %.2f MiB, %.1f%%\n",
|
||||
$totalCompressedSize / (1024 * 1024), $totalCompressedSize / ($totalNarSize || 1) * 100;
|
||||
|
||||
|
||||
sub copyFile {
|
||||
my $src = shift;
|
||||
my $dst = shift;
|
||||
my $tmp = "$dst.tmp.$$";
|
||||
system("@coreutils@/cp", $src, $tmp) == 0 or die "cannot copy file";
|
||||
rename($tmp, $dst) or die "cannot rename file: $!";
|
||||
}
|
||||
|
||||
|
||||
# Upload/copy the archives.
|
||||
print STDERR "uploading/copying archives...\n";
|
||||
|
||||
sub archiveExists {
|
||||
my $name = shift;
|
||||
print STDERR " HEAD on $archivesGetURL/$name\n";
|
||||
return system("$curl --head $archivesGetURL/$name > /dev/null") == 0;
|
||||
}
|
||||
|
||||
foreach my $narArchive (@narArchives) {
|
||||
|
||||
$narArchive =~ /\/([^\/]*)$/;
|
||||
my $basename = $1;
|
||||
|
||||
if ($localCopy) {
|
||||
# Since nix-push creates $dst atomically, if it exists we
|
||||
# don't have to copy again.
|
||||
my $dst = "$localArchivesDir/$basename";
|
||||
if (! -f "$localArchivesDir/$basename") {
|
||||
print STDERR " $narArchive\n";
|
||||
copyFile $narArchive, $dst;
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (!archiveExists("$basename")) {
|
||||
print STDERR " $narArchive\n";
|
||||
system("$curl --show-error --upload-file " .
|
||||
"'$narArchive' '$archivesPutURL/$basename' > /dev/null") == 0 or
|
||||
die "curl failed on $narArchive: $?";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
# Upload the manifest.
|
||||
print STDERR "uploading manifest...\n";
|
||||
if ($localCopy) {
|
||||
copyFile $manifest, $localManifestFile;
|
||||
copyFile "$manifest.bz2", "$localManifestFile.bz2";
|
||||
} else {
|
||||
system("$curl --show-error --upload-file " .
|
||||
"'$manifest' '$manifestPutURL' > /dev/null") == 0 or
|
||||
die "curl failed on $manifest: $?";
|
||||
system("$curl --show-error --upload-file " .
|
||||
"'$manifest'.bz2 '$manifestPutURL'.bz2 > /dev/null") == 0 or
|
||||
die "curl failed on $manifest: $?";
|
||||
}
|
||||
# Optionally write a manifest.
|
||||
writeManifest($manifestPath // "$destDir/MANIFEST", \%narFiles, \()) if $writeManifest;
|
||||
|
||||
+2
-2
@@ -1,3 +1,3 @@
|
||||
SUBDIRS = bin2c boost libutil libstore libmain nix-store nix-hash \
|
||||
libexpr nix-instantiate nix-env nix-worker nix-setuid-helper \
|
||||
SUBDIRS = boost libutil libstore libmain nix-store nix-hash \
|
||||
libexpr nix-instantiate nix-env nix-daemon nix-setuid-helper \
|
||||
nix-log2xml bsdiff-4.3
|
||||
|
||||
@@ -1,6 +0,0 @@
|
||||
noinst_PROGRAMS = bin2c
|
||||
|
||||
bin2c_SOURCES = bin2c.c
|
||||
|
||||
bin2c$(EXEEXT): bin2c.c
|
||||
$(CC_FOR_BUILD) $(CFLAGS_FOR_BUILD) -o bin2c bin2c.c
|
||||
@@ -1,23 +0,0 @@
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <stdarg.h>
|
||||
|
||||
void print(const char *format, ...)
|
||||
{
|
||||
va_list ap;
|
||||
va_start(ap, format);
|
||||
if (vprintf(format, ap) < 0) abort();
|
||||
va_end(ap);
|
||||
}
|
||||
|
||||
int main(int argc, char * * argv)
|
||||
{
|
||||
int c;
|
||||
if (argc != 2) abort();
|
||||
print("static unsigned char %s[] = { ", argv[1]);
|
||||
while ((c = getchar()) != EOF) {
|
||||
print("0x%02x, ", (unsigned char) c);
|
||||
}
|
||||
print("0 };\n");
|
||||
return 0;
|
||||
}
|
||||
+23
-22
@@ -6,19 +6,18 @@
|
||||
namespace nix {
|
||||
|
||||
|
||||
// !!! Shouldn't we return a pointer to a Value?
|
||||
void findAlongAttrPath(EvalState & state, const string & attrPath,
|
||||
Bindings & autoArgs, Expr * e, Value & v)
|
||||
Value * findAlongAttrPath(EvalState & state, const string & attrPath,
|
||||
Bindings & autoArgs, Value & vIn)
|
||||
{
|
||||
Strings tokens = tokenizeString(attrPath, ".");
|
||||
Strings tokens = tokenizeString<Strings>(attrPath, ".");
|
||||
|
||||
Error attrError =
|
||||
Error(format("attribute selection path `%1%' does not match expression") % attrPath);
|
||||
|
||||
string curPath;
|
||||
|
||||
state.mkThunk_(v, e);
|
||||
|
||||
Value * v = &vIn;
|
||||
|
||||
foreach (Strings::iterator, i, tokens) {
|
||||
|
||||
if (!curPath.empty()) curPath += ".";
|
||||
@@ -27,14 +26,14 @@ void findAlongAttrPath(EvalState & state, const string & attrPath,
|
||||
/* Is *i an index (integer) or a normal attribute name? */
|
||||
enum { apAttr, apIndex } apType = apAttr;
|
||||
string attr = *i;
|
||||
int attrIndex = -1;
|
||||
unsigned int attrIndex;
|
||||
if (string2Int(attr, attrIndex)) apType = apIndex;
|
||||
|
||||
/* Evaluate the expression. */
|
||||
Value vTmp;
|
||||
state.autoCallFunction(autoArgs, v, vTmp);
|
||||
v = vTmp;
|
||||
state.forceValue(v);
|
||||
Value * vNew = state.allocValue();
|
||||
state.autoCallFunction(autoArgs, *v, *vNew);
|
||||
v = vNew;
|
||||
state.forceValue(*v);
|
||||
|
||||
/* It should evaluate to either an attribute set or an
|
||||
expression, according to what is specified in the
|
||||
@@ -42,32 +41,34 @@ void findAlongAttrPath(EvalState & state, const string & attrPath,
|
||||
|
||||
if (apType == apAttr) {
|
||||
|
||||
if (v.type != tAttrs)
|
||||
if (v->type != tAttrs)
|
||||
throw TypeError(
|
||||
format("the expression selected by the selection path `%1%' should be an attribute set but is %2%")
|
||||
% curPath % showType(v));
|
||||
% curPath % showType(*v));
|
||||
|
||||
Bindings::iterator a = v.attrs->find(state.symbols.create(attr));
|
||||
if (a == v.attrs->end())
|
||||
Bindings::iterator a = v->attrs->find(state.symbols.create(attr));
|
||||
if (a == v->attrs->end())
|
||||
throw Error(format("attribute `%1%' in selection path `%2%' not found") % attr % curPath);
|
||||
v = *a->value;
|
||||
v = &*a->value;
|
||||
}
|
||||
|
||||
else if (apType == apIndex) {
|
||||
|
||||
if (v.type != tList)
|
||||
if (v->type != tList)
|
||||
throw TypeError(
|
||||
format("the expression selected by the selection path `%1%' should be a list but is %2%")
|
||||
% curPath % showType(v));
|
||||
% curPath % showType(*v));
|
||||
|
||||
if (attrIndex >= v.list.length)
|
||||
if (attrIndex >= v->list.length)
|
||||
throw Error(format("list index %1% in selection path `%2%' is out of range") % attrIndex % curPath);
|
||||
|
||||
v = *v.list.elems[attrIndex];
|
||||
v = v->list.elems[attrIndex];
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
return v;
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
|
||||
namespace nix {
|
||||
|
||||
void findAlongAttrPath(EvalState & state, const string & attrPath,
|
||||
Bindings & autoArgs, Expr * e, Value & v);
|
||||
Value * findAlongAttrPath(EvalState & state, const string & attrPath,
|
||||
Bindings & autoArgs, Value & vIn);
|
||||
|
||||
}
|
||||
|
||||
@@ -21,13 +21,16 @@ LocalNoInlineNoReturn(void throwTypeError(const char * s, const string & s2))
|
||||
void EvalState::forceValue(Value & v)
|
||||
{
|
||||
if (v.type == tThunk) {
|
||||
ValueType saved = v.type;
|
||||
Env * env = v.thunk.env;
|
||||
Expr * expr = v.thunk.expr;
|
||||
try {
|
||||
v.type = tBlackhole;
|
||||
//checkInterrupt();
|
||||
v.thunk.expr->eval(*this, *v.thunk.env, v);
|
||||
expr->eval(*this, *env, v);
|
||||
} catch (Error & e) {
|
||||
v.type = saved;
|
||||
v.type = tThunk;
|
||||
v.thunk.env = env;
|
||||
v.thunk.expr = expr;
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
+214
-119
@@ -44,7 +44,7 @@ void Bindings::sort()
|
||||
{
|
||||
std::sort(begin(), end());
|
||||
}
|
||||
|
||||
|
||||
|
||||
std::ostream & operator << (std::ostream & str, const Value & v)
|
||||
{
|
||||
@@ -138,12 +138,18 @@ EvalState::EvalState()
|
||||
, sName(symbols.create("name"))
|
||||
, sSystem(symbols.create("system"))
|
||||
, sOverrides(symbols.create("__overrides"))
|
||||
, sOutputs(symbols.create("outputs"))
|
||||
, sOutputName(symbols.create("outputName"))
|
||||
, sIgnoreNulls(symbols.create("__ignoreNulls"))
|
||||
, repair(false)
|
||||
, baseEnv(allocEnv(128))
|
||||
, baseEnvDispl(0)
|
||||
, staticBaseEnv(false, 0)
|
||||
, baseEnvDispl(0)
|
||||
{
|
||||
nrEnvs = nrValuesInEnvs = nrValues = nrListElems = 0;
|
||||
nrAttrsets = nrOpUpdates = nrOpUpdateValuesCopied = 0;
|
||||
nrListConcats = nrPrimOpCalls = nrFunctionCalls = 0;
|
||||
countCalls = getEnv("NIX_COUNT_CALLS", "0") != "0";
|
||||
|
||||
#if HAVE_BOEHMGC
|
||||
static bool gcInitialised = true;
|
||||
@@ -152,7 +158,7 @@ EvalState::EvalState()
|
||||
physical RAM, up to a maximum of 384 MiB) so that in most
|
||||
cases we don't need to garbage collect at all. (Collection
|
||||
has a fairly significant overhead.) The heap size can be
|
||||
overriden through libgc's GC_INITIAL_HEAP_SIZE environment
|
||||
overridden through libgc's GC_INITIAL_HEAP_SIZE environment
|
||||
variable. We should probably also provide a nix.conf
|
||||
setting for this. Note that GC_expand_hp() causes a lot of
|
||||
virtual, but not physical (resident) memory to be
|
||||
@@ -177,9 +183,9 @@ EvalState::EvalState()
|
||||
|
||||
/* Initialise the Nix expression search path. */
|
||||
searchPathInsertionPoint = searchPath.end();
|
||||
Strings paths = tokenizeString(getEnv("NIX_PATH", ""), ":");
|
||||
Strings paths = tokenizeString<Strings>(getEnv("NIX_PATH", ""), ":");
|
||||
foreach (Strings::iterator, i, paths) addToSearchPath(*i);
|
||||
addToSearchPath("nix=" + nixDataDir + "/nix/corepkgs");
|
||||
addToSearchPath("nix=" + settings.nixDataDir + "/nix/corepkgs");
|
||||
searchPathInsertionPoint = searchPath.begin();
|
||||
|
||||
createBaseEnv();
|
||||
@@ -216,6 +222,12 @@ void EvalState::addPrimOp(const string & name,
|
||||
}
|
||||
|
||||
|
||||
void EvalState::getBuiltin(const string & name, Value & v)
|
||||
{
|
||||
v = *baseEnv.values[0]->attrs->find(symbols.create(name))->value;
|
||||
}
|
||||
|
||||
|
||||
/* Every "format" object (even temporary) takes up a few hundred bytes
|
||||
of stack space, which is a real killer in the recursive
|
||||
evaluator. So here are some helper functions for throwing
|
||||
@@ -241,6 +253,11 @@ LocalNoInlineNoReturn(void throwTypeError(const char * s, const Pos & pos, const
|
||||
throw TypeError(format(s) % pos % s2);
|
||||
}
|
||||
|
||||
LocalNoInlineNoReturn(void throwTypeError(const char * s, const string & s1, const string & s2))
|
||||
{
|
||||
throw TypeError(format(s) % s1 % s2);
|
||||
}
|
||||
|
||||
LocalNoInlineNoReturn(void throwTypeError(const char * s, const Pos & pos))
|
||||
{
|
||||
throw TypeError(format(s) % pos);
|
||||
@@ -280,7 +297,7 @@ void mkString(Value & v, const string & s, const PathSet & context)
|
||||
unsigned int n = 0;
|
||||
v.string.context = (const char * *)
|
||||
GC_MALLOC((context.size() + 1) * sizeof(char *));
|
||||
foreach (PathSet::const_iterator, i, context)
|
||||
foreach (PathSet::const_iterator, i, context)
|
||||
v.string.context[n++] = GC_STRDUP(i->c_str());
|
||||
v.string.context[n] = 0;
|
||||
}
|
||||
@@ -293,21 +310,29 @@ void mkPath(Value & v, const char * s)
|
||||
}
|
||||
|
||||
|
||||
inline Value * EvalState::lookupVar(Env * env, const VarRef & var)
|
||||
inline Value * EvalState::lookupVar(Env * env, const VarRef & var, bool noEval)
|
||||
{
|
||||
for (unsigned int l = var.level; l; --l, env = env->up) ;
|
||||
|
||||
if (var.fromWith) {
|
||||
while (1) {
|
||||
Bindings::iterator j = env->values[0]->attrs->find(var.name);
|
||||
if (j != env->values[0]->attrs->end())
|
||||
return j->value;
|
||||
if (env->prevWith == 0)
|
||||
throwEvalError("undefined variable `%1%'", var.name);
|
||||
for (unsigned int l = env->prevWith; l; --l, env = env->up) ;
|
||||
|
||||
if (!var.fromWith) return env->values[var.displ];
|
||||
|
||||
while (1) {
|
||||
if (!env->haveWithAttrs) {
|
||||
if (noEval) return 0;
|
||||
Expr * attrs = (Expr *) env->values[0];
|
||||
env->values[0] = allocValue();
|
||||
evalAttrs(*env->up, attrs, *env->values[0]);
|
||||
env->haveWithAttrs = true;
|
||||
}
|
||||
} else
|
||||
return env->values[var.displ];
|
||||
Bindings::iterator j = env->values[0]->attrs->find(var.name);
|
||||
if (j != env->values[0]->attrs->end()) {
|
||||
if (countCalls && j->pos) attrSelects[*j->pos]++;
|
||||
return j->value;
|
||||
}
|
||||
if (!env->prevWith)
|
||||
throwEvalError("undefined variable `%1%'", var.name);
|
||||
for (unsigned int l = env->prevWith; l; --l, env = env->up) ;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -324,10 +349,10 @@ Env & EvalState::allocEnv(unsigned int size)
|
||||
nrValuesInEnvs += size;
|
||||
Env * env = (Env *) GC_MALLOC(sizeof(Env) + size * sizeof(Value *));
|
||||
|
||||
/* Clear the values because maybeThunk() expects this. */
|
||||
/* Clear the values because maybeThunk() and lookupVar fromWith expects this. */
|
||||
for (unsigned i = 0; i < size; ++i)
|
||||
env->values[i] = 0;
|
||||
|
||||
|
||||
return *env;
|
||||
}
|
||||
|
||||
@@ -339,12 +364,12 @@ Value * EvalState::allocAttr(Value & vAttrs, const Symbol & name)
|
||||
return v;
|
||||
}
|
||||
|
||||
|
||||
|
||||
void EvalState::mkList(Value & v, unsigned int length)
|
||||
{
|
||||
v.type = tList;
|
||||
v.list.length = length;
|
||||
v.list.elems = (Value * *) GC_MALLOC(length * sizeof(Value *));
|
||||
v.list.elems = length ? (Value * *) GC_MALLOC(length * sizeof(Value *)) : 0;
|
||||
nrListElems += length;
|
||||
}
|
||||
|
||||
@@ -392,7 +417,7 @@ unsigned long nrAvoided = 0;
|
||||
|
||||
Value * ExprVar::maybeThunk(EvalState & state, Env & env)
|
||||
{
|
||||
Value * v = state.lookupVar(&env, info);
|
||||
Value * v = state.lookupVar(&env, info, true);
|
||||
/* The value might not be initialised in the environment yet.
|
||||
In that case, ignore it. */
|
||||
if (v) { nrAvoided++; return v; }
|
||||
@@ -421,19 +446,30 @@ Value * ExprPath::maybeThunk(EvalState & state, Env & env)
|
||||
|
||||
void EvalState::evalFile(const Path & path, Value & v)
|
||||
{
|
||||
FileEvalCache::iterator i = fileEvalCache.find(path);
|
||||
if (i == fileEvalCache.end()) {
|
||||
startNest(nest, lvlTalkative, format("evaluating file `%1%'") % path);
|
||||
Expr * e = parseExprFromFile(path);
|
||||
try {
|
||||
eval(e, v);
|
||||
} catch (Error & e) {
|
||||
addErrorPrefix(e, "while evaluating the file `%1%':\n", path);
|
||||
throw;
|
||||
}
|
||||
fileEvalCache[path] = v;
|
||||
} else
|
||||
Path path2 = resolveExprPath(path);
|
||||
|
||||
FileEvalCache::iterator i = fileEvalCache.find(path2);
|
||||
if (i != fileEvalCache.end()) {
|
||||
v = i->second;
|
||||
return;
|
||||
}
|
||||
|
||||
startNest(nest, lvlTalkative, format("evaluating file `%1%'") % path2);
|
||||
Expr * e = parseExprFromFile(path2);
|
||||
try {
|
||||
eval(e, v);
|
||||
} catch (Error & e) {
|
||||
addErrorPrefix(e, "while evaluating the file `%1%':\n", path2);
|
||||
throw;
|
||||
}
|
||||
fileEvalCache[path2] = v;
|
||||
//if (path != path2) fileEvalCache[path2] = v;
|
||||
}
|
||||
|
||||
|
||||
void EvalState::resetFileCache()
|
||||
{
|
||||
fileEvalCache.clear();
|
||||
}
|
||||
|
||||
|
||||
@@ -502,23 +538,17 @@ void ExprAttrs::eval(EvalState & state, Env & env, Value & v)
|
||||
environment, while the inherited attributes are evaluated
|
||||
in the original environment. */
|
||||
unsigned int displ = 0;
|
||||
foreach (AttrDefs::iterator, i, attrs)
|
||||
if (i->second.inherited) {
|
||||
/* !!! handle overrides? */
|
||||
Value * vAttr = state.lookupVar(&env, i->second.var);
|
||||
env2.values[displ++] = vAttr;
|
||||
v.attrs->push_back(Attr(i->first, vAttr, &i->second.pos));
|
||||
} else {
|
||||
Value * vAttr;
|
||||
if (hasOverrides) {
|
||||
vAttr = state.allocValue();
|
||||
mkThunk(*vAttr, env2, i->second.e);
|
||||
} else
|
||||
vAttr = i->second.e->maybeThunk(state, env2);
|
||||
env2.values[displ++] = vAttr;
|
||||
v.attrs->push_back(Attr(i->first, vAttr, &i->second.pos));
|
||||
}
|
||||
|
||||
foreach (AttrDefs::iterator, i, attrs) {
|
||||
Value * vAttr;
|
||||
if (hasOverrides && !i->second.inherited) {
|
||||
vAttr = state.allocValue();
|
||||
mkThunk(*vAttr, env2, i->second.e);
|
||||
} else
|
||||
vAttr = i->second.e->maybeThunk(state, i->second.inherited ? env : env2);
|
||||
env2.values[displ++] = vAttr;
|
||||
v.attrs->push_back(Attr(i->first, vAttr, &i->second.pos));
|
||||
}
|
||||
|
||||
/* If the rec contains an attribute called `__overrides', then
|
||||
evaluate it, and add the attributes in that set to the rec.
|
||||
This allows overriding of recursive attributes, which is
|
||||
@@ -544,10 +574,7 @@ void ExprAttrs::eval(EvalState & state, Env & env, Value & v)
|
||||
|
||||
else {
|
||||
foreach (AttrDefs::iterator, i, attrs)
|
||||
if (i->second.inherited)
|
||||
v.attrs->push_back(Attr(i->first, state.lookupVar(&env, i->second.var), &i->second.pos));
|
||||
else
|
||||
v.attrs->push_back(Attr(i->first, i->second.e->maybeThunk(state, env), &i->second.pos));
|
||||
v.attrs->push_back(Attr(i->first, i->second.e->maybeThunk(state, env), &i->second.pos));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -564,10 +591,7 @@ void ExprLet::eval(EvalState & state, Env & env, Value & v)
|
||||
environment. */
|
||||
unsigned int displ = 0;
|
||||
foreach (ExprAttrs::AttrDefs::iterator, i, attrs->attrs)
|
||||
if (i->second.inherited)
|
||||
env2.values[displ++] = state.lookupVar(&env, i->second.var);
|
||||
else
|
||||
env2.values[displ++] = i->second.e->maybeThunk(state, env2);
|
||||
env2.values[displ++] = i->second.e->maybeThunk(state, i->second.inherited ? env : env2);
|
||||
|
||||
body->eval(state, env2, v);
|
||||
}
|
||||
@@ -583,7 +607,7 @@ void ExprList::eval(EvalState & state, Env & env, Value & v)
|
||||
|
||||
void ExprVar::eval(EvalState & state, Env & env, Value & v)
|
||||
{
|
||||
Value * v2 = state.lookupVar(&env, info);
|
||||
Value * v2 = state.lookupVar(&env, info, false);
|
||||
state.forceValue(*v2);
|
||||
v = *v2;
|
||||
}
|
||||
@@ -600,7 +624,7 @@ void ExprSelect::eval(EvalState & state, Env & env, Value & v)
|
||||
e->eval(state, env, vTmp);
|
||||
|
||||
try {
|
||||
|
||||
|
||||
foreach (AttrPath::const_iterator, i, attrPath) {
|
||||
nrLookups++;
|
||||
Bindings::iterator j;
|
||||
@@ -619,17 +643,19 @@ void ExprSelect::eval(EvalState & state, Env & env, Value & v)
|
||||
}
|
||||
vAttrs = j->value;
|
||||
pos = j->pos;
|
||||
if (state.countCalls && pos) state.attrSelects[*pos]++;
|
||||
}
|
||||
|
||||
|
||||
|
||||
state.forceValue(*vAttrs);
|
||||
|
||||
|
||||
} catch (Error & e) {
|
||||
if (pos)
|
||||
addErrorPrefix(e, "while evaluating the attribute `%1%' at %2%:\n",
|
||||
showAttrPath(attrPath), *pos);
|
||||
throw;
|
||||
}
|
||||
|
||||
|
||||
v = *vAttrs;
|
||||
}
|
||||
|
||||
@@ -653,7 +679,7 @@ void ExprOpHasAttr::eval(EvalState & state, Env & env, Value & v)
|
||||
vAttrs = j->value;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
mkBool(v, true);
|
||||
}
|
||||
|
||||
@@ -688,10 +714,10 @@ void EvalState::callFunction(Value & fun, Value & arg, Value & v)
|
||||
assert(primOp->type == tPrimOp);
|
||||
unsigned int arity = primOp->primOp->arity;
|
||||
unsigned int argsLeft = arity - argsDone;
|
||||
|
||||
|
||||
if (argsLeft == 1) {
|
||||
/* We have all the arguments, so call the primop. */
|
||||
|
||||
|
||||
/* Put all the arguments in an array. */
|
||||
Value * vArgs[arity];
|
||||
unsigned int n = arity - 1;
|
||||
@@ -700,6 +726,8 @@ void EvalState::callFunction(Value & fun, Value & arg, Value & v)
|
||||
vArgs[n--] = arg->primOpApp.right;
|
||||
|
||||
/* And call the primop. */
|
||||
nrPrimOpCalls++;
|
||||
if (countCalls) primOpCalls[primOp->primOp->name]++;
|
||||
try {
|
||||
primOp->primOp->fun(*this, vArgs, v);
|
||||
} catch (Error & e) {
|
||||
@@ -714,9 +742,9 @@ void EvalState::callFunction(Value & fun, Value & arg, Value & v)
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
if (fun.type != tLambda)
|
||||
throwTypeError("attempt to call something which is neither a function nor a primop (built-in operation) but %1%",
|
||||
throwTypeError("attempt to call something which is not a function but %1%",
|
||||
showType(fun));
|
||||
|
||||
unsigned int size =
|
||||
@@ -732,7 +760,7 @@ void EvalState::callFunction(Value & fun, Value & arg, Value & v)
|
||||
|
||||
else {
|
||||
forceAttrs(arg);
|
||||
|
||||
|
||||
if (!fun.lambda.fun->arg.empty())
|
||||
env2.values[displ++] = &arg;
|
||||
|
||||
@@ -743,8 +771,8 @@ void EvalState::callFunction(Value & fun, Value & arg, Value & v)
|
||||
foreach (Formals::Formals_::iterator, i, fun.lambda.fun->formals->formals) {
|
||||
Bindings::iterator j = arg.attrs->find(i->name);
|
||||
if (j == arg.attrs->end()) {
|
||||
if (!i->def) throwTypeError("function at %1% called without required argument `%2%'",
|
||||
fun.lambda.fun->pos, i->name);
|
||||
if (!i->def) throwTypeError("%1% called without required argument `%2%'",
|
||||
fun.lambda.fun->showNamePos(), i->name);
|
||||
env2.values[displ++] = i->def->maybeThunk(*this, env2);
|
||||
} else {
|
||||
attrsUsed++;
|
||||
@@ -753,17 +781,24 @@ void EvalState::callFunction(Value & fun, Value & arg, Value & v)
|
||||
}
|
||||
|
||||
/* Check that each actual argument is listed as a formal
|
||||
argument (unless the attribute match specifies a `...').
|
||||
TODO: show the names of the expected/unexpected
|
||||
arguments. */
|
||||
if (!fun.lambda.fun->formals->ellipsis && attrsUsed != arg.attrs->size())
|
||||
throwTypeError("function at %1% called with unexpected argument", fun.lambda.fun->pos);
|
||||
argument (unless the attribute match specifies a `...'). */
|
||||
if (!fun.lambda.fun->formals->ellipsis && attrsUsed != arg.attrs->size()) {
|
||||
/* Nope, so show the first unexpected argument to the
|
||||
user. */
|
||||
foreach (Bindings::iterator, i, *arg.attrs)
|
||||
if (fun.lambda.fun->formals->argNames.find(i->name) == fun.lambda.fun->formals->argNames.end())
|
||||
throwTypeError("%1% called with unexpected argument `%2%'", fun.lambda.fun->showNamePos(), i->name);
|
||||
abort(); // can't happen
|
||||
}
|
||||
}
|
||||
|
||||
nrFunctionCalls++;
|
||||
if (countCalls) functionCalls[fun.lambda.fun]++;
|
||||
|
||||
try {
|
||||
fun.lambda.fun->body->eval(*this, env2, v);
|
||||
} catch (Error & e) {
|
||||
addErrorPrefix(e, "while evaluating the function at %1%:\n", fun.lambda.fun->pos);
|
||||
addErrorPrefix(e, "while evaluating %1%:\n", fun.lambda.fun->showNamePos());
|
||||
throw;
|
||||
}
|
||||
}
|
||||
@@ -778,20 +813,20 @@ void EvalState::autoCallFunction(Bindings & args, Value & fun, Value & res)
|
||||
return;
|
||||
}
|
||||
|
||||
Value actualArgs;
|
||||
mkAttrs(actualArgs, fun.lambda.fun->formals->formals.size());
|
||||
Value * actualArgs = allocValue();
|
||||
mkAttrs(*actualArgs, fun.lambda.fun->formals->formals.size());
|
||||
|
||||
foreach (Formals::Formals_::iterator, i, fun.lambda.fun->formals->formals) {
|
||||
Bindings::iterator j = args.find(i->name);
|
||||
if (j != args.end())
|
||||
actualArgs.attrs->push_back(*j);
|
||||
actualArgs->attrs->push_back(*j);
|
||||
else if (!i->def)
|
||||
throwTypeError("cannot auto-call a function that has an argument without a default value (`%1%')", i->name);
|
||||
}
|
||||
|
||||
actualArgs.attrs->sort();
|
||||
actualArgs->attrs->sort();
|
||||
|
||||
callFunction(fun, actualArgs, res);
|
||||
callFunction(fun, *actualArgs, res);
|
||||
}
|
||||
|
||||
|
||||
@@ -800,9 +835,8 @@ void ExprWith::eval(EvalState & state, Env & env, Value & v)
|
||||
Env & env2(state.allocEnv(1));
|
||||
env2.up = &env;
|
||||
env2.prevWith = prevWith;
|
||||
|
||||
env2.values[0] = state.allocValue();
|
||||
state.evalAttrs(env, attrs, *env2.values[0]);
|
||||
env2.haveWithAttrs = false;
|
||||
env2.values[0] = (Value *) attrs;
|
||||
|
||||
body->eval(state, env2, v);
|
||||
}
|
||||
@@ -813,7 +847,7 @@ void ExprIf::eval(EvalState & state, Env & env, Value & v)
|
||||
(state.evalBool(env, cond) ? then : else_)->eval(state, env, v);
|
||||
}
|
||||
|
||||
|
||||
|
||||
void ExprAssert::eval(EvalState & state, Env & env, Value & v)
|
||||
{
|
||||
if (!state.evalBool(env, cond))
|
||||
@@ -821,7 +855,7 @@ void ExprAssert::eval(EvalState & state, Env & env, Value & v)
|
||||
body->eval(state, env, v);
|
||||
}
|
||||
|
||||
|
||||
|
||||
void ExprOpNot::eval(EvalState & state, Env & env, Value & v)
|
||||
{
|
||||
mkBool(v, !state.evalBool(env, e));
|
||||
@@ -894,7 +928,7 @@ void ExprOpUpdate::eval(EvalState & state, Env & env, Value & v)
|
||||
|
||||
while (i != v1.attrs->end()) v.attrs->push_back(*i++);
|
||||
while (j != v2.attrs->end()) v.attrs->push_back(*j++);
|
||||
|
||||
|
||||
state.nrOpUpdateValuesCopied += v.attrs->size();
|
||||
}
|
||||
|
||||
@@ -902,14 +936,36 @@ void ExprOpUpdate::eval(EvalState & state, Env & env, Value & v)
|
||||
void ExprOpConcatLists::eval(EvalState & state, Env & env, Value & v)
|
||||
{
|
||||
Value v1; e1->eval(state, env, v1);
|
||||
state.forceList(v1);
|
||||
Value v2; e2->eval(state, env, v2);
|
||||
state.forceList(v2);
|
||||
state.mkList(v, v1.list.length + v2.list.length);
|
||||
for (unsigned int n = 0; n < v1.list.length; ++n)
|
||||
v.list.elems[n] = v1.list.elems[n];
|
||||
for (unsigned int n = 0; n < v2.list.length; ++n)
|
||||
v.list.elems[n + v1.list.length] = v2.list.elems[n];
|
||||
Value * lists[2] = { &v1, &v2 };
|
||||
state.concatLists(v, 2, lists);
|
||||
}
|
||||
|
||||
|
||||
void EvalState::concatLists(Value & v, unsigned int nrLists, Value * * lists)
|
||||
{
|
||||
nrListConcats++;
|
||||
|
||||
Value * nonEmpty = 0;
|
||||
unsigned int len = 0;
|
||||
for (unsigned int n = 0; n < nrLists; ++n) {
|
||||
forceList(*lists[n]);
|
||||
unsigned int l = lists[n]->list.length;
|
||||
len += l;
|
||||
if (l) nonEmpty = lists[n];
|
||||
}
|
||||
|
||||
if (nonEmpty && len == nonEmpty->list.length) {
|
||||
v = *nonEmpty;
|
||||
return;
|
||||
}
|
||||
|
||||
mkList(v, len);
|
||||
for (unsigned int n = 0, pos = 0; n < nrLists; ++n) {
|
||||
unsigned int l = lists[n]->list.length;
|
||||
memcpy(v.list.elems + pos, lists[n]->list.elems, l * sizeof(Value *));
|
||||
pos += l;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -917,31 +973,39 @@ void ExprConcatStrings::eval(EvalState & state, Env & env, Value & v)
|
||||
{
|
||||
PathSet context;
|
||||
std::ostringstream s;
|
||||
|
||||
bool first = true, isPath = false;
|
||||
Value vStr;
|
||||
NixInt n = 0;
|
||||
|
||||
bool first = true;
|
||||
ValueType firstType;
|
||||
|
||||
foreach (vector<Expr *>::iterator, i, *es) {
|
||||
(*i)->eval(state, env, vStr);
|
||||
Value vTmp;
|
||||
(*i)->eval(state, env, vTmp);
|
||||
|
||||
/* If the first element is a path, then the result will also
|
||||
be a path, we don't copy anything (yet - that's done later,
|
||||
since paths are copied when they are used in a derivation),
|
||||
and none of the strings are allowed to have contexts. */
|
||||
if (first) {
|
||||
isPath = vStr.type == tPath;
|
||||
firstType = vTmp.type;
|
||||
first = false;
|
||||
}
|
||||
|
||||
s << state.coerceToString(vStr, context, false, !isPath);
|
||||
}
|
||||
|
||||
if (isPath && !context.empty())
|
||||
throwEvalError("a string that refers to a store path cannot be appended to a path, in `%1%'", s.str());
|
||||
|
||||
if (isPath)
|
||||
if (firstType == tInt && !forceString) {
|
||||
if (vTmp.type != tInt)
|
||||
throwEvalError("cannot add %1% to an integer", showType(vTmp));
|
||||
n += vTmp.integer;
|
||||
} else
|
||||
s << state.coerceToString(vTmp, context, false, firstType != tPath);
|
||||
}
|
||||
|
||||
if (firstType == tInt)
|
||||
mkInt(v, n);
|
||||
else if (firstType == tPath) {
|
||||
if (!context.empty())
|
||||
throwEvalError("a string that refers to a store path cannot be appended to a path, in `%1%'", s.str());
|
||||
mkPath(v, s.str().c_str());
|
||||
else
|
||||
} else
|
||||
mkString(v, s.str(), context);
|
||||
}
|
||||
|
||||
@@ -949,12 +1013,12 @@ void ExprConcatStrings::eval(EvalState & state, Env & env, Value & v)
|
||||
void EvalState::strictForceValue(Value & v)
|
||||
{
|
||||
forceValue(v);
|
||||
|
||||
|
||||
if (v.type == tAttrs) {
|
||||
foreach (Bindings::iterator, i, *v.attrs)
|
||||
strictForceValue(*i->value);
|
||||
}
|
||||
|
||||
|
||||
else if (v.type == tList) {
|
||||
for (unsigned int n = 0; n < v.list.length; ++n)
|
||||
strictForceValue(*v.list.elems[n]);
|
||||
@@ -962,7 +1026,7 @@ void EvalState::strictForceValue(Value & v)
|
||||
}
|
||||
|
||||
|
||||
int EvalState::forceInt(Value & v)
|
||||
NixInt EvalState::forceInt(Value & v)
|
||||
{
|
||||
forceValue(v);
|
||||
if (v.type != tInt)
|
||||
@@ -1000,7 +1064,7 @@ string EvalState::forceString(Value & v)
|
||||
void copyContext(const Value & v, PathSet & context)
|
||||
{
|
||||
if (v.string.context)
|
||||
for (const char * * p = v.string.context; *p; ++p)
|
||||
for (const char * * p = v.string.context; *p; ++p)
|
||||
context.insert(*p);
|
||||
}
|
||||
|
||||
@@ -1050,7 +1114,7 @@ string EvalState::coerceToString(Value & v, PathSet & context,
|
||||
Path path(canonPath(v.path));
|
||||
|
||||
if (!copyToStore) return path;
|
||||
|
||||
|
||||
if (nix::isDerivation(path))
|
||||
throwEvalError("file names are not allowed to end in `%1%'", drvExtension);
|
||||
|
||||
@@ -1058,9 +1122,9 @@ string EvalState::coerceToString(Value & v, PathSet & context,
|
||||
if (srcToStore[path] != "")
|
||||
dstPath = srcToStore[path];
|
||||
else {
|
||||
dstPath = readOnlyMode
|
||||
dstPath = settings.readOnlyMode
|
||||
? computeStorePathForPath(path).first
|
||||
: store->addToStore(path);
|
||||
: store->addToStore(path, true, htSHA256, defaultPathFilter, repair);
|
||||
srcToStore[path] = dstPath;
|
||||
printMsg(lvlChatty, format("copied source `%1%' -> `%2%'")
|
||||
% path % dstPath);
|
||||
@@ -1099,7 +1163,7 @@ string EvalState::coerceToString(Value & v, PathSet & context,
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
throwTypeError("cannot coerce %1% to a string", showType(v));
|
||||
}
|
||||
|
||||
@@ -1175,7 +1239,7 @@ bool EvalState::eqValues(Value & v1, Value & v2)
|
||||
for (i = v1.attrs->begin(), j = v2.attrs->begin(); i != v1.attrs->end(); ++i, ++j)
|
||||
if (i->name != j->name || !eqValues(*i->value, *j->value))
|
||||
return false;
|
||||
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -1207,6 +1271,7 @@ void EvalState::printStats()
|
||||
% nrEnvs % (nrEnvs * sizeof(Env) + nrValuesInEnvs * sizeof(Value *)));
|
||||
printMsg(v, format(" list elements: %1% (%2% bytes)")
|
||||
% nrListElems % (nrListElems * sizeof(Value *)));
|
||||
printMsg(v, format(" list concatenations: %1%") % nrListConcats);
|
||||
printMsg(v, format(" values allocated: %1% (%2% bytes)")
|
||||
% nrValues % (nrValues * sizeof(Value)));
|
||||
printMsg(v, format(" attribute sets allocated: %1%") % nrAttrsets);
|
||||
@@ -1216,6 +1281,36 @@ void EvalState::printStats()
|
||||
printMsg(v, format(" number of thunks: %1%") % nrThunks);
|
||||
printMsg(v, format(" number of thunks avoided: %1%") % nrAvoided);
|
||||
printMsg(v, format(" number of attr lookups: %1%") % nrLookups);
|
||||
printMsg(v, format(" number of primop calls: %1%") % nrPrimOpCalls);
|
||||
printMsg(v, format(" number of function calls: %1%") % nrFunctionCalls);
|
||||
|
||||
if (countCalls) {
|
||||
|
||||
printMsg(v, format("calls to %1% primops:") % primOpCalls.size());
|
||||
typedef std::multimap<unsigned int, Symbol> PrimOpCalls_;
|
||||
PrimOpCalls_ primOpCalls_;
|
||||
foreach (PrimOpCalls::iterator, i, primOpCalls)
|
||||
primOpCalls_.insert(std::pair<unsigned int, Symbol>(i->second, i->first));
|
||||
foreach_reverse (PrimOpCalls_::reverse_iterator, i, primOpCalls_)
|
||||
printMsg(v, format("%1$10d %2%") % i->first % i->second);
|
||||
|
||||
printMsg(v, format("calls to %1% functions:") % functionCalls.size());
|
||||
typedef std::multimap<unsigned int, ExprLambda *> FunctionCalls_;
|
||||
FunctionCalls_ functionCalls_;
|
||||
foreach (FunctionCalls::iterator, i, functionCalls)
|
||||
functionCalls_.insert(std::pair<unsigned int, ExprLambda *>(i->second, i->first));
|
||||
foreach_reverse (FunctionCalls_::reverse_iterator, i, functionCalls_)
|
||||
printMsg(v, format("%1$10d %2%") % i->first % i->second->showNamePos());
|
||||
|
||||
printMsg(v, format("evaluations of %1% attributes:") % attrSelects.size());
|
||||
typedef std::multimap<unsigned int, Pos> AttrSelects_;
|
||||
AttrSelects_ attrSelects_;
|
||||
foreach (AttrSelects::iterator, i, attrSelects)
|
||||
attrSelects_.insert(std::pair<unsigned int, Pos>(i->second, i->first));
|
||||
foreach_reverse (AttrSelects_::reverse_iterator, i, attrSelects_)
|
||||
printMsg(v, format("%1$10d %2%") % i->first % i->second);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
+60
-30
@@ -52,7 +52,8 @@ struct PrimOp
|
||||
struct Env
|
||||
{
|
||||
Env * up;
|
||||
unsigned int prevWith; // nr of levels up to next `with' environment
|
||||
unsigned short prevWith; // nr of levels up to next `with' environment
|
||||
bool haveWithAttrs;
|
||||
Value * values[0];
|
||||
};
|
||||
|
||||
@@ -87,19 +88,20 @@ struct EvalState;
|
||||
std::ostream & operator << (std::ostream & str, const Value & v);
|
||||
|
||||
|
||||
class EvalState
|
||||
class EvalState
|
||||
{
|
||||
public:
|
||||
SymbolTable symbols;
|
||||
|
||||
const Symbol sWith, sOutPath, sDrvPath, sType, sMeta, sName,
|
||||
sSystem, sOverrides;
|
||||
sSystem, sOverrides, sOutputs, sOutputName, sIgnoreNulls;
|
||||
|
||||
/* If set, force copying files to the Nix store even if they
|
||||
already exist there. */
|
||||
bool repair;
|
||||
|
||||
private:
|
||||
SrcToStore srcToStore;
|
||||
|
||||
/* A cache from path names to parse trees. */
|
||||
std::map<Path, Expr *> parseTrees;
|
||||
SrcToStore srcToStore;
|
||||
|
||||
/* A cache from path names to values. */
|
||||
#if HAVE_BOEHMGC
|
||||
@@ -114,23 +116,25 @@ private:
|
||||
SearchPath::iterator searchPathInsertionPoint;
|
||||
|
||||
public:
|
||||
|
||||
|
||||
EvalState();
|
||||
~EvalState();
|
||||
|
||||
void addToSearchPath(const string & s);
|
||||
|
||||
/* Parse a Nix expression from the specified file. If `path'
|
||||
refers to a directory, then "/default.nix" is appended. */
|
||||
Expr * parseExprFromFile(Path path);
|
||||
/* Parse a Nix expression from the specified file. */
|
||||
Expr * parseExprFromFile(const Path & path);
|
||||
|
||||
/* Parse a Nix expression from the specified string. */
|
||||
Expr * parseExprFromString(const string & s, const Path & basePath, StaticEnv & staticEnv);
|
||||
Expr * parseExprFromString(const string & s, const Path & basePath);
|
||||
|
||||
|
||||
/* Evaluate an expression read from the given file to normal
|
||||
form. */
|
||||
void evalFile(const Path & path, Value & v);
|
||||
|
||||
void resetFileCache();
|
||||
|
||||
/* Look up a file in the search path. */
|
||||
Path findFile(const string & path);
|
||||
|
||||
@@ -154,7 +158,7 @@ public:
|
||||
void strictForceValue(Value & v);
|
||||
|
||||
/* Force `v', and then verify that it has the expected type. */
|
||||
int forceInt(Value & v);
|
||||
NixInt forceInt(Value & v);
|
||||
bool forceBool(Value & v);
|
||||
inline void forceAttrs(Value & v);
|
||||
inline void forceList(Value & v);
|
||||
@@ -170,7 +174,7 @@ public:
|
||||
/* String coercion. Converts strings, paths and derivations to a
|
||||
string. If `coerceMore' is set, also converts nulls, integers,
|
||||
booleans and lists to a string. If `copyToStore' is set,
|
||||
referenced paths are copied to the Nix store as a side effect.q */
|
||||
referenced paths are copied to the Nix store as a side effect. */
|
||||
string coerceToString(Value & v, PathSet & context,
|
||||
bool coerceMore = false, bool copyToStore = true);
|
||||
|
||||
@@ -179,39 +183,43 @@ public:
|
||||
path. Nothing is copied to the store. */
|
||||
Path coerceToPath(Value & v, PathSet & context);
|
||||
|
||||
private:
|
||||
public:
|
||||
|
||||
/* The base environment, containing the builtin functions and
|
||||
values. */
|
||||
Env & baseEnv;
|
||||
|
||||
unsigned int baseEnvDispl;
|
||||
|
||||
public:
|
||||
|
||||
/* The same, but used during parsing to resolve variables. */
|
||||
StaticEnv staticBaseEnv; // !!! should be private
|
||||
|
||||
private:
|
||||
|
||||
|
||||
unsigned int baseEnvDispl;
|
||||
|
||||
void createBaseEnv();
|
||||
|
||||
|
||||
void addConstant(const string & name, Value & v);
|
||||
|
||||
void addPrimOp(const string & name,
|
||||
unsigned int arity, PrimOpFun primOp);
|
||||
|
||||
inline Value * lookupVar(Env * env, const VarRef & var);
|
||||
|
||||
public:
|
||||
|
||||
void getBuiltin(const string & name, Value & v);
|
||||
|
||||
private:
|
||||
|
||||
inline Value * lookupVar(Env * env, const VarRef & var, bool noEval);
|
||||
|
||||
friend class ExprVar;
|
||||
friend class ExprAttrs;
|
||||
friend class ExprLet;
|
||||
|
||||
Expr * parse(const char * text,
|
||||
const Path & path, const Path & basePath);
|
||||
Expr * parse(const char * text, const Path & path,
|
||||
const Path & basePath, StaticEnv & staticEnv);
|
||||
|
||||
public:
|
||||
|
||||
|
||||
/* Do a deep equality test between two values. That is, list
|
||||
elements and attributes are compared recursively. */
|
||||
bool eqValues(Value & v1, Value & v2);
|
||||
@@ -221,7 +229,7 @@ public:
|
||||
/* Automatically call a function for which each argument has a
|
||||
default value or has a binding in the `args' map. */
|
||||
void autoCallFunction(Bindings & args, Value & fun, Value & res);
|
||||
|
||||
|
||||
/* Allocation primitives. */
|
||||
Value * allocValue();
|
||||
Env & allocEnv(unsigned int size);
|
||||
@@ -232,11 +240,13 @@ public:
|
||||
void mkAttrs(Value & v, unsigned int expected);
|
||||
void mkThunk_(Value & v, Expr * expr);
|
||||
|
||||
void concatLists(Value & v, unsigned int nrLists, Value * * lists);
|
||||
|
||||
/* Print statistics. */
|
||||
void printStats();
|
||||
|
||||
private:
|
||||
|
||||
|
||||
unsigned long nrEnvs;
|
||||
unsigned long nrValuesInEnvs;
|
||||
unsigned long nrValues;
|
||||
@@ -244,9 +254,25 @@ private:
|
||||
unsigned long nrAttrsets;
|
||||
unsigned long nrOpUpdates;
|
||||
unsigned long nrOpUpdateValuesCopied;
|
||||
|
||||
friend class RecursionCounter;
|
||||
unsigned long nrListConcats;
|
||||
unsigned long nrPrimOpCalls;
|
||||
unsigned long nrFunctionCalls;
|
||||
|
||||
bool countCalls;
|
||||
|
||||
typedef std::map<Symbol, unsigned int> PrimOpCalls;
|
||||
PrimOpCalls primOpCalls;
|
||||
|
||||
typedef std::map<ExprLambda *, unsigned int> FunctionCalls;
|
||||
FunctionCalls functionCalls;
|
||||
|
||||
typedef std::map<Pos, unsigned int> AttrSelects;
|
||||
AttrSelects attrSelects;
|
||||
|
||||
friend class ExprOpUpdate;
|
||||
friend class ExprOpConcatLists;
|
||||
friend class ExprSelect;
|
||||
friend void prim_getAttr(EvalState & state, Value * * args, Value & v);
|
||||
};
|
||||
|
||||
|
||||
@@ -254,4 +280,8 @@ private:
|
||||
string showType(const Value & v);
|
||||
|
||||
|
||||
/* If `path' refers to a directory, then append "/default.nix". */
|
||||
Path resolveExprPath(Path path);
|
||||
|
||||
|
||||
}
|
||||
|
||||
+64
-19
@@ -28,12 +28,53 @@ string DrvInfo::queryOutPath(EvalState & state) const
|
||||
}
|
||||
|
||||
|
||||
DrvInfo::Outputs DrvInfo::queryOutputs(EvalState & state)
|
||||
{
|
||||
if (outputs.empty()) {
|
||||
/* Get the ‘outputs’ list. */
|
||||
Bindings::iterator i = attrs->find(state.sOutputs);
|
||||
|
||||
if (i == attrs->end())
|
||||
outputs["out"] = queryOutPath(state);
|
||||
else {
|
||||
state.forceList(*i->value);
|
||||
|
||||
/* For each output... */
|
||||
for (unsigned int j = 0; j < i->value->list.length; ++j) {
|
||||
/* Evaluate the corresponding attribute set. */
|
||||
string name = state.forceStringNoCtx(*i->value->list.elems[j]);
|
||||
Bindings::iterator out = attrs->find(state.symbols.create(name));
|
||||
if (out == attrs->end()) continue; // FIXME: throw error?
|
||||
state.forceAttrs(*out->value);
|
||||
|
||||
/* And evaluate its ‘outPath’ attribute. */
|
||||
Bindings::iterator outPath = out->value->attrs->find(state.sOutPath);
|
||||
if (outPath == out->value->attrs->end()) continue; // FIXME: throw error?
|
||||
PathSet context;
|
||||
outputs[name] = state.coerceToPath(*outPath->value, context);
|
||||
}
|
||||
}
|
||||
}
|
||||
return outputs;
|
||||
}
|
||||
|
||||
|
||||
string DrvInfo::queryOutputName(EvalState & state) const
|
||||
{
|
||||
if (outputName == "" && attrs) {
|
||||
Bindings::iterator i = attrs->find(state.sOutputName);
|
||||
(string &) outputName = i != attrs->end() ? state.forceStringNoCtx(*i->value) : "";
|
||||
}
|
||||
return outputName;
|
||||
}
|
||||
|
||||
|
||||
MetaInfo DrvInfo::queryMetaInfo(EvalState & state) const
|
||||
{
|
||||
if (metaInfoRead) return meta;
|
||||
|
||||
|
||||
(bool &) metaInfoRead = true;
|
||||
|
||||
|
||||
Bindings::iterator a = attrs->find(state.sMeta);
|
||||
if (a == attrs->end()) return meta; /* fine, empty meta information */
|
||||
|
||||
@@ -84,7 +125,8 @@ typedef set<Bindings *> Done;
|
||||
makes sense for the caller to recursively search for derivations in
|
||||
`v'. */
|
||||
static bool getDerivation(EvalState & state, Value & v,
|
||||
const string & attrPath, DrvInfos & drvs, Done & done)
|
||||
const string & attrPath, DrvInfos & drvs, Done & done,
|
||||
bool ignoreAssertionFailures)
|
||||
{
|
||||
try {
|
||||
state.forceValue(v);
|
||||
@@ -96,7 +138,7 @@ static bool getDerivation(EvalState & state, Value & v,
|
||||
done.insert(v.attrs);
|
||||
|
||||
DrvInfo drv;
|
||||
|
||||
|
||||
Bindings::iterator i = v.attrs->find(state.sName);
|
||||
/* !!! We really would like to have a decent back trace here. */
|
||||
if (i == v.attrs->end()) throw TypeError("derivation name missing");
|
||||
@@ -114,18 +156,20 @@ static bool getDerivation(EvalState & state, Value & v,
|
||||
|
||||
drvs.push_back(drv);
|
||||
return false;
|
||||
|
||||
|
||||
} catch (AssertionError & e) {
|
||||
return false;
|
||||
if (ignoreAssertionFailures) return false;
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
bool getDerivation(EvalState & state, Value & v, DrvInfo & drv)
|
||||
bool getDerivation(EvalState & state, Value & v, DrvInfo & drv,
|
||||
bool ignoreAssertionFailures)
|
||||
{
|
||||
Done done;
|
||||
DrvInfos drvs;
|
||||
getDerivation(state, v, "", drvs, done);
|
||||
getDerivation(state, v, "", drvs, done, ignoreAssertionFailures);
|
||||
if (drvs.size() != 1) return false;
|
||||
drv = drvs.front();
|
||||
return true;
|
||||
@@ -140,15 +184,16 @@ static string addToPath(const string & s1, const string & s2)
|
||||
|
||||
static void getDerivations(EvalState & state, Value & vIn,
|
||||
const string & pathPrefix, Bindings & autoArgs,
|
||||
DrvInfos & drvs, Done & done)
|
||||
DrvInfos & drvs, Done & done,
|
||||
bool ignoreAssertionFailures)
|
||||
{
|
||||
Value v;
|
||||
state.autoCallFunction(autoArgs, vIn, v);
|
||||
|
||||
|
||||
/* Process the expression. */
|
||||
DrvInfo drv;
|
||||
|
||||
if (!getDerivation(state, v, pathPrefix, drvs, done)) ;
|
||||
if (!getDerivation(state, v, pathPrefix, drvs, done, ignoreAssertionFailures)) ;
|
||||
|
||||
else if (v.type == tAttrs) {
|
||||
|
||||
@@ -171,8 +216,8 @@ static void getDerivations(EvalState & state, Value & vIn,
|
||||
string pathPrefix2 = addToPath(pathPrefix, i->first);
|
||||
Value & v2(*v.attrs->find(i->second)->value);
|
||||
if (combineChannels)
|
||||
getDerivations(state, v2, pathPrefix2, autoArgs, drvs, done);
|
||||
else if (getDerivation(state, v2, pathPrefix2, drvs, done)) {
|
||||
getDerivations(state, v2, pathPrefix2, autoArgs, drvs, done, ignoreAssertionFailures);
|
||||
else if (getDerivation(state, v2, pathPrefix2, drvs, done, ignoreAssertionFailures)) {
|
||||
/* If the value of this attribute is itself an
|
||||
attribute set, should we recurse into it? => Only
|
||||
if it has a `recurseForDerivations = true'
|
||||
@@ -180,7 +225,7 @@ static void getDerivations(EvalState & state, Value & vIn,
|
||||
if (v2.type == tAttrs) {
|
||||
Bindings::iterator j = v2.attrs->find(state.symbols.create("recurseForDerivations"));
|
||||
if (j != v2.attrs->end() && state.forceBool(*j->value))
|
||||
getDerivations(state, v2, pathPrefix2, autoArgs, drvs, done);
|
||||
getDerivations(state, v2, pathPrefix2, autoArgs, drvs, done, ignoreAssertionFailures);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -191,8 +236,8 @@ static void getDerivations(EvalState & state, Value & vIn,
|
||||
startNest(nest, lvlDebug,
|
||||
format("evaluating list element"));
|
||||
string pathPrefix2 = addToPath(pathPrefix, (format("%1%") % n).str());
|
||||
if (getDerivation(state, *v.list.elems[n], pathPrefix2, drvs, done))
|
||||
getDerivations(state, *v.list.elems[n], pathPrefix2, autoArgs, drvs, done);
|
||||
if (getDerivation(state, *v.list.elems[n], pathPrefix2, drvs, done, ignoreAssertionFailures))
|
||||
getDerivations(state, *v.list.elems[n], pathPrefix2, autoArgs, drvs, done, ignoreAssertionFailures);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -201,11 +246,11 @@ static void getDerivations(EvalState & state, Value & vIn,
|
||||
|
||||
|
||||
void getDerivations(EvalState & state, Value & v, const string & pathPrefix,
|
||||
Bindings & autoArgs, DrvInfos & drvs)
|
||||
Bindings & autoArgs, DrvInfos & drvs, bool ignoreAssertionFailures)
|
||||
{
|
||||
Done done;
|
||||
getDerivations(state, v, pathPrefix, autoArgs, drvs, done);
|
||||
getDerivations(state, v, pathPrefix, autoArgs, drvs, done, ignoreAssertionFailures);
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
+20
-6
@@ -25,13 +25,20 @@ typedef std::map<string, MetaValue> MetaInfo;
|
||||
|
||||
struct DrvInfo
|
||||
{
|
||||
public:
|
||||
typedef std::map<string, Path> Outputs;
|
||||
|
||||
private:
|
||||
string drvPath;
|
||||
string outPath;
|
||||
string outputName;
|
||||
Outputs outputs;
|
||||
|
||||
bool metaInfoRead;
|
||||
MetaInfo meta;
|
||||
|
||||
|
||||
bool failed; // set if we get an AssertionError
|
||||
|
||||
public:
|
||||
string name;
|
||||
string attrPath; /* path towards the derivation */
|
||||
@@ -40,10 +47,12 @@ public:
|
||||
/* !!! make this private */
|
||||
Bindings * attrs;
|
||||
|
||||
DrvInfo() : metaInfoRead(false), attrs(0) { };
|
||||
DrvInfo() : metaInfoRead(false), failed(false), attrs(0) { };
|
||||
|
||||
string queryDrvPath(EvalState & state) const;
|
||||
string queryOutPath(EvalState & state) const;
|
||||
string queryOutputName(EvalState & state) const;
|
||||
Outputs queryOutputs(EvalState & state);
|
||||
MetaInfo queryMetaInfo(EvalState & state) const;
|
||||
MetaValue queryMetaInfo(EvalState & state, const string & name) const;
|
||||
|
||||
@@ -51,13 +60,16 @@ public:
|
||||
{
|
||||
drvPath = s;
|
||||
}
|
||||
|
||||
|
||||
void setOutPath(const string & s)
|
||||
{
|
||||
outPath = s;
|
||||
}
|
||||
|
||||
void setMetaInfo(const MetaInfo & meta);
|
||||
|
||||
void setFailed() { failed = true; };
|
||||
bool hasFailed() { return failed; };
|
||||
};
|
||||
|
||||
|
||||
@@ -70,10 +82,12 @@ typedef list<DrvInfo> DrvInfos;
|
||||
|
||||
/* If value `v' denotes a derivation, store information about the
|
||||
derivation in `drv' and return true. Otherwise, return false. */
|
||||
bool getDerivation(EvalState & state, Value & v, DrvInfo & drv);
|
||||
bool getDerivation(EvalState & state, Value & v, DrvInfo & drv,
|
||||
bool ignoreAssertionFailures);
|
||||
|
||||
void getDerivations(EvalState & state, Value & v, const string & pathPrefix,
|
||||
Bindings & autoArgs, DrvInfos & drvs);
|
||||
Bindings & autoArgs, DrvInfos & drvs,
|
||||
bool ignoreAssertionFailures);
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
+12
-9
@@ -9,21 +9,20 @@
|
||||
|
||||
%{
|
||||
#include "nixexpr.hh"
|
||||
#define BISON_HEADER_HACK
|
||||
#include "parser-tab.hh"
|
||||
|
||||
using namespace nix;
|
||||
|
||||
namespace nix {
|
||||
|
||||
|
||||
|
||||
static void initLoc(YYLTYPE * loc)
|
||||
{
|
||||
loc->first_line = loc->last_line = 1;
|
||||
loc->first_column = loc->last_column = 1;
|
||||
}
|
||||
|
||||
|
||||
|
||||
static void adjustLoc(YYLTYPE * loc, const char * s, size_t len)
|
||||
{
|
||||
loc->first_line = loc->last_line;
|
||||
@@ -35,7 +34,7 @@ static void adjustLoc(YYLTYPE * loc, const char * s, size_t len)
|
||||
if (*s == '\n') /* cr/lf */
|
||||
s++;
|
||||
/* fall through */
|
||||
case '\n':
|
||||
case '\n':
|
||||
++loc->last_line;
|
||||
loc->last_column = 1;
|
||||
break;
|
||||
@@ -69,7 +68,7 @@ static Expr * unescapeStr(SymbolTable & symbols, const char * s)
|
||||
return new ExprString(symbols.create(t));
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
#define YY_USER_INIT initLoc(yylloc)
|
||||
@@ -78,7 +77,7 @@ static Expr * unescapeStr(SymbolTable & symbols, const char * s)
|
||||
%}
|
||||
|
||||
|
||||
ID [a-zA-Z\_][a-zA-Z0-9\_\']*
|
||||
ID [a-zA-Z\_][a-zA-Z0-9\_\'\-]*
|
||||
INT [0-9]+
|
||||
PATH [a-zA-Z0-9\.\_\-\+]*(\/[a-zA-Z0-9\.\_\-\+]+)+
|
||||
SPATH \<[a-zA-Z0-9\.\_\-\+]+(\/[a-zA-Z0-9\.\_\-\+]+)*\>
|
||||
@@ -102,6 +101,8 @@ or { return OR_KW; }
|
||||
|
||||
\=\= { return EQ; }
|
||||
\!\= { return NEQ; }
|
||||
\<\= { return LEQ; }
|
||||
\>\= { return GEQ; }
|
||||
\&\& { return AND; }
|
||||
\|\| { return OR; }
|
||||
\-\> { return IMPL; }
|
||||
@@ -109,8 +110,10 @@ or { return OR_KW; }
|
||||
\+\+ { return CONCAT; }
|
||||
|
||||
{ID} { yylval->id = strdup(yytext); return ID; }
|
||||
{INT} { int n = atoi(yytext); /* !!! overflow */
|
||||
yylval->n = n;
|
||||
{INT} { errno = 0;
|
||||
yylval->n = strtol(yytext, 0, 10);
|
||||
if (errno != 0)
|
||||
throw ParseError(format("invalid integer `%1%'") % yytext);
|
||||
return INT;
|
||||
}
|
||||
|
||||
@@ -168,7 +171,7 @@ or { return OR_KW; }
|
||||
|
||||
|
||||
namespace nix {
|
||||
|
||||
|
||||
/* Horrible, disgusting hack: allow the parser to set the scanner
|
||||
start condition back to STRING. Necessary in interpolations like
|
||||
"foo${expr}bar"; after the close brace we have to go back to the
|
||||
|
||||
@@ -55,7 +55,7 @@ static string nextComponent(string::const_iterator & p,
|
||||
else
|
||||
while (p != end && (!isdigit(*p) && *p != '.' && *p != '-'))
|
||||
s += *p++;
|
||||
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
@@ -80,7 +80,7 @@ int compareVersions(const string & v1, const string & v2)
|
||||
{
|
||||
string::const_iterator p1 = v1.begin();
|
||||
string::const_iterator p2 = v2.begin();
|
||||
|
||||
|
||||
while (p1 != v1.end() || p2 != v2.end()) {
|
||||
string c1 = nextComponent(p1, v1.end());
|
||||
string c2 = nextComponent(p2, v2.end());
|
||||
@@ -95,11 +95,10 @@ int compareVersions(const string & v1, const string & v2)
|
||||
DrvNames drvNamesFromArgs(const Strings & opArgs)
|
||||
{
|
||||
DrvNames result;
|
||||
for (Strings::const_iterator i = opArgs.begin();
|
||||
i != opArgs.end(); ++i)
|
||||
foreach (Strings::const_iterator, i, opArgs)
|
||||
result.push_back(DrvName(*i));
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
+32
-15
@@ -224,20 +224,18 @@ void ExprAttrs::bindVars(const StaticEnv & env)
|
||||
{
|
||||
if (recursive) {
|
||||
StaticEnv newEnv(false, &env);
|
||||
|
||||
|
||||
unsigned int displ = 0;
|
||||
foreach (AttrDefs::iterator, i, attrs)
|
||||
newEnv.vars[i->first] = i->second.displ = displ++;
|
||||
|
||||
|
||||
foreach (AttrDefs::iterator, i, attrs)
|
||||
if (i->second.inherited) i->second.var.bind(env);
|
||||
else i->second.e->bindVars(newEnv);
|
||||
i->second.e->bindVars(i->second.inherited ? env : newEnv);
|
||||
}
|
||||
|
||||
else
|
||||
foreach (AttrDefs::iterator, i, attrs)
|
||||
if (i->second.inherited) i->second.var.bind(env);
|
||||
else i->second.e->bindVars(env);
|
||||
i->second.e->bindVars(env);
|
||||
}
|
||||
|
||||
void ExprList::bindVars(const StaticEnv & env)
|
||||
@@ -249,9 +247,9 @@ void ExprList::bindVars(const StaticEnv & env)
|
||||
void ExprLambda::bindVars(const StaticEnv & env)
|
||||
{
|
||||
StaticEnv newEnv(false, &env);
|
||||
|
||||
|
||||
unsigned int displ = 0;
|
||||
|
||||
|
||||
if (!arg.empty()) newEnv.vars[arg] = displ++;
|
||||
|
||||
if (matchAttrs) {
|
||||
@@ -268,15 +266,14 @@ void ExprLambda::bindVars(const StaticEnv & env)
|
||||
void ExprLet::bindVars(const StaticEnv & env)
|
||||
{
|
||||
StaticEnv newEnv(false, &env);
|
||||
|
||||
|
||||
unsigned int displ = 0;
|
||||
foreach (ExprAttrs::AttrDefs::iterator, i, attrs->attrs)
|
||||
newEnv.vars[i->first] = i->second.displ = displ++;
|
||||
|
||||
|
||||
foreach (ExprAttrs::AttrDefs::iterator, i, attrs->attrs)
|
||||
if (i->second.inherited) i->second.var.bind(env);
|
||||
else i->second.e->bindVars(newEnv);
|
||||
|
||||
i->second.e->bindVars(i->second.inherited ? env : newEnv);
|
||||
|
||||
body->bindVars(newEnv);
|
||||
}
|
||||
|
||||
@@ -293,8 +290,8 @@ void ExprWith::bindVars(const StaticEnv & env)
|
||||
prevWith = level;
|
||||
break;
|
||||
}
|
||||
|
||||
attrs->bindVars(env);
|
||||
|
||||
attrs->bindVars(env);
|
||||
StaticEnv newEnv(true, &env);
|
||||
body->bindVars(newEnv);
|
||||
}
|
||||
@@ -324,4 +321,24 @@ void ExprConcatStrings::bindVars(const StaticEnv & env)
|
||||
}
|
||||
|
||||
|
||||
/* Storing function names. */
|
||||
|
||||
void Expr::setName(Symbol & name)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
void ExprLambda::setName(Symbol & name)
|
||||
{
|
||||
this->name = name;
|
||||
body->setName(name);
|
||||
}
|
||||
|
||||
|
||||
string ExprLambda::showNamePos()
|
||||
{
|
||||
return (format("%1% at %2%") % (name.set() ? "`" + (string) name + "'" : "an anonymous function") % pos).str();
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
+21
-8
@@ -27,6 +27,15 @@ struct Pos
|
||||
Pos() : line(0), column(0) { };
|
||||
Pos(const string & file, unsigned int line, unsigned int column)
|
||||
: file(file), line(line), column(column) { };
|
||||
bool operator < (const Pos & p2) const
|
||||
{
|
||||
int d = file.compare(p2.file);
|
||||
if (d < 0) return true;
|
||||
if (d > 0) return false;
|
||||
if (line < p2.line) return true;
|
||||
if (line > p2.line) return false;
|
||||
return column < p2.column;
|
||||
}
|
||||
};
|
||||
|
||||
extern Pos noPos;
|
||||
@@ -54,6 +63,7 @@ struct Expr
|
||||
virtual void bindVars(const StaticEnv & env);
|
||||
virtual void eval(EvalState & state, Env & env, Value & v);
|
||||
virtual Value * maybeThunk(EvalState & state, Env & env);
|
||||
virtual void setName(Symbol & name);
|
||||
};
|
||||
|
||||
std::ostream & operator << (std::ostream & str, Expr & e);
|
||||
@@ -65,9 +75,9 @@ std::ostream & operator << (std::ostream & str, Expr & e);
|
||||
|
||||
struct ExprInt : Expr
|
||||
{
|
||||
int n;
|
||||
NixInt n;
|
||||
Value v;
|
||||
ExprInt(int n) : n(n) { mkInt(v, n); };
|
||||
ExprInt(NixInt n) : n(n) { mkInt(v, n); };
|
||||
COMMON_METHODS
|
||||
Value * maybeThunk(EvalState & state, Env & env);
|
||||
};
|
||||
@@ -104,7 +114,7 @@ struct VarRef
|
||||
/* Whether the variable comes from an environment (e.g. a rec, let
|
||||
or function argument) or from a "with". */
|
||||
bool fromWith;
|
||||
|
||||
|
||||
/* In the former case, the value is obtained by going `level'
|
||||
levels up from the current environment and getting the
|
||||
`displ'th value in that environment. In the latter case, the
|
||||
@@ -149,12 +159,10 @@ struct ExprAttrs : Expr
|
||||
bool recursive;
|
||||
struct AttrDef {
|
||||
bool inherited;
|
||||
Expr * e; // if not inherited
|
||||
VarRef var; // if inherited
|
||||
Expr * e;
|
||||
Pos pos;
|
||||
unsigned int displ; // displacement
|
||||
AttrDef(Expr * e, const Pos & pos) : inherited(false), e(e), pos(pos) { };
|
||||
AttrDef(const Symbol & name, const Pos & pos) : inherited(true), var(name), pos(pos) { };
|
||||
AttrDef(Expr * e, const Pos & pos, bool inherited=false) : inherited(inherited), e(e), pos(pos) { };
|
||||
AttrDef() { };
|
||||
};
|
||||
typedef std::map<Symbol, AttrDef> AttrDefs;
|
||||
@@ -188,6 +196,7 @@ struct Formals
|
||||
struct ExprLambda : Expr
|
||||
{
|
||||
Pos pos;
|
||||
Symbol name;
|
||||
Symbol arg;
|
||||
bool matchAttrs;
|
||||
Formals * formals;
|
||||
@@ -199,6 +208,8 @@ struct ExprLambda : Expr
|
||||
throw ParseError(format("duplicate formal function argument `%1%' at %2%")
|
||||
% arg % pos);
|
||||
};
|
||||
void setName(Symbol & name);
|
||||
string showNamePos();
|
||||
COMMON_METHODS
|
||||
};
|
||||
|
||||
@@ -268,8 +279,10 @@ MakeBinOp(OpConcatLists, "++")
|
||||
|
||||
struct ExprConcatStrings : Expr
|
||||
{
|
||||
bool forceString;
|
||||
vector<Expr *> * es;
|
||||
ExprConcatStrings(vector<Expr *> * es) : es(es) { };
|
||||
ExprConcatStrings(bool forceString, vector<Expr *> * es)
|
||||
: forceString(forceString), es(es) { };
|
||||
COMMON_METHODS
|
||||
};
|
||||
|
||||
|
||||
+57
-42
@@ -4,26 +4,26 @@
|
||||
%error-verbose
|
||||
%defines
|
||||
/* %no-lines */
|
||||
%parse-param { yyscan_t scanner }
|
||||
%parse-param { ParseData * data }
|
||||
%lex-param { yyscan_t scanner }
|
||||
%lex-param { ParseData * data }
|
||||
%parse-param { void * scanner }
|
||||
%parse-param { nix::ParseData * data }
|
||||
%lex-param { void * scanner }
|
||||
%lex-param { nix::ParseData * data }
|
||||
%expect 1
|
||||
%expect-rr 1
|
||||
|
||||
%code requires {
|
||||
|
||||
|
||||
#ifndef BISON_HEADER
|
||||
#define BISON_HEADER
|
||||
|
||||
|
||||
#include "util.hh"
|
||||
|
||||
|
||||
#include "nixexpr.hh"
|
||||
#include "eval.hh"
|
||||
|
||||
namespace nix {
|
||||
|
||||
struct ParseData
|
||||
struct ParseData
|
||||
{
|
||||
EvalState & state;
|
||||
SymbolTable & symbols;
|
||||
@@ -38,7 +38,7 @@ namespace nix {
|
||||
, sLetBody(symbols.create("<let-body>"))
|
||||
{ };
|
||||
};
|
||||
|
||||
|
||||
}
|
||||
|
||||
#define YY_DECL int yylex \
|
||||
@@ -52,7 +52,6 @@ namespace nix {
|
||||
|
||||
#include "parser-tab.hh"
|
||||
#include "lexer-tab.hh"
|
||||
#define YYSTYPE YYSTYPE // workaround a bug in Bison 2.4
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
@@ -64,14 +63,14 @@ using namespace nix;
|
||||
|
||||
|
||||
namespace nix {
|
||||
|
||||
|
||||
|
||||
static void dupAttr(const AttrPath & attrPath, const Pos & pos, const Pos & prevPos)
|
||||
{
|
||||
throw ParseError(format("attribute `%1%' at %2% already defined at %3%")
|
||||
% showAttrPath(attrPath) % pos % prevPos);
|
||||
}
|
||||
|
||||
|
||||
|
||||
static void dupAttr(Symbol attr, const Pos & pos, const Pos & prevPos)
|
||||
{
|
||||
@@ -79,7 +78,7 @@ static void dupAttr(Symbol attr, const Pos & pos, const Pos & prevPos)
|
||||
throw ParseError(format("attribute `%1%' at %2% already defined at %3%")
|
||||
% showAttrPath(attrPath) % pos % prevPos);
|
||||
}
|
||||
|
||||
|
||||
|
||||
static void addAttr(ExprAttrs * attrs, AttrPath & attrPath,
|
||||
Expr * e, const Pos & pos)
|
||||
@@ -105,6 +104,7 @@ static void addAttr(ExprAttrs * attrs, AttrPath & attrPath,
|
||||
}
|
||||
}
|
||||
}
|
||||
e->setName(attrPath.back());
|
||||
}
|
||||
|
||||
|
||||
@@ -121,7 +121,7 @@ static void addFormal(const Pos & pos, Formals * formals, const Formal & formal)
|
||||
static Expr * stripIndentation(SymbolTable & symbols, vector<Expr *> & es)
|
||||
{
|
||||
if (es.empty()) return new ExprString(symbols.create(""));
|
||||
|
||||
|
||||
/* Figure out the minimum indentation. Note that by design
|
||||
whitespace-only final lines are not taken into account. (So
|
||||
the " " in "\n ''" is ignored, but the " " in "\n foo''" is.) */
|
||||
@@ -170,7 +170,7 @@ static Expr * stripIndentation(SymbolTable & symbols, vector<Expr *> & es)
|
||||
es2->push_back(*i);
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
string s2;
|
||||
for (unsigned int j = 0; j < e->s.size(); ++j) {
|
||||
if (atStartOfLine) {
|
||||
@@ -203,7 +203,7 @@ static Expr * stripIndentation(SymbolTable & symbols, vector<Expr *> & es)
|
||||
es2->push_back(new ExprString(symbols.create(s2)));
|
||||
}
|
||||
|
||||
return new ExprConcatStrings(es2);
|
||||
return es2->size() == 1 ? (*es2)[0] : new ExprConcatStrings(true, es2);
|
||||
}
|
||||
|
||||
|
||||
@@ -232,13 +232,13 @@ void yyerror(YYLTYPE * loc, yyscan_t scanner, ParseData * data, const char * err
|
||||
%}
|
||||
|
||||
%union {
|
||||
// !!! We're probably leaking stuff here.
|
||||
// !!! We're probably leaking stuff here.
|
||||
nix::Expr * e;
|
||||
nix::ExprList * list;
|
||||
nix::ExprAttrs * attrs;
|
||||
nix::Formals * formals;
|
||||
nix::Formal * formal;
|
||||
int n;
|
||||
nix::NixInt n;
|
||||
char * id; // !!! -> Symbol
|
||||
char * path;
|
||||
char * uri;
|
||||
@@ -269,12 +269,15 @@ void yyerror(YYLTYPE * loc, yyscan_t scanner, ParseData * data, const char * err
|
||||
%left OR
|
||||
%left AND
|
||||
%nonassoc EQ NEQ
|
||||
%left '<' '>' LEQ GEQ
|
||||
%right UPDATE
|
||||
%left NEG
|
||||
%left '+'
|
||||
%left NOT
|
||||
%left '+' '-'
|
||||
%left '*' '/'
|
||||
%right CONCAT
|
||||
%nonassoc '?'
|
||||
%nonassoc '~'
|
||||
%nonassoc NEGATE
|
||||
|
||||
%%
|
||||
|
||||
@@ -306,9 +309,14 @@ expr_if
|
||||
;
|
||||
|
||||
expr_op
|
||||
: '!' expr_op %prec NEG { $$ = new ExprOpNot($2); }
|
||||
: '!' expr_op %prec NOT { $$ = new ExprOpNot($2); }
|
||||
| '-' expr_op %prec NEGATE { $$ = new ExprApp(new ExprApp(new ExprVar(data->symbols.create("__sub")), new ExprInt(0)), $2); }
|
||||
| expr_op EQ expr_op { $$ = new ExprOpEq($1, $3); }
|
||||
| expr_op NEQ expr_op { $$ = new ExprOpNEq($1, $3); }
|
||||
| expr_op '<' expr_op { $$ = new ExprApp(new ExprApp(new ExprVar(data->symbols.create("__lessThan")), $1), $3); }
|
||||
| expr_op LEQ expr_op { $$ = new ExprOpNot(new ExprApp(new ExprApp(new ExprVar(data->symbols.create("__lessThan")), $3), $1)); }
|
||||
| expr_op '>' expr_op { $$ = new ExprApp(new ExprApp(new ExprVar(data->symbols.create("__lessThan")), $3), $1); }
|
||||
| expr_op GEQ expr_op { $$ = new ExprOpNot(new ExprApp(new ExprApp(new ExprVar(data->symbols.create("__lessThan")), $1), $3)); }
|
||||
| expr_op AND expr_op { $$ = new ExprOpAnd($1, $3); }
|
||||
| expr_op OR expr_op { $$ = new ExprOpOr($1, $3); }
|
||||
| expr_op IMPL expr_op { $$ = new ExprOpImpl($1, $3); }
|
||||
@@ -318,8 +326,11 @@ expr_op
|
||||
{ vector<Expr *> * l = new vector<Expr *>;
|
||||
l->push_back($1);
|
||||
l->push_back($3);
|
||||
$$ = new ExprConcatStrings(l);
|
||||
$$ = new ExprConcatStrings(false, l);
|
||||
}
|
||||
| expr_op '-' expr_op { $$ = new ExprApp(new ExprApp(new ExprVar(data->symbols.create("__sub")), $1), $3); }
|
||||
| expr_op '*' expr_op { $$ = new ExprApp(new ExprApp(new ExprVar(data->symbols.create("__mul")), $1), $3); }
|
||||
| expr_op '/' expr_op { $$ = new ExprApp(new ExprApp(new ExprVar(data->symbols.create("__div")), $1), $3); }
|
||||
| expr_op CONCAT expr_op { $$ = new ExprOpConcatLists($1, $3); }
|
||||
| expr_app
|
||||
;
|
||||
@@ -349,7 +360,7 @@ expr_simple
|
||||
/* For efficiency, and to simplify parse trees a bit. */
|
||||
if ($2->empty()) $$ = new ExprString(data->symbols.create(""));
|
||||
else if ($2->size() == 1) $$ = $2->front();
|
||||
else $$ = new ExprConcatStrings($2);
|
||||
else $$ = new ExprConcatStrings(true, $2);
|
||||
}
|
||||
| IND_STRING_OPEN ind_string_parts IND_STRING_CLOSE {
|
||||
$$ = stripIndentation(data->symbols, *$2);
|
||||
@@ -402,7 +413,7 @@ binds
|
||||
if ($$->attrs.find(*i) != $$->attrs.end())
|
||||
dupAttr(*i, makeCurPos(@3, data), $$->attrs[*i].pos);
|
||||
Pos pos = makeCurPos(@3, data);
|
||||
$$->attrs[*i] = ExprAttrs::AttrDef(*i, pos);
|
||||
$$->attrs[*i] = ExprAttrs::AttrDef(new ExprVar(*i), pos, true);
|
||||
}
|
||||
}
|
||||
| binds INHERIT '(' expr ')' attrs ';'
|
||||
@@ -454,7 +465,7 @@ formal
|
||||
: ID { $$ = new Formal(data->symbols.create($1), 0); }
|
||||
| ID '?' expr { $$ = new Formal(data->symbols.create($1), $3); }
|
||||
;
|
||||
|
||||
|
||||
%%
|
||||
|
||||
|
||||
@@ -467,10 +478,10 @@ formal
|
||||
|
||||
|
||||
namespace nix {
|
||||
|
||||
|
||||
|
||||
Expr * EvalState::parse(const char * text,
|
||||
const Path & path, const Path & basePath)
|
||||
const Path & path, const Path & basePath, StaticEnv & staticEnv)
|
||||
{
|
||||
yyscan_t scanner;
|
||||
ParseData data(*this);
|
||||
@@ -481,20 +492,20 @@ Expr * EvalState::parse(const char * text,
|
||||
yy_scan_string(text, scanner);
|
||||
int res = yyparse(scanner, &data);
|
||||
yylex_destroy(scanner);
|
||||
|
||||
|
||||
if (res) throw ParseError(data.error);
|
||||
|
||||
try {
|
||||
data.result->bindVars(staticBaseEnv);
|
||||
data.result->bindVars(staticEnv);
|
||||
} catch (Error & e) {
|
||||
throw ParseError(format("%1%, in `%2%'") % e.msg() % path);
|
||||
}
|
||||
|
||||
|
||||
return data.result;
|
||||
}
|
||||
|
||||
|
||||
Expr * EvalState::parseExprFromFile(Path path)
|
||||
Path resolveExprPath(Path path)
|
||||
{
|
||||
assert(path[0] == '/');
|
||||
|
||||
@@ -512,21 +523,25 @@ Expr * EvalState::parseExprFromFile(Path path)
|
||||
if (S_ISDIR(st.st_mode))
|
||||
path = canonPath(path + "/default.nix");
|
||||
|
||||
/* Read and parse the input file, unless it's already in the parse
|
||||
tree cache. */
|
||||
Expr * e = parseTrees[path];
|
||||
if (!e) {
|
||||
e = parse(readFile(path).c_str(), path, dirOf(path));
|
||||
parseTrees[path] = e;
|
||||
}
|
||||
return path;
|
||||
}
|
||||
|
||||
return e;
|
||||
|
||||
Expr * EvalState::parseExprFromFile(const Path & path)
|
||||
{
|
||||
return parse(readFile(path).c_str(), path, dirOf(path), staticBaseEnv);
|
||||
}
|
||||
|
||||
|
||||
Expr * EvalState::parseExprFromString(const string & s, const Path & basePath, StaticEnv & staticEnv)
|
||||
{
|
||||
return parse(s.c_str(), "(string)", basePath, staticEnv);
|
||||
}
|
||||
|
||||
|
||||
Expr * EvalState::parseExprFromString(const string & s, const Path & basePath)
|
||||
{
|
||||
return parse(s.c_str(), "(string)", basePath);
|
||||
return parseExprFromString(s, basePath, staticBaseEnv);
|
||||
}
|
||||
|
||||
|
||||
@@ -541,7 +556,7 @@ void EvalState::addToSearchPath(const string & s)
|
||||
prefix = string(s, 0, pos);
|
||||
path = string(s, pos + 1);
|
||||
}
|
||||
|
||||
|
||||
path = absPath(path);
|
||||
if (pathExists(path)) {
|
||||
debug(format("adding path `%1%' to the search path") % path);
|
||||
@@ -554,7 +569,7 @@ Path EvalState::findFile(const string & path)
|
||||
{
|
||||
foreach (SearchPath::iterator, i, searchPath) {
|
||||
Path res;
|
||||
if (i->first.empty())
|
||||
if (i->first.empty())
|
||||
res = i->second + "/" + path;
|
||||
else {
|
||||
if (path.compare(0, i->first.size(), i->first) != 0 ||
|
||||
|
||||
+187
-45
@@ -37,7 +37,7 @@ std::pair<string, string> decodeContext(const string & s)
|
||||
|
||||
|
||||
/* Load and evaluate an expression from path specified by the
|
||||
argument. */
|
||||
argument. */
|
||||
static void prim_import(EvalState & state, Value * * args, Value & v)
|
||||
{
|
||||
PathSet context;
|
||||
@@ -51,6 +51,12 @@ static void prim_import(EvalState & state, Value * * args, Value & v)
|
||||
% path % ctx);
|
||||
if (isDerivation(ctx))
|
||||
try {
|
||||
/* For performance, prefetch all substitute info. */
|
||||
PathSet willBuild, willSubstitute, unknown;
|
||||
unsigned long long downloadSize, narSize;
|
||||
queryMissing(*store, singleton<PathSet>(ctx),
|
||||
willBuild, willSubstitute, unknown, downloadSize, narSize);
|
||||
|
||||
/* !!! If using a substitute, we only need to fetch
|
||||
the selected output of this derivation. */
|
||||
store->buildPaths(singleton<PathSet>(ctx));
|
||||
@@ -59,7 +65,30 @@ static void prim_import(EvalState & state, Value * * args, Value & v)
|
||||
}
|
||||
}
|
||||
|
||||
state.evalFile(path, v);
|
||||
if (isStorePath(path) && store->isValidPath(path) && isDerivation(path)) {
|
||||
Derivation drv = parseDerivation(readFile(path));
|
||||
Value & w = *state.allocValue();
|
||||
state.mkAttrs(w, 1 + drv.outputs.size());
|
||||
mkString(*state.allocAttr(w, state.sDrvPath), path, singleton<PathSet>("=" + path));
|
||||
state.mkList(*state.allocAttr(w, state.symbols.create("outputs")), drv.outputs.size());
|
||||
unsigned int outputs_index = 0;
|
||||
|
||||
Value * outputsVal = w.attrs->find(state.symbols.create("outputs"))->value;
|
||||
foreach (DerivationOutputs::iterator, i, drv.outputs) {
|
||||
mkString(*state.allocAttr(w, state.symbols.create(i->first)),
|
||||
i->second.path, singleton<PathSet>("!" + i->first + "!" + path));
|
||||
mkString(*(outputsVal->list.elems[outputs_index++] = state.allocValue()),
|
||||
i->first);
|
||||
}
|
||||
w.attrs->sort();
|
||||
Value fun;
|
||||
state.evalFile(state.findFile("nix/imported-drv-to-derivation.nix"), fun);
|
||||
state.forceFunction(fun);
|
||||
mkApp(v, fun, w);
|
||||
state.forceAttrs(v);
|
||||
} else {
|
||||
state.evalFile(path, v);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -153,8 +182,8 @@ static void prim_genericClosure(EvalState & state, Value * * args, Value & v)
|
||||
list<Value> res;
|
||||
set<Value, CompareValues> doneKeys; // !!! use Value *?
|
||||
while (!workSet.empty()) {
|
||||
Value * e = *(workSet.begin());
|
||||
workSet.pop_front();
|
||||
Value * e = *(workSet.begin());
|
||||
workSet.pop_front();
|
||||
|
||||
state.forceAttrs(*e);
|
||||
|
||||
@@ -167,7 +196,7 @@ static void prim_genericClosure(EvalState & state, Value * * args, Value & v)
|
||||
if (doneKeys.find(*key->value) != doneKeys.end()) continue;
|
||||
doneKeys.insert(*key->value);
|
||||
res.push_back(*e);
|
||||
|
||||
|
||||
/* Call the `operator' function with `e' as argument. */
|
||||
Value call;
|
||||
mkApp(call, *op->value, *e);
|
||||
@@ -217,7 +246,7 @@ static void prim_addErrorContext(EvalState & state, Value * * args, Value & v)
|
||||
}
|
||||
|
||||
|
||||
/* Try evaluating the argument. Success => {success=true; value=something;},
|
||||
/* Try evaluating the argument. Success => {success=true; value=something;},
|
||||
* else => {success=false; value=false;} */
|
||||
static void prim_tryEval(EvalState & state, Value * * args, Value & v)
|
||||
{
|
||||
@@ -280,16 +309,22 @@ static void prim_derivationStrict(EvalState & state, Value * * args, Value & v)
|
||||
throw EvalError("required attribute `name' missing");
|
||||
string drvName;
|
||||
Pos & posDrvName(*attr->pos);
|
||||
try {
|
||||
try {
|
||||
drvName = state.forceStringNoCtx(*attr->value);
|
||||
} catch (Error & e) {
|
||||
e.addPrefix(format("while evaluating the derivation attribute `name' at %1%:\n") % posDrvName);
|
||||
throw;
|
||||
}
|
||||
|
||||
|
||||
/* Check whether null attributes should be ignored. */
|
||||
bool ignoreNulls = false;
|
||||
attr = args[0]->attrs->find(state.sIgnoreNulls);
|
||||
if (attr != args[0]->attrs->end())
|
||||
ignoreNulls = state.forceBool(*attr->value);
|
||||
|
||||
/* Build the derivation expression by processing the attributes. */
|
||||
Derivation drv;
|
||||
|
||||
|
||||
PathSet context;
|
||||
|
||||
string outputHash, outputHashAlgo;
|
||||
@@ -299,11 +334,17 @@ static void prim_derivationStrict(EvalState & state, Value * * args, Value & v)
|
||||
outputs.insert("out");
|
||||
|
||||
foreach (Bindings::iterator, i, *args[0]->attrs) {
|
||||
if (i->name == state.sIgnoreNulls) continue;
|
||||
string key = i->name;
|
||||
startNest(nest, lvlVomit, format("processing attribute `%1%'") % key);
|
||||
|
||||
try {
|
||||
|
||||
if (ignoreNulls) {
|
||||
state.forceValue(*i->value);
|
||||
if (i->value->type == tNull) continue;
|
||||
}
|
||||
|
||||
/* The `args' attribute is special: it supplies the
|
||||
command-line arguments to the builder. */
|
||||
if (key == "args") {
|
||||
@@ -328,12 +369,12 @@ static void prim_derivationStrict(EvalState & state, Value * * args, Value & v)
|
||||
else if (key == "outputHash") outputHash = s;
|
||||
else if (key == "outputHashAlgo") outputHashAlgo = s;
|
||||
else if (key == "outputHashMode") {
|
||||
if (s == "recursive") outputHashRecursive = true;
|
||||
if (s == "recursive") outputHashRecursive = true;
|
||||
else if (s == "flat") outputHashRecursive = false;
|
||||
else throw EvalError(format("invalid value `%1%' for `outputHashMode' attribute") % s);
|
||||
}
|
||||
else if (key == "outputs") {
|
||||
Strings tmp = tokenizeString(s);
|
||||
Strings tmp = tokenizeString<Strings>(s);
|
||||
outputs.clear();
|
||||
foreach (Strings::iterator, j, tmp) {
|
||||
if (outputs.find(*j) != outputs.end())
|
||||
@@ -360,13 +401,13 @@ static void prim_derivationStrict(EvalState & state, Value * * args, Value & v)
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* Everything in the context of the strings in the derivation
|
||||
attributes should be added as dependencies of the resulting
|
||||
derivation. */
|
||||
foreach (PathSet::iterator, i, context) {
|
||||
Path path = *i;
|
||||
|
||||
|
||||
/* Paths marked with `=' denote that the path of a derivation
|
||||
is explicitly passed to the builder. Since that allows the
|
||||
builder to gain access to every path in the dependency
|
||||
@@ -403,7 +444,7 @@ static void prim_derivationStrict(EvalState & state, Value * * args, Value & v)
|
||||
else
|
||||
drv.inputSrcs.insert(path);
|
||||
}
|
||||
|
||||
|
||||
/* Do we have all required attributes? */
|
||||
if (drv.builder == "")
|
||||
throw EvalError("required attribute `builder' missing");
|
||||
@@ -420,14 +461,14 @@ static void prim_derivationStrict(EvalState & state, Value * * args, Value & v)
|
||||
/* Handle fixed-output derivations. */
|
||||
if (outputs.size() != 1 || *(outputs.begin()) != "out")
|
||||
throw Error("multiple outputs are not supported in fixed-output derivations");
|
||||
|
||||
|
||||
HashType ht = parseHashType(outputHashAlgo);
|
||||
if (ht == htUnknown)
|
||||
throw EvalError(format("unknown hash algorithm `%1%'") % outputHashAlgo);
|
||||
Hash h = parseHash16or32(ht, outputHash);
|
||||
outputHash = printHash(h);
|
||||
if (outputHashRecursive) outputHashAlgo = "r:" + outputHashAlgo;
|
||||
|
||||
|
||||
Path outPath = makeFixedOutputPath(outputHashRecursive, ht, h, drvName);
|
||||
drv.env["out"] = outPath;
|
||||
drv.outputs["out"] = DerivationOutput(outPath, outputHashAlgo, outputHash);
|
||||
@@ -447,7 +488,7 @@ static void prim_derivationStrict(EvalState & state, Value * * args, Value & v)
|
||||
/* Use the masked derivation expression to compute the output
|
||||
path. */
|
||||
Hash h = hashDerivationModulo(*store, drv);
|
||||
|
||||
|
||||
foreach (DerivationOutputs::iterator, i, drv.outputs)
|
||||
if (i->second.path == "") {
|
||||
Path outPath = makeOutputPath(i->first, h, drvName);
|
||||
@@ -457,7 +498,7 @@ static void prim_derivationStrict(EvalState & state, Value * * args, Value & v)
|
||||
}
|
||||
|
||||
/* Write the resulting term into the Nix store directory. */
|
||||
Path drvPath = writeDerivation(*store, drv, drvName);
|
||||
Path drvPath = writeDerivation(*store, drv, drvName, state.repair);
|
||||
|
||||
printMsg(lvlChatty, format("instantiated `%1%' -> `%2%'")
|
||||
% drvName % drvPath);
|
||||
@@ -592,10 +633,10 @@ static void prim_toFile(EvalState & state, Value * * args, Value & v)
|
||||
throw EvalError(format("in `toFile': the file `%1%' cannot refer to derivation outputs") % name);
|
||||
refs.insert(path);
|
||||
}
|
||||
|
||||
Path storePath = readOnlyMode
|
||||
|
||||
Path storePath = settings.readOnlyMode
|
||||
? computeStorePathForText(name, contents, refs)
|
||||
: store->addTextToStore(name, contents, refs);
|
||||
: store->addTextToStore(name, contents, refs, state.repair);
|
||||
|
||||
/* Note: we don't need to add `context' to the context of the
|
||||
result, since `storePath' itself has references to the paths
|
||||
@@ -609,7 +650,7 @@ struct FilterFromExpr : PathFilter
|
||||
{
|
||||
EvalState & state;
|
||||
Value & filter;
|
||||
|
||||
|
||||
FilterFromExpr(EvalState & state, Value & filter)
|
||||
: state(state), filter(filter)
|
||||
{
|
||||
@@ -630,12 +671,12 @@ struct FilterFromExpr : PathFilter
|
||||
state.callFunction(filter, arg1, fun2);
|
||||
|
||||
Value arg2;
|
||||
mkString(arg2,
|
||||
mkString(arg2,
|
||||
S_ISREG(st.st_mode) ? "regular" :
|
||||
S_ISDIR(st.st_mode) ? "directory" :
|
||||
S_ISLNK(st.st_mode) ? "symlink" :
|
||||
"unknown" /* not supported, will fail! */);
|
||||
|
||||
|
||||
Value res;
|
||||
state.callFunction(fun2, arg2, res);
|
||||
|
||||
@@ -657,9 +698,9 @@ static void prim_filterSource(EvalState & state, Value * * args, Value & v)
|
||||
|
||||
FilterFromExpr filter(state, *args[0]);
|
||||
|
||||
Path dstPath = readOnlyMode
|
||||
Path dstPath = settings.readOnlyMode
|
||||
? computeStorePathForPath(path, true, htSHA256, filter).first
|
||||
: store->addToStore(path, true, htSHA256, filter);
|
||||
: store->addToStore(path, true, htSHA256, filter, state.repair);
|
||||
|
||||
mkString(v, dstPath, singleton<PathSet>(dstPath));
|
||||
}
|
||||
@@ -689,7 +730,7 @@ static void prim_attrNames(EvalState & state, Value * * args, Value & v)
|
||||
|
||||
|
||||
/* Dynamic version of the `.' operator. */
|
||||
static void prim_getAttr(EvalState & state, Value * * args, Value & v)
|
||||
void prim_getAttr(EvalState & state, Value * * args, Value & v)
|
||||
{
|
||||
string attr = state.forceStringNoCtx(*args[0]);
|
||||
state.forceAttrs(*args[1]);
|
||||
@@ -698,6 +739,7 @@ static void prim_getAttr(EvalState & state, Value * * args, Value & v)
|
||||
if (i == args[1]->attrs->end())
|
||||
throw EvalError(format("attribute `%1%' missing") % attr);
|
||||
// !!! add to stack trace?
|
||||
if (state.countCalls && i->pos) state.attrSelects[*i->pos]++;
|
||||
state.forceValue(*i->value);
|
||||
v = *i->value;
|
||||
}
|
||||
@@ -758,12 +800,12 @@ static void prim_listToAttrs(EvalState & state, Value * * args, Value & v)
|
||||
for (unsigned int i = 0; i < args[0]->list.length; ++i) {
|
||||
Value & v2(*args[0]->list.elems[i]);
|
||||
state.forceAttrs(v2);
|
||||
|
||||
|
||||
Bindings::iterator j = v2.attrs->find(state.sName);
|
||||
if (j == v2.attrs->end())
|
||||
throw TypeError("`name' attribute missing in a call to `listToAttrs'");
|
||||
string name = state.forceStringNoCtx(*j->value);
|
||||
|
||||
|
||||
Bindings::iterator j2 = v2.attrs->find(state.symbols.create("value"));
|
||||
if (j2 == v2.attrs->end())
|
||||
throw TypeError("`value' attribute missing in a call to `listToAttrs'");
|
||||
@@ -787,7 +829,7 @@ static void prim_intersectAttrs(EvalState & state, Value * * args, Value & v)
|
||||
{
|
||||
state.forceAttrs(*args[0]);
|
||||
state.forceAttrs(*args[1]);
|
||||
|
||||
|
||||
state.mkAttrs(v, std::min(args[0]->attrs->size(), args[1]->attrs->size()));
|
||||
|
||||
foreach (Bindings::iterator, i, *args[0]->attrs) {
|
||||
@@ -843,19 +885,33 @@ static void prim_isList(EvalState & state, Value * * args, Value & v)
|
||||
}
|
||||
|
||||
|
||||
static void elemAt(EvalState & state, Value & list, int n, Value & v)
|
||||
{
|
||||
state.forceList(list);
|
||||
if (n < 0 || n >= list.list.length)
|
||||
throw Error(format("list index %1% is out of bounds") % n);
|
||||
state.forceValue(*list.list.elems[n]);
|
||||
v = *list.list.elems[n];
|
||||
}
|
||||
|
||||
|
||||
/* Return the n-1'th element of a list. */
|
||||
static void prim_elemAt(EvalState & state, Value * * args, Value & v)
|
||||
{
|
||||
elemAt(state, *args[0], state.forceInt(*args[1]), v);
|
||||
}
|
||||
|
||||
|
||||
/* Return the first element of a list. */
|
||||
static void prim_head(EvalState & state, Value * * args, Value & v)
|
||||
{
|
||||
state.forceList(*args[0]);
|
||||
if (args[0]->list.length == 0)
|
||||
throw Error("`head' called on an empty list");
|
||||
state.forceValue(*args[0]->list.elems[0]);
|
||||
v = *args[0]->list.elems[0];
|
||||
elemAt(state, *args[0], 0, v);
|
||||
}
|
||||
|
||||
|
||||
/* Return a list consisting of everything but the the first element of
|
||||
a list. */
|
||||
a list. Warning: this function takes O(n) time, so you probably
|
||||
don't want to use it! */
|
||||
static void prim_tail(EvalState & state, Value * * args, Value & v)
|
||||
{
|
||||
state.forceList(*args[0]);
|
||||
@@ -876,11 +932,64 @@ static void prim_map(EvalState & state, Value * * args, Value & v)
|
||||
state.mkList(v, args[1]->list.length);
|
||||
|
||||
for (unsigned int n = 0; n < v.list.length; ++n)
|
||||
mkApp(*(v.list.elems[n] = state.allocValue()),
|
||||
mkApp(*(v.list.elems[n] = state.allocValue()),
|
||||
*args[0], *args[1]->list.elems[n]);
|
||||
}
|
||||
|
||||
|
||||
/* Filter a list using a predicate; that is, return a list containing
|
||||
every element from the list for which the predicate function
|
||||
returns true. */
|
||||
static void prim_filter(EvalState & state, Value * * args, Value & v)
|
||||
{
|
||||
state.forceFunction(*args[0]);
|
||||
state.forceList(*args[1]);
|
||||
|
||||
// FIXME: putting this on the stack is risky.
|
||||
Value * vs[args[1]->list.length];
|
||||
unsigned int k = 0;
|
||||
|
||||
bool same = true;
|
||||
for (unsigned int n = 0; n < args[1]->list.length; ++n) {
|
||||
Value res;
|
||||
state.callFunction(*args[0], *args[1]->list.elems[n], res);
|
||||
if (state.forceBool(res))
|
||||
vs[k++] = args[1]->list.elems[n];
|
||||
else
|
||||
same = false;
|
||||
}
|
||||
|
||||
if (same)
|
||||
v = *args[1];
|
||||
else {
|
||||
state.mkList(v, k);
|
||||
for (unsigned int n = 0; n < k; ++n) v.list.elems[n] = vs[n];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* Return true if a list contains a given element. */
|
||||
static void prim_elem(EvalState & state, Value * * args, Value & v)
|
||||
{
|
||||
bool res = false;
|
||||
state.forceList(*args[1]);
|
||||
for (unsigned int n = 0; n < args[1]->list.length; ++n)
|
||||
if (state.eqValues(*args[0], *args[1]->list.elems[n])) {
|
||||
res = true;
|
||||
break;
|
||||
}
|
||||
mkBool(v, res);
|
||||
}
|
||||
|
||||
|
||||
/* Concatenate a list of lists. */
|
||||
static void prim_concatLists(EvalState & state, Value * * args, Value & v)
|
||||
{
|
||||
state.forceList(*args[0]);
|
||||
state.concatLists(v, args[0]->list.length, args[0]->list.elems);
|
||||
}
|
||||
|
||||
|
||||
/* Return the length of a list. This is an O(1) time operation. */
|
||||
static void prim_length(EvalState & state, Value * * args, Value & v)
|
||||
{
|
||||
@@ -914,7 +1023,7 @@ static void prim_mul(EvalState & state, Value * * args, Value & v)
|
||||
|
||||
static void prim_div(EvalState & state, Value * * args, Value & v)
|
||||
{
|
||||
int i2 = state.forceInt(*args[1]);
|
||||
NixInt i2 = state.forceInt(*args[1]);
|
||||
if (i2 == 0) throw EvalError("division by zero");
|
||||
mkInt(v, state.forceInt(*args[0]) / i2);
|
||||
}
|
||||
@@ -922,7 +1031,10 @@ static void prim_div(EvalState & state, Value * * args, Value & v)
|
||||
|
||||
static void prim_lessThan(EvalState & state, Value * * args, Value & v)
|
||||
{
|
||||
mkBool(v, state.forceInt(*args[0]) < state.forceInt(*args[1]));
|
||||
state.forceValue(*args[0]);
|
||||
state.forceValue(*args[1]);
|
||||
CompareValues comp;
|
||||
mkBool(v, comp(*args[0], *args[1]));
|
||||
}
|
||||
|
||||
|
||||
@@ -992,11 +1104,26 @@ static void prim_unsafeDiscardOutputDependency(EvalState & state, Value * * args
|
||||
if (p.at(0) == '=') p = "~" + string(p, 1);
|
||||
context2.insert(p);
|
||||
}
|
||||
|
||||
|
||||
mkString(v, s, context2);
|
||||
}
|
||||
|
||||
|
||||
/* Return the cryptographic hash of a string in base-16. */
|
||||
static void prim_hashString(EvalState & state, Value * * args, Value & v)
|
||||
{
|
||||
string type = state.forceStringNoCtx(*args[0]);
|
||||
HashType ht = parseHashType(type);
|
||||
if (ht == htUnknown)
|
||||
throw Error(format("unknown hash type `%1%'") % type);
|
||||
|
||||
PathSet context; // discarded
|
||||
string s = state.forceString(*args[1], context);
|
||||
|
||||
mkString(v, printHash(hashString(ht, s)), context);
|
||||
};
|
||||
|
||||
|
||||
/*************************************************************
|
||||
* Versions
|
||||
*************************************************************/
|
||||
@@ -1039,19 +1166,29 @@ void EvalState::createBaseEnv()
|
||||
|
||||
mkBool(v, true);
|
||||
addConstant("true", v);
|
||||
|
||||
|
||||
mkBool(v, false);
|
||||
addConstant("false", v);
|
||||
|
||||
|
||||
v.type = tNull;
|
||||
addConstant("null", v);
|
||||
|
||||
mkInt(v, time(0));
|
||||
addConstant("__currentTime", v);
|
||||
|
||||
mkString(v, thisSystem.c_str());
|
||||
mkString(v, settings.thisSystem.c_str());
|
||||
addConstant("__currentSystem", v);
|
||||
|
||||
mkString(v, nixVersion.c_str());
|
||||
addConstant("__nixVersion", v);
|
||||
|
||||
/* Language version. This should be increased every time a new
|
||||
language feature gets added. It's not necessary to increase it
|
||||
when primops get added, because you can just use `builtins ?
|
||||
primOp' to check. */
|
||||
mkInt(v, 1);
|
||||
addConstant("__langVersion", v);
|
||||
|
||||
// Miscellaneous
|
||||
addPrimOp("import", 1, prim_import);
|
||||
addPrimOp("isNull", 1, prim_isNull);
|
||||
@@ -1092,11 +1229,15 @@ void EvalState::createBaseEnv()
|
||||
|
||||
// Lists
|
||||
addPrimOp("__isList", 1, prim_isList);
|
||||
addPrimOp("__elemAt", 2, prim_elemAt);
|
||||
addPrimOp("__head", 1, prim_head);
|
||||
addPrimOp("__tail", 1, prim_tail);
|
||||
addPrimOp("map", 2, prim_map);
|
||||
addPrimOp("__filter", 2, prim_filter);
|
||||
addPrimOp("__elem", 2, prim_elem);
|
||||
addPrimOp("__concatLists", 1, prim_concatLists);
|
||||
addPrimOp("__length", 1, prim_length);
|
||||
|
||||
|
||||
// Integer arithmetic
|
||||
addPrimOp("__add", 2, prim_add);
|
||||
addPrimOp("__sub", 2, prim_sub);
|
||||
@@ -1110,6 +1251,7 @@ void EvalState::createBaseEnv()
|
||||
addPrimOp("__stringLength", 1, prim_stringLength);
|
||||
addPrimOp("__unsafeDiscardStringContext", 1, prim_unsafeDiscardStringContext);
|
||||
addPrimOp("__unsafeDiscardOutputDependency", 1, prim_unsafeDiscardOutputDependency);
|
||||
addPrimOp("__hashString", 2, prim_hashString);
|
||||
|
||||
// Versions
|
||||
addPrimOp("__parseDrvName", 1, prim_parseDrvName);
|
||||
@@ -1120,7 +1262,7 @@ void EvalState::createBaseEnv()
|
||||
|
||||
/* Add a wrapper around the derivation primop that computes the
|
||||
`drvPath' and `outPath' attributes lazily. */
|
||||
mkThunk_(v, parseExprFromFile(findFile("nix/derivation.nix")));
|
||||
evalFile(findFile("nix/derivation.nix"), v);
|
||||
addConstant("derivation", v);
|
||||
|
||||
/* Now that we've added all primops, sort the `builtins' attribute
|
||||
|
||||
@@ -28,17 +28,17 @@ private:
|
||||
|
||||
public:
|
||||
Symbol() : s(0) { };
|
||||
|
||||
|
||||
bool operator == (const Symbol & s2) const
|
||||
{
|
||||
return s == s2.s;
|
||||
}
|
||||
|
||||
|
||||
bool operator != (const Symbol & s2) const
|
||||
{
|
||||
return s != s2.s;
|
||||
}
|
||||
|
||||
|
||||
bool operator < (const Symbol & s2) const
|
||||
{
|
||||
return s < s2.s;
|
||||
@@ -49,6 +49,11 @@ public:
|
||||
return *s;
|
||||
}
|
||||
|
||||
bool set() const
|
||||
{
|
||||
return s;
|
||||
}
|
||||
|
||||
bool empty() const
|
||||
{
|
||||
return s->empty();
|
||||
@@ -66,7 +71,7 @@ inline std::ostream & operator << (std::ostream & str, const Symbol & sym)
|
||||
class SymbolTable
|
||||
{
|
||||
private:
|
||||
#if HAVE_TR1_UNORDERED_SET
|
||||
#if HAVE_TR1_UNORDERED_SET
|
||||
typedef std::tr1::unordered_set<string> Symbols;
|
||||
#else
|
||||
typedef std::set<string> Symbols;
|
||||
|
||||
+12
-9
@@ -31,17 +31,20 @@ struct PrimOp;
|
||||
struct Symbol;
|
||||
|
||||
|
||||
typedef long NixInt;
|
||||
|
||||
|
||||
struct Value
|
||||
{
|
||||
ValueType type;
|
||||
union
|
||||
union
|
||||
{
|
||||
int integer;
|
||||
NixInt integer;
|
||||
bool boolean;
|
||||
|
||||
/* Strings in the evaluator carry a so-called `context' (the
|
||||
ATermList) which is a list of strings representing store
|
||||
paths. This is to allow users to write things like
|
||||
|
||||
/* Strings in the evaluator carry a so-called `context' which
|
||||
is a list of strings representing store paths. This is to
|
||||
allow users to write things like
|
||||
|
||||
"--with-freetype2-library=" + freetype + "/lib"
|
||||
|
||||
@@ -63,7 +66,7 @@ struct Value
|
||||
const char * s;
|
||||
const char * * context; // must be in sorted order
|
||||
} string;
|
||||
|
||||
|
||||
const char * path;
|
||||
Bindings * attrs;
|
||||
struct {
|
||||
@@ -97,7 +100,7 @@ static inline void clearValue(Value & v)
|
||||
}
|
||||
|
||||
|
||||
static inline void mkInt(Value & v, int n)
|
||||
static inline void mkInt(Value & v, NixInt n)
|
||||
{
|
||||
clearValue(v);
|
||||
v.type = tInt;
|
||||
@@ -131,7 +134,7 @@ static inline void mkStringNoCopy(Value & v, const char * s)
|
||||
|
||||
static inline void mkString(Value & v, const Symbol & s)
|
||||
{
|
||||
mkStringNoCopy(v, ((string) s).c_str());
|
||||
mkStringNoCopy(v, ((const string &) s).c_str());
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -1,12 +1,11 @@
|
||||
pkglib_LTLIBRARIES = libmain.la
|
||||
|
||||
libmain_la_SOURCES = shared.cc
|
||||
libmain_la_SOURCES = shared.cc stack.cc
|
||||
|
||||
libmain_la_LIBADD = ../libstore/libstore.la @BDW_GC_LIBS@
|
||||
|
||||
pkginclude_HEADERS = shared.hh
|
||||
|
||||
AM_CXXFLAGS = \
|
||||
-DNIX_VERSION=\"$(VERSION)\" \
|
||||
-I$(srcdir)/.. -I$(srcdir)/../libutil \
|
||||
-I$(srcdir)/../libstore
|
||||
|
||||
+64
-95
@@ -10,8 +10,10 @@
|
||||
#include <cctype>
|
||||
#include <exception>
|
||||
|
||||
#include <sys/time.h>
|
||||
#include <sys/stat.h>
|
||||
#include <unistd.h>
|
||||
#include <signal.h>
|
||||
|
||||
#if HAVE_BOEHMGC
|
||||
#include <gc/gc.h>
|
||||
@@ -33,20 +35,10 @@ static void sigintHandler(int signo)
|
||||
}
|
||||
|
||||
|
||||
Path makeRootName(const Path & gcRoot, int & counter)
|
||||
{
|
||||
counter++;
|
||||
if (counter == 1)
|
||||
return gcRoot;
|
||||
else
|
||||
return (format("%1%-%2%") % gcRoot % counter).str();
|
||||
}
|
||||
|
||||
|
||||
void printGCWarning()
|
||||
{
|
||||
static bool haveWarned = false;
|
||||
warnOnce(haveWarned,
|
||||
warnOnce(haveWarned,
|
||||
"you did not specify `--add-root'; "
|
||||
"the result might be removed by the garbage collector");
|
||||
}
|
||||
@@ -57,7 +49,14 @@ void printMissing(StoreAPI & store, const PathSet & paths)
|
||||
unsigned long long downloadSize, narSize;
|
||||
PathSet willBuild, willSubstitute, unknown;
|
||||
queryMissing(store, paths, willBuild, willSubstitute, unknown, downloadSize, narSize);
|
||||
printMissing(willBuild, willSubstitute, unknown, downloadSize, narSize);
|
||||
}
|
||||
|
||||
|
||||
void printMissing(const PathSet & willBuild,
|
||||
const PathSet & willSubstitute, const PathSet & unknown,
|
||||
unsigned long long downloadSize, unsigned long long narSize)
|
||||
{
|
||||
if (!willBuild.empty()) {
|
||||
printMsg(lvlInfo, format("these derivations will be built:"));
|
||||
foreach (PathSet::iterator, i, willBuild)
|
||||
@@ -74,7 +73,7 @@ void printMissing(StoreAPI & store, const PathSet & paths)
|
||||
|
||||
if (!unknown.empty()) {
|
||||
printMsg(lvlInfo, format("don't know how to build these paths%1%:")
|
||||
% (readOnlyMode ? " (may be caused by read-only store access)" : ""));
|
||||
% (settings.readOnlyMode ? " (may be caused by read-only store access)" : ""));
|
||||
foreach (PathSet::iterator, i, unknown)
|
||||
printMsg(lvlInfo, format(" %1%") % *i);
|
||||
}
|
||||
@@ -93,12 +92,25 @@ static void setLogType(string lt)
|
||||
static bool showTrace = false;
|
||||
|
||||
|
||||
string getArg(const string & opt,
|
||||
Strings::iterator & i, const Strings::iterator & end)
|
||||
{
|
||||
++i;
|
||||
if (i == end) throw UsageError(format("`%1%' requires an argument") % opt);
|
||||
return *i;
|
||||
}
|
||||
|
||||
|
||||
void detectStackOverflow();
|
||||
|
||||
|
||||
/* Initialize and reorder arguments, then call the actual argument
|
||||
processor. */
|
||||
static void initAndRun(int argc, char * * argv)
|
||||
{
|
||||
setDefaultsFromEnvironment();
|
||||
|
||||
settings.processEnvironment();
|
||||
settings.loadConfFile();
|
||||
|
||||
/* Catch SIGINT. */
|
||||
struct sigaction act;
|
||||
act.sa_handler = sigintHandler;
|
||||
@@ -123,12 +135,19 @@ static void initAndRun(int argc, char * * argv)
|
||||
if (sigaction(SIGCHLD, &act, 0))
|
||||
throw SysError("resetting SIGCHLD");
|
||||
|
||||
/* Register a SIGSEGV handler to detect stack overflows. */
|
||||
detectStackOverflow();
|
||||
|
||||
/* There is no privacy in the Nix system ;-) At least not for
|
||||
now. In particular, store objects should be readable by
|
||||
everybody. This prevents nasty surprises when using a shared
|
||||
store (with the setuid() hack). */
|
||||
everybody. */
|
||||
umask(0022);
|
||||
|
||||
/* Initialise the PRNG. */
|
||||
struct timeval tv;
|
||||
gettimeofday(&tv, 0);
|
||||
srandom(tv.tv_usec);
|
||||
|
||||
/* Process the NIX_LOG_TYPE environment variable. */
|
||||
string lt = getEnv("NIX_LOG_TYPE");
|
||||
if (lt != "") setLogType(lt);
|
||||
@@ -137,7 +156,7 @@ static void initAndRun(int argc, char * * argv)
|
||||
Strings args, remaining;
|
||||
while (argc--) args.push_back(*argv++);
|
||||
args.erase(args.begin());
|
||||
|
||||
|
||||
/* Expand compound dash options (i.e., `-qlf' -> `-q -l -f'), and
|
||||
ignore options for the ATerm library. */
|
||||
for (Strings::iterator i = args.begin(); i != args.end(); ++i) {
|
||||
@@ -156,46 +175,44 @@ static void initAndRun(int argc, char * * argv)
|
||||
remaining.clear();
|
||||
|
||||
/* Process default options. */
|
||||
int verbosityDelta = 0;
|
||||
for (Strings::iterator i = args.begin(); i != args.end(); ++i) {
|
||||
string arg = *i;
|
||||
if (arg == "--verbose" || arg == "-v") verbosityDelta++;
|
||||
else if (arg == "--quiet") verbosityDelta--;
|
||||
if (arg == "--verbose" || arg == "-v") verbosity = (Verbosity) (verbosity + 1);
|
||||
else if (arg == "--quiet") verbosity = verbosity > lvlError ? (Verbosity) (verbosity - 1) : lvlError;
|
||||
else if (arg == "--log-type") {
|
||||
++i;
|
||||
if (i == args.end()) throw UsageError("`--log-type' requires an argument");
|
||||
setLogType(*i);
|
||||
string s = getArg(arg, i, args.end());
|
||||
setLogType(s);
|
||||
}
|
||||
else if (arg == "--no-build-output" || arg == "-Q")
|
||||
buildVerbosity = lvlVomit;
|
||||
settings.buildVerbosity = lvlVomit;
|
||||
else if (arg == "--print-build-trace")
|
||||
printBuildTrace = true;
|
||||
settings.printBuildTrace = true;
|
||||
else if (arg == "--help") {
|
||||
printHelp();
|
||||
return;
|
||||
}
|
||||
else if (arg == "--version") {
|
||||
std::cout << format("%1% (Nix) %2%") % programId % NIX_VERSION << std::endl;
|
||||
std::cout << format("%1% (Nix) %2%") % programId % nixVersion << std::endl;
|
||||
return;
|
||||
}
|
||||
else if (arg == "--keep-failed" || arg == "-K")
|
||||
keepFailed = true;
|
||||
settings.keepFailed = true;
|
||||
else if (arg == "--keep-going" || arg == "-k")
|
||||
keepGoing = true;
|
||||
settings.keepGoing = true;
|
||||
else if (arg == "--fallback")
|
||||
tryFallback = true;
|
||||
settings.set("build-fallback", "true");
|
||||
else if (arg == "--max-jobs" || arg == "-j")
|
||||
maxBuildJobs = getIntArg<unsigned int>(arg, i, args.end());
|
||||
settings.set("build-max-jobs", getArg(arg, i, args.end()));
|
||||
else if (arg == "--cores")
|
||||
buildCores = getIntArg<unsigned int>(arg, i, args.end());
|
||||
settings.set("build-cores", getArg(arg, i, args.end()));
|
||||
else if (arg == "--readonly-mode")
|
||||
readOnlyMode = true;
|
||||
settings.readOnlyMode = true;
|
||||
else if (arg == "--max-silent-time")
|
||||
maxSilentTime = getIntArg<unsigned int>(arg, i, args.end());
|
||||
settings.set("build-max-silent-time", getArg(arg, i, args.end()));
|
||||
else if (arg == "--timeout")
|
||||
buildTimeout = getIntArg<unsigned int>(arg, i, args.end());
|
||||
settings.set("build-timeout", getArg(arg, i, args.end()));
|
||||
else if (arg == "--no-build-hook")
|
||||
useBuildHook = false;
|
||||
settings.useBuildHook = false;
|
||||
else if (arg == "--show-trace")
|
||||
showTrace = true;
|
||||
else if (arg == "--option") {
|
||||
@@ -203,14 +220,13 @@ static void initAndRun(int argc, char * * argv)
|
||||
string name = *i;
|
||||
++i; if (i == args.end()) throw UsageError("`--option' requires two arguments");
|
||||
string value = *i;
|
||||
overrideSetting(name, tokenizeString(value));
|
||||
settings.set(name, value);
|
||||
}
|
||||
else remaining.push_back(arg);
|
||||
}
|
||||
|
||||
verbosityDelta += queryIntSetting("verbosity", lvlInfo);
|
||||
verbosity = (Verbosity) (verbosityDelta < 0 ? 0 : verbosityDelta);
|
||||
|
||||
settings.update();
|
||||
|
||||
run(remaining);
|
||||
|
||||
/* Close the Nix database. */
|
||||
@@ -218,56 +234,6 @@ static void initAndRun(int argc, char * * argv)
|
||||
}
|
||||
|
||||
|
||||
bool setuidMode = false;
|
||||
|
||||
|
||||
static void setuidInit()
|
||||
{
|
||||
/* Don't do anything if this is not a setuid binary. */
|
||||
if (getuid() == geteuid() && getgid() == getegid()) return;
|
||||
|
||||
uid_t nixUid = geteuid();
|
||||
gid_t nixGid = getegid();
|
||||
|
||||
setuidCleanup();
|
||||
|
||||
/* Don't trust the current directory. */
|
||||
if (chdir("/") == -1) abort();
|
||||
|
||||
/* Set the real (and preferably also the save) uid/gid to the
|
||||
effective uid/gid. This matters mostly when we're not using
|
||||
build-users (bad!), since some builders (like Perl) complain
|
||||
when real != effective.
|
||||
|
||||
On systems where setresuid is unavailable, we can't drop the
|
||||
saved uid/gid. This means that we could go back to the
|
||||
original real uid (i.e., the uid of the caller). That's not
|
||||
really a problem, except maybe when we execute a builder and
|
||||
we're not using build-users. In that case, the builder may be
|
||||
able to switch to the uid of the caller and possibly do bad
|
||||
stuff. But note that when not using build-users, the builder
|
||||
could also modify the Nix executables (say, replace them by a
|
||||
Trojan horse), so the problem is already there. */
|
||||
|
||||
#if HAVE_SETRESUID
|
||||
if (setresuid(nixUid, nixUid, nixUid)) abort();
|
||||
if (setresgid(nixGid, nixGid, nixGid)) abort();
|
||||
#elif HAVE_SETREUID
|
||||
/* Note: doesn't set saved uid/gid! */
|
||||
fprintf(stderr, "warning: cannot set saved uid\n");
|
||||
if (setreuid(nixUid, nixUid)) abort();
|
||||
if (setregid(nixGid, nixGid)) abort();
|
||||
#else
|
||||
/* Note: doesn't set real and saved uid/gid! */
|
||||
fprintf(stderr, "warning: cannot set real and saved uids\n");
|
||||
if (setuid(nixUid)) abort();
|
||||
if (setgid(nixGid)) abort();
|
||||
#endif
|
||||
|
||||
setuidMode = true;
|
||||
}
|
||||
|
||||
|
||||
/* Called when the Boehm GC runs out of memory. */
|
||||
static void * oomHandler(size_t requested)
|
||||
{
|
||||
@@ -276,6 +242,14 @@ static void * oomHandler(size_t requested)
|
||||
}
|
||||
|
||||
|
||||
void showManPage(const string & name)
|
||||
{
|
||||
string cmd = "man " + name;
|
||||
if (system(cmd.c_str()) != 0)
|
||||
throw Error(format("command `%1%' failed") % cmd);
|
||||
}
|
||||
|
||||
|
||||
int exitCode = 0;
|
||||
char * * argvSaved = 0;
|
||||
|
||||
@@ -290,11 +264,6 @@ int main(int argc, char * * argv)
|
||||
|
||||
argvSaved = argv;
|
||||
|
||||
/* If we're setuid, then we need to take some security precautions
|
||||
right away. */
|
||||
if (argc == 0) abort();
|
||||
setuidInit();
|
||||
|
||||
/* Turn on buffering for cerr. */
|
||||
#if HAVE_PUBSETBUF
|
||||
std::cerr.rdbuf()->pubsetbuf(buf, sizeof(buf));
|
||||
@@ -323,7 +292,7 @@ int main(int argc, char * * argv)
|
||||
throw;
|
||||
}
|
||||
} catch (UsageError & e) {
|
||||
printMsg(lvlError,
|
||||
printMsg(lvlError,
|
||||
format(
|
||||
"error: %1%\n"
|
||||
"Try `%2% --help' for more information.")
|
||||
|
||||
@@ -26,11 +26,14 @@ MakeError(UsageError, nix::Error);
|
||||
class StoreAPI;
|
||||
|
||||
/* Ugh. No better place to put this. */
|
||||
Path makeRootName(const Path & gcRoot, int & counter);
|
||||
void printGCWarning();
|
||||
|
||||
void printMissing(StoreAPI & store, const PathSet & paths);
|
||||
|
||||
void printMissing(const PathSet & willBuild,
|
||||
const PathSet & willSubstitute, const PathSet & unknown,
|
||||
unsigned long long downloadSize, unsigned long long narSize);
|
||||
|
||||
template<class N> N getIntArg(const string & opt,
|
||||
Strings::iterator & i, const Strings::iterator & end)
|
||||
{
|
||||
@@ -42,8 +45,8 @@ template<class N> N getIntArg(const string & opt,
|
||||
return n;
|
||||
}
|
||||
|
||||
/* Whether we're running setuid. */
|
||||
extern bool setuidMode;
|
||||
/* Show the manual page for the specified program. */
|
||||
void showManPage(const string & name);
|
||||
|
||||
extern volatile ::sig_atomic_t blockInt;
|
||||
|
||||
|
||||
@@ -0,0 +1,72 @@
|
||||
#include "config.h"
|
||||
|
||||
#include "types.hh"
|
||||
|
||||
#include <cstring>
|
||||
#include <cstddef>
|
||||
#include <cstdlib>
|
||||
|
||||
#include <unistd.h>
|
||||
#include <signal.h>
|
||||
|
||||
namespace nix {
|
||||
|
||||
|
||||
static void sigsegvHandler(int signo, siginfo_t * info, void * ctx)
|
||||
{
|
||||
/* Detect stack overflows by comparing the faulting address with
|
||||
the stack pointer. Unfortunately, getting the stack pointer is
|
||||
not portable. */
|
||||
bool haveSP = true;
|
||||
char * sp;
|
||||
#if defined(__x86_64__) && defined(REG_RSP)
|
||||
sp = (char *) ((ucontext *) ctx)->uc_mcontext.gregs[REG_RSP];
|
||||
#elif defined(REG_ESP)
|
||||
sp = (char *) ((ucontext *) ctx)->uc_mcontext.gregs[REG_ESP];
|
||||
#else
|
||||
haveSP = false;
|
||||
#endif
|
||||
|
||||
if (haveSP) {
|
||||
ptrdiff_t diff = (char *) info->si_addr - sp;
|
||||
if (diff < 0) diff = -diff;
|
||||
if (diff < 4096) {
|
||||
char msg[] = "error: stack overflow (possible infinite recursion)\n";
|
||||
write(2, msg, strlen(msg));
|
||||
_exit(1); // maybe abort instead?
|
||||
}
|
||||
}
|
||||
|
||||
/* Restore default behaviour (i.e. segfault and dump core). */
|
||||
struct sigaction act;
|
||||
sigfillset(&act.sa_mask);
|
||||
act.sa_handler = SIG_DFL;
|
||||
act.sa_flags = 0;
|
||||
if (sigaction(SIGSEGV, &act, 0)) abort();
|
||||
}
|
||||
|
||||
|
||||
void detectStackOverflow()
|
||||
{
|
||||
#if defined(SA_SIGINFO) && defined (SA_ONSTACK)
|
||||
/* Install a SIGSEGV handler to detect stack overflows. This
|
||||
requires an alternative stack, otherwise the signal cannot be
|
||||
delivered when we're out of stack space. */
|
||||
stack_t stack;
|
||||
stack.ss_size = 4096 * 4 + MINSIGSTKSZ;
|
||||
stack.ss_sp = new char[stack.ss_size];
|
||||
if (!stack.ss_sp) throw Error("cannot allocate alternative stack");
|
||||
stack.ss_flags = 0;
|
||||
if (sigaltstack(&stack, 0) == -1) throw SysError("cannot set alternative stack");
|
||||
|
||||
struct sigaction act;
|
||||
sigfillset(&act.sa_mask);
|
||||
act.sa_sigaction = sigsegvHandler;
|
||||
act.sa_flags = SA_SIGINFO | SA_ONSTACK;
|
||||
if (sigaction(SIGSEGV, &act, 0))
|
||||
throw SysError("resetting SIGCHLD");
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -23,10 +23,11 @@ AM_CXXFLAGS = -Wall \
|
||||
-DNIX_CONF_DIR=\"$(sysconfdir)/nix\" \
|
||||
-DNIX_LIBEXEC_DIR=\"$(libexecdir)\" \
|
||||
-DNIX_BIN_DIR=\"$(bindir)\" \
|
||||
-DNIX_VERSION=\"$(VERSION)\" \
|
||||
-I$(srcdir)/.. -I$(srcdir)/../libutil \
|
||||
-I$(srcdir)/../libstore
|
||||
|
||||
local-store.lo: schema.sql.hh
|
||||
|
||||
%.sql.hh: %.sql
|
||||
../bin2c/bin2c schema < $< > $@ || (rm $@ && exit 1)
|
||||
sed -e 's/"/\\"/g' -e 's/\(.*\)/"\1\\n"/' < $< > $@ || (rm $@ && exit 1)
|
||||
|
||||
+825
-458
File diff suppressed because it is too large
Load Diff
+36
-13
@@ -12,7 +12,7 @@ void DerivationOutput::parseHashInfo(bool & recursive, HashType & hashType, Hash
|
||||
{
|
||||
recursive = false;
|
||||
string algo = hashAlgo;
|
||||
|
||||
|
||||
if (string(algo, 0, 2) == "r:") {
|
||||
recursive = true;
|
||||
algo = string(algo, 2);
|
||||
@@ -21,13 +21,13 @@ void DerivationOutput::parseHashInfo(bool & recursive, HashType & hashType, Hash
|
||||
hashType = parseHashType(algo);
|
||||
if (hashType == htUnknown)
|
||||
throw Error(format("unknown hash algorithm `%1%'") % algo);
|
||||
|
||||
|
||||
hash = parseHash(hashType, this->hash);
|
||||
}
|
||||
|
||||
|
||||
Path writeDerivation(StoreAPI & store,
|
||||
const Derivation & drv, const string & name)
|
||||
const Derivation & drv, const string & name, bool repair)
|
||||
{
|
||||
PathSet references;
|
||||
references.insert(drv.inputSrcs.begin(), drv.inputSrcs.end());
|
||||
@@ -38,9 +38,9 @@ Path writeDerivation(StoreAPI & store,
|
||||
held during a garbage collection). */
|
||||
string suffix = name + drvExtension;
|
||||
string contents = unparseDerivation(drv);
|
||||
return readOnlyMode
|
||||
return settings.readOnlyMode
|
||||
? computeStorePathForText(suffix, contents, references)
|
||||
: store.addTextToStore(suffix, contents, references);
|
||||
: store.addTextToStore(suffix, contents, references, repair);
|
||||
}
|
||||
|
||||
|
||||
@@ -51,7 +51,7 @@ static Path parsePath(std::istream & str)
|
||||
throw Error(format("bad path `%1%' in derivation") % s);
|
||||
return s;
|
||||
}
|
||||
|
||||
|
||||
|
||||
static StringSet parseStrings(std::istream & str, bool arePaths)
|
||||
{
|
||||
@@ -60,7 +60,7 @@ static StringSet parseStrings(std::istream & str, bool arePaths)
|
||||
res.insert(arePaths ? parsePath(str) : parseString(str));
|
||||
return res;
|
||||
}
|
||||
|
||||
|
||||
|
||||
Derivation parseDerivation(const string & s)
|
||||
{
|
||||
@@ -106,7 +106,7 @@ Derivation parseDerivation(const string & s)
|
||||
expect(str, ")");
|
||||
drv.env[name] = value;
|
||||
}
|
||||
|
||||
|
||||
expect(str, ")");
|
||||
return drv;
|
||||
}
|
||||
@@ -165,7 +165,7 @@ string unparseDerivation(const Derivation & drv)
|
||||
|
||||
s += "],";
|
||||
printStrings(s, drv.inputSrcs.begin(), drv.inputSrcs.end());
|
||||
|
||||
|
||||
s += ','; printString(s, drv.platform);
|
||||
s += ','; printString(s, drv.builder);
|
||||
s += ','; printStrings(s, drv.args.begin(), drv.args.end());
|
||||
@@ -178,9 +178,9 @@ string unparseDerivation(const Derivation & drv)
|
||||
s += ','; printString(s, i->second);
|
||||
s += ')';
|
||||
}
|
||||
|
||||
|
||||
s += "])";
|
||||
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
@@ -190,7 +190,7 @@ bool isDerivation(const string & fileName)
|
||||
return hasSuffix(fileName, drvExtension);
|
||||
}
|
||||
|
||||
|
||||
|
||||
bool isFixedOutputDrv(const Derivation & drv)
|
||||
{
|
||||
return drv.outputs.size() == 1 &&
|
||||
@@ -247,9 +247,32 @@ Hash hashDerivationModulo(StoreAPI & store, Derivation drv)
|
||||
inputs2[printHash(h)] = i->second;
|
||||
}
|
||||
drv.inputDrvs = inputs2;
|
||||
|
||||
|
||||
return hashString(htSHA256, unparseDerivation(drv));
|
||||
}
|
||||
|
||||
|
||||
DrvPathWithOutputs parseDrvPathWithOutputs(const string & s)
|
||||
{
|
||||
size_t n = s.find("!");
|
||||
return n == s.npos
|
||||
? DrvPathWithOutputs(s, std::set<string>())
|
||||
: DrvPathWithOutputs(string(s, 0, n), tokenizeString<std::set<string> >(string(s, n + 1), ","));
|
||||
}
|
||||
|
||||
|
||||
Path makeDrvPathWithOutputs(const Path & drvPath, const std::set<string> & outputs)
|
||||
{
|
||||
return outputs.empty()
|
||||
? drvPath
|
||||
: drvPath + "!" + concatStringsSep(",", outputs);
|
||||
}
|
||||
|
||||
|
||||
bool wantOutput(const string & output, const std::set<string> & wanted)
|
||||
{
|
||||
return wanted.empty() || wanted.find(output) != wanted.end();
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
#pragma once
|
||||
|
||||
#include <map>
|
||||
|
||||
#include "types.hh"
|
||||
#include "hash.hh"
|
||||
|
||||
#include <map>
|
||||
|
||||
|
||||
namespace nix {
|
||||
|
||||
@@ -57,7 +57,7 @@ class StoreAPI;
|
||||
|
||||
/* Write a derivation to the Nix store, and return its path. */
|
||||
Path writeDerivation(StoreAPI & store,
|
||||
const Derivation & drv, const string & name);
|
||||
const Derivation & drv, const string & name, bool repair = false);
|
||||
|
||||
/* Parse a derivation. */
|
||||
Derivation parseDerivation(const string & s);
|
||||
@@ -79,4 +79,15 @@ typedef std::map<Path, Hash> DrvHashes;
|
||||
|
||||
extern DrvHashes drvHashes;
|
||||
|
||||
/* Split a string specifying a derivation and a set of outputs
|
||||
(/nix/store/hash-foo!out1,out2,...) into the derivation path and
|
||||
the outputs. */
|
||||
typedef std::pair<string, std::set<string> > DrvPathWithOutputs;
|
||||
DrvPathWithOutputs parseDrvPathWithOutputs(const string & s);
|
||||
|
||||
Path makeDrvPathWithOutputs(const Path & drvPath, const std::set<string> & outputs);
|
||||
|
||||
bool wantOutput(const string & output, const std::set<string> & wanted);
|
||||
|
||||
|
||||
}
|
||||
|
||||
+278
-213
@@ -1,7 +1,6 @@
|
||||
#include "globals.hh"
|
||||
#include "misc.hh"
|
||||
#include "local-store.hh"
|
||||
#include "immutable.hh"
|
||||
|
||||
#include <boost/shared_ptr.hpp>
|
||||
|
||||
@@ -34,10 +33,10 @@ static const int defaultGcLevel = 1000;
|
||||
int LocalStore::openGCLock(LockType lockType)
|
||||
{
|
||||
Path fnGCLock = (format("%1%/%2%")
|
||||
% nixStateDir % gcLockName).str();
|
||||
|
||||
% settings.nixStateDir % gcLockName).str();
|
||||
|
||||
debug(format("acquiring global GC lock `%1%'") % fnGCLock);
|
||||
|
||||
|
||||
AutoCloseFD fdGCLock = open(fnGCLock.c_str(), O_RDWR | O_CREAT, 0600);
|
||||
if (fdGCLock == -1)
|
||||
throw SysError(format("opening global GC lock `%1%'") % fnGCLock);
|
||||
@@ -51,7 +50,7 @@ int LocalStore::openGCLock(LockType lockType)
|
||||
/* !!! Restrict read permission on the GC root. Otherwise any
|
||||
process that can open the file for reading can DoS the
|
||||
collector. */
|
||||
|
||||
|
||||
return fdGCLock.borrow();
|
||||
}
|
||||
|
||||
@@ -85,7 +84,7 @@ void LocalStore::addIndirectRoot(const Path & path)
|
||||
{
|
||||
string hash = printHash32(hashString(htSHA1, path));
|
||||
Path realRoot = canonPath((format("%1%/%2%/auto/%3%")
|
||||
% nixStateDir % gcRootsDir % hash).str());
|
||||
% settings.nixStateDir % gcRootsDir % hash).str());
|
||||
createSymlink(realRoot, path);
|
||||
}
|
||||
|
||||
@@ -113,15 +112,15 @@ Path addPermRoot(StoreAPI & store, const Path & _storePath,
|
||||
|
||||
else {
|
||||
if (!allowOutsideRootsDir) {
|
||||
Path rootsDir = canonPath((format("%1%/%2%") % nixStateDir % gcRootsDir).str());
|
||||
|
||||
Path rootsDir = canonPath((format("%1%/%2%") % settings.nixStateDir % gcRootsDir).str());
|
||||
|
||||
if (string(gcRoot, 0, rootsDir.size() + 1) != rootsDir + "/")
|
||||
throw Error(format(
|
||||
"path `%1%' is not a valid garbage collector root; "
|
||||
"it's not in the directory `%2%'")
|
||||
% gcRoot % rootsDir);
|
||||
}
|
||||
|
||||
|
||||
createSymlink(gcRoot, storePath);
|
||||
}
|
||||
|
||||
@@ -130,10 +129,10 @@ Path addPermRoot(StoreAPI & store, const Path & _storePath,
|
||||
Instead of reading all the roots, it would be more efficient to
|
||||
check if the root is in a directory in or linked from the
|
||||
gcroots directory. */
|
||||
if (queryBoolSetting("gc-check-reachability", false)) {
|
||||
if (settings.checkRootReachability) {
|
||||
Roots roots = store.findRoots();
|
||||
if (roots.find(gcRoot) == roots.end())
|
||||
printMsg(lvlError,
|
||||
printMsg(lvlError,
|
||||
format(
|
||||
"warning: `%1%' is not in a directory where the garbage collector looks for roots; "
|
||||
"therefore, `%2%' might be removed by the garbage collector")
|
||||
@@ -144,7 +143,7 @@ Path addPermRoot(StoreAPI & store, const Path & _storePath,
|
||||
progress. This prevents the set of permanent roots from
|
||||
increasing while a GC is in progress. */
|
||||
store.syncWithGC();
|
||||
|
||||
|
||||
return gcRoot;
|
||||
}
|
||||
|
||||
@@ -160,23 +159,23 @@ void LocalStore::addTempRoot(const Path & path)
|
||||
if (fdTempRoots == -1) {
|
||||
|
||||
while (1) {
|
||||
Path dir = (format("%1%/%2%") % nixStateDir % tempRootsDir).str();
|
||||
Path dir = (format("%1%/%2%") % settings.nixStateDir % tempRootsDir).str();
|
||||
createDirs(dir);
|
||||
|
||||
|
||||
fnTempRoots = (format("%1%/%2%")
|
||||
% dir % getpid()).str();
|
||||
|
||||
AutoCloseFD fdGCLock = openGCLock(ltRead);
|
||||
|
||||
|
||||
if (pathExists(fnTempRoots))
|
||||
/* It *must* be stale, since there can be no two
|
||||
processes with the same pid. */
|
||||
unlink(fnTempRoots.c_str());
|
||||
|
||||
fdTempRoots = openLockFile(fnTempRoots, true);
|
||||
fdTempRoots = openLockFile(fnTempRoots, true);
|
||||
|
||||
fdGCLock.close();
|
||||
|
||||
|
||||
debug(format("acquiring read lock on `%1%'") % fnTempRoots);
|
||||
lockFile(fdTempRoots, ltRead, true);
|
||||
|
||||
@@ -186,7 +185,7 @@ void LocalStore::addTempRoot(const Path & path)
|
||||
if (fstat(fdTempRoots, &st) == -1)
|
||||
throw SysError(format("statting `%1%'") % fnTempRoots);
|
||||
if (st.st_size == 0) break;
|
||||
|
||||
|
||||
/* The garbage collector deleted this file before we could
|
||||
get a lock. (It won't delete the file after we get a
|
||||
lock.) Try again. */
|
||||
@@ -218,7 +217,7 @@ void removeTempRoots()
|
||||
|
||||
|
||||
/* Automatically clean up the temporary roots file when we exit. */
|
||||
struct RemoveTempRoots
|
||||
struct RemoveTempRoots
|
||||
{
|
||||
~RemoveTempRoots()
|
||||
{
|
||||
@@ -238,10 +237,10 @@ static void readTempRoots(PathSet & tempRoots, FDs & fds)
|
||||
/* Read the `temproots' directory for per-process temporary root
|
||||
files. */
|
||||
Strings tempRootFiles = readDirectory(
|
||||
(format("%1%/%2%") % nixStateDir % tempRootsDir).str());
|
||||
(format("%1%/%2%") % settings.nixStateDir % tempRootsDir).str());
|
||||
|
||||
foreach (Strings::iterator, i, tempRootFiles) {
|
||||
Path path = (format("%1%/%2%/%3%") % nixStateDir % tempRootsDir % *i).str();
|
||||
Path path = (format("%1%/%2%/%3%") % settings.nixStateDir % tempRootsDir % *i).str();
|
||||
|
||||
debug(format("reading temporary root file `%1%'") % path);
|
||||
FDPtr fd(new AutoCloseFD(open(path.c_str(), O_RDWR, 0666)));
|
||||
@@ -259,8 +258,7 @@ static void readTempRoots(PathSet & tempRoots, FDs & fds)
|
||||
only succeed if the owning process has died. In that case
|
||||
we don't care about its temporary roots. */
|
||||
if (lockFile(*fd, ltWrite, false)) {
|
||||
printMsg(lvlError, format("removing stale temporary roots file `%1%'")
|
||||
% path);
|
||||
printMsg(lvlError, format("removing stale temporary roots file `%1%'") % path);
|
||||
unlink(path.c_str());
|
||||
writeFull(*fd, (const unsigned char *) "d", 1);
|
||||
continue;
|
||||
@@ -291,46 +289,47 @@ static void readTempRoots(PathSet & tempRoots, FDs & fds)
|
||||
}
|
||||
|
||||
|
||||
static void findRoots(StoreAPI & store, const Path & path,
|
||||
bool recurseSymlinks, bool deleteStale, Roots & roots)
|
||||
static void foundRoot(StoreAPI & store,
|
||||
const Path & path, const Path & target, Roots & roots)
|
||||
{
|
||||
Path storePath = toStorePath(target);
|
||||
if (store.isValidPath(storePath))
|
||||
roots[path] = storePath;
|
||||
else
|
||||
printMsg(lvlInfo, format("skipping invalid root from `%1%' to `%2%'") % path % storePath);
|
||||
}
|
||||
|
||||
|
||||
static void findRoots(StoreAPI & store, const Path & path, Roots & roots)
|
||||
{
|
||||
try {
|
||||
|
||||
struct stat st;
|
||||
if (lstat(path.c_str(), &st) == -1)
|
||||
throw SysError(format("statting `%1%'") % path);
|
||||
|
||||
printMsg(lvlVomit, format("looking at `%1%'") % path);
|
||||
struct stat st = lstat(path);
|
||||
|
||||
if (S_ISDIR(st.st_mode)) {
|
||||
Strings names = readDirectory(path);
|
||||
foreach (Strings::iterator, i, names)
|
||||
findRoots(store, path + "/" + *i, recurseSymlinks, deleteStale, roots);
|
||||
findRoots(store, path + "/" + *i, roots);
|
||||
}
|
||||
|
||||
else if (S_ISLNK(st.st_mode)) {
|
||||
Path target = absPath(readLink(path), dirOf(path));
|
||||
Path target = readLink(path);
|
||||
if (isInStore(target))
|
||||
foundRoot(store, path, target, roots);
|
||||
|
||||
if (isInStore(target)) {
|
||||
debug(format("found root `%1%' in `%2%'")
|
||||
% target % path);
|
||||
Path storePath = toStorePath(target);
|
||||
if (store.isValidPath(storePath))
|
||||
roots[path] = storePath;
|
||||
else
|
||||
printMsg(lvlInfo, format("skipping invalid root from `%1%' to `%2%'")
|
||||
% path % storePath);
|
||||
}
|
||||
|
||||
else if (recurseSymlinks) {
|
||||
if (pathExists(target))
|
||||
findRoots(store, target, false, deleteStale, roots);
|
||||
else if (deleteStale) {
|
||||
printMsg(lvlInfo, format("removing stale link from `%1%' to `%2%'") % path % target);
|
||||
/* Note that we only delete when recursing, i.e.,
|
||||
when we are still in the `gcroots' tree. We
|
||||
never delete stuff outside that tree. */
|
||||
unlink(path.c_str());
|
||||
/* Handle indirect roots. */
|
||||
else {
|
||||
target = absPath(target, dirOf(path));
|
||||
if (!pathExists(target)) {
|
||||
if (isInDir(path, settings.nixStateDir + "/" + gcRootsDir + "/auto")) {
|
||||
printMsg(lvlInfo, format("removing stale link from `%1%' to `%2%'") % path % target);
|
||||
unlink(path.c_str());
|
||||
}
|
||||
} else {
|
||||
struct stat st2 = lstat(target);
|
||||
if (!S_ISLNK(st2.st_mode)) return;
|
||||
Path target2 = readLink(target);
|
||||
if (isInStore(target2)) foundRoot(store, path, target2, roots);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -347,35 +346,33 @@ static void findRoots(StoreAPI & store, const Path & path,
|
||||
}
|
||||
|
||||
|
||||
static Roots findRoots(StoreAPI & store, bool deleteStale)
|
||||
{
|
||||
Roots roots;
|
||||
Path rootsDir = canonPath((format("%1%/%2%") % nixStateDir % gcRootsDir).str());
|
||||
findRoots(store, rootsDir, true, deleteStale, roots);
|
||||
return roots;
|
||||
}
|
||||
|
||||
|
||||
Roots LocalStore::findRoots()
|
||||
{
|
||||
return nix::findRoots(*this, false);
|
||||
Roots roots;
|
||||
|
||||
/* Process direct roots in {gcroots,manifests,profiles}. */
|
||||
nix::findRoots(*this, settings.nixStateDir + "/" + gcRootsDir, roots);
|
||||
nix::findRoots(*this, settings.nixStateDir + "/manifests", roots);
|
||||
nix::findRoots(*this, settings.nixStateDir + "/profiles", roots);
|
||||
|
||||
return roots;
|
||||
}
|
||||
|
||||
|
||||
static void addAdditionalRoots(StoreAPI & store, PathSet & roots)
|
||||
{
|
||||
Path rootFinder = getEnv("NIX_ROOT_FINDER",
|
||||
nixLibexecDir + "/nix/find-runtime-roots.pl");
|
||||
settings.nixLibexecDir + "/nix/find-runtime-roots.pl");
|
||||
|
||||
if (rootFinder.empty()) return;
|
||||
|
||||
|
||||
debug(format("executing `%1%' to find additional roots") % rootFinder);
|
||||
|
||||
string result = runProgram(rootFinder);
|
||||
|
||||
Strings paths = tokenizeString(result, "\n");
|
||||
|
||||
foreach (Strings::iterator, i, paths) {
|
||||
StringSet paths = tokenizeString<StringSet>(result, "\n");
|
||||
|
||||
foreach (StringSet::iterator, i, paths) {
|
||||
if (isInStore(*i)) {
|
||||
Path path = toStorePath(*i);
|
||||
if (roots.find(path) == roots.end() && store.isValidPath(path)) {
|
||||
@@ -396,24 +393,17 @@ struct LocalStore::GCState
|
||||
GCResults & results;
|
||||
PathSet roots;
|
||||
PathSet tempRoots;
|
||||
PathSet deleted;
|
||||
PathSet live;
|
||||
PathSet busy;
|
||||
PathSet invalidated;
|
||||
PathSet dead;
|
||||
PathSet alive;
|
||||
bool gcKeepOutputs;
|
||||
bool gcKeepDerivations;
|
||||
unsigned long long bytesInvalidated;
|
||||
Path trashDir;
|
||||
bool shouldDelete;
|
||||
GCState(GCResults & results_) : results(results_), bytesInvalidated(0) { }
|
||||
};
|
||||
|
||||
|
||||
static bool shouldDelete(GCOptions::GCAction action)
|
||||
{
|
||||
return action == GCOptions::gcDeleteDead
|
||||
|| action == GCOptions::gcDeleteSpecific;
|
||||
}
|
||||
|
||||
|
||||
bool LocalStore::isActiveTempFile(const GCState & state,
|
||||
const Path & path, const string & suffix)
|
||||
{
|
||||
@@ -424,158 +414,207 @@ bool LocalStore::isActiveTempFile(const GCState & state,
|
||||
|
||||
void LocalStore::deleteGarbage(GCState & state, const Path & path)
|
||||
{
|
||||
printMsg(lvlInfo, format("deleting `%1%'") % path);
|
||||
unsigned long long bytesFreed, blocksFreed;
|
||||
deletePathWrapped(path, bytesFreed, blocksFreed);
|
||||
unsigned long long bytesFreed;
|
||||
deletePathWrapped(path, bytesFreed);
|
||||
state.results.bytesFreed += bytesFreed;
|
||||
state.results.blocksFreed += blocksFreed;
|
||||
}
|
||||
|
||||
|
||||
bool LocalStore::tryToDelete(GCState & state, const Path & path)
|
||||
void LocalStore::deletePathRecursive(GCState & state, const Path & path)
|
||||
{
|
||||
checkInterrupt();
|
||||
|
||||
struct stat st;
|
||||
if (lstat(path.c_str(), &st)) {
|
||||
if (errno == ENOENT) return true;
|
||||
throw SysError(format("getting status of %1%") % path);
|
||||
}
|
||||
|
||||
if (state.deleted.find(path) != state.deleted.end()) return true;
|
||||
if (state.live.find(path) != state.live.end()) return false;
|
||||
|
||||
startNest(nest, lvlDebug, format("considering whether to delete `%1%'") % path);
|
||||
|
||||
if (state.roots.find(path) != state.roots.end()) {
|
||||
printMsg(lvlDebug, format("cannot delete `%1%' because it's a root") % path);
|
||||
goto isLive;
|
||||
}
|
||||
unsigned long long size = 0;
|
||||
|
||||
if (isValidPath(path)) {
|
||||
|
||||
/* Recursively try to delete the referrers of this path. If
|
||||
any referrer can't be deleted, then this path can't be
|
||||
deleted either. */
|
||||
PathSet referrers;
|
||||
queryReferrers(path, referrers);
|
||||
foreach (PathSet::iterator, i, referrers)
|
||||
if (*i != path && !tryToDelete(state, *i)) {
|
||||
printMsg(lvlDebug, format("cannot delete `%1%' because it has live referrers") % path);
|
||||
goto isLive;
|
||||
}
|
||||
|
||||
/* If gc-keep-derivations is set and this is a derivation,
|
||||
then don't delete the derivation if any of the outputs are
|
||||
live. */
|
||||
if (state.gcKeepDerivations && isDerivation(path)) {
|
||||
PathSet outputs = queryDerivationOutputs(path);
|
||||
foreach (PathSet::iterator, i, outputs)
|
||||
if (!tryToDelete(state, *i)) {
|
||||
printMsg(lvlDebug, format("cannot delete derivation `%1%' because its output `%2%' is alive") % path % *i);
|
||||
goto isLive;
|
||||
}
|
||||
}
|
||||
|
||||
/* If gc-keep-derivations and gc-keep-outputs are both set,
|
||||
it's possible that the path has already been deleted (due
|
||||
to the recursion below), so bail out. */
|
||||
if (!pathExists(path)) return true;
|
||||
|
||||
/* If gc-keep-outputs is set, then don't delete this path if
|
||||
there are derivers of this path that are not garbage. */
|
||||
if (state.gcKeepOutputs) {
|
||||
PathSet derivers = queryValidDerivers(path);
|
||||
foreach (PathSet::iterator, deriver, derivers) {
|
||||
/* Break an infinite recursion if gc-keep-derivations
|
||||
and gc-keep-outputs are both set by tentatively
|
||||
assuming that this path is garbage. This is a safe
|
||||
assumption because at this point, the only thing
|
||||
that can prevent it from being garbage is the
|
||||
deriver. Since tryToDelete() works "upwards"
|
||||
through the dependency graph, it won't encouter
|
||||
this path except in the call to tryToDelete() in
|
||||
the gc-keep-derivation branch. */
|
||||
state.deleted.insert(path);
|
||||
if (!tryToDelete(state, *deriver)) {
|
||||
state.deleted.erase(path);
|
||||
printMsg(lvlDebug, format("cannot delete `%1%' because its deriver `%2%' is alive") % path % *deriver);
|
||||
goto isLive;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (*i != path) deletePathRecursive(state, *i);
|
||||
size = queryPathInfo(path).narSize;
|
||||
invalidatePathChecked(path);
|
||||
}
|
||||
|
||||
else {
|
||||
struct stat st;
|
||||
if (lstat(path.c_str(), &st)) {
|
||||
if (errno == ENOENT) return;
|
||||
throw SysError(format("getting status of %1%") % path);
|
||||
}
|
||||
|
||||
printMsg(lvlInfo, format("deleting `%1%'") % path);
|
||||
|
||||
state.results.paths.insert(path);
|
||||
|
||||
/* If the path is not a regular file or symlink, move it to the
|
||||
trash directory. The move is to ensure that later (when we're
|
||||
not holding the global GC lock) we can delete the path without
|
||||
being afraid that the path has become alive again. Otherwise
|
||||
delete it right away. */
|
||||
if (S_ISDIR(st.st_mode)) {
|
||||
// Estimate the amount freed using the narSize field. FIXME:
|
||||
// if the path was not valid, need to determine the actual
|
||||
// size.
|
||||
state.bytesInvalidated += size;
|
||||
// Mac OS X cannot rename directories if they are read-only.
|
||||
if (chmod(path.c_str(), st.st_mode | S_IWUSR) == -1)
|
||||
throw SysError(format("making `%1%' writable") % path);
|
||||
Path tmp = state.trashDir + "/" + baseNameOf(path);
|
||||
if (rename(path.c_str(), tmp.c_str()))
|
||||
throw SysError(format("unable to rename `%1%' to `%2%'") % path % tmp);
|
||||
} else
|
||||
deleteGarbage(state, path);
|
||||
|
||||
if (state.results.bytesFreed + state.bytesInvalidated > state.options.maxFreed) {
|
||||
printMsg(lvlInfo, format("deleted or invalidated more than %1% bytes; stopping") % state.options.maxFreed);
|
||||
throw GCLimitReached();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
bool LocalStore::canReachRoot(GCState & state, PathSet & visited, const Path & path)
|
||||
{
|
||||
if (visited.find(path) != visited.end()) return false;
|
||||
|
||||
if (state.alive.find(path) != state.alive.end()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (state.dead.find(path) != state.dead.end()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (state.roots.find(path) != state.roots.end()) {
|
||||
printMsg(lvlDebug, format("cannot delete `%1%' because it's a root") % path);
|
||||
state.alive.insert(path);
|
||||
return true;
|
||||
}
|
||||
|
||||
visited.insert(path);
|
||||
|
||||
if (!isValidPath(path)) return false;
|
||||
|
||||
PathSet incoming;
|
||||
|
||||
/* Don't delete this path if any of its referrers are alive. */
|
||||
queryReferrers(path, incoming);
|
||||
|
||||
/* If gc-keep-derivations is set and this is a derivation, then
|
||||
don't delete the derivation if any of the outputs are alive. */
|
||||
if (state.gcKeepDerivations && isDerivation(path)) {
|
||||
PathSet outputs = queryDerivationOutputs(path);
|
||||
foreach (PathSet::iterator, i, outputs)
|
||||
if (isValidPath(*i) && queryDeriver(*i) == path)
|
||||
incoming.insert(*i);
|
||||
}
|
||||
|
||||
/* If gc-keep-outputs is set, then don't delete this path if there
|
||||
are derivers of this path that are not garbage. */
|
||||
if (state.gcKeepOutputs) {
|
||||
PathSet derivers = queryValidDerivers(path);
|
||||
foreach (PathSet::iterator, i, derivers)
|
||||
incoming.insert(*i);
|
||||
}
|
||||
|
||||
foreach (PathSet::iterator, i, incoming)
|
||||
if (*i != path)
|
||||
if (canReachRoot(state, visited, *i)) {
|
||||
state.alive.insert(path);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
void LocalStore::tryToDelete(GCState & state, const Path & path)
|
||||
{
|
||||
checkInterrupt();
|
||||
|
||||
if (path == linksDir || path == state.trashDir) return;
|
||||
|
||||
startNest(nest, lvlDebug, format("considering whether to delete `%1%'") % path);
|
||||
|
||||
if (!isValidPath(path)) {
|
||||
/* A lock file belonging to a path that we're building right
|
||||
now isn't garbage. */
|
||||
if (isActiveTempFile(state, path, ".lock")) return false;
|
||||
if (isActiveTempFile(state, path, ".lock")) return;
|
||||
|
||||
/* Don't delete .chroot directories for derivations that are
|
||||
currently being built. */
|
||||
if (isActiveTempFile(state, path, ".chroot")) return false;
|
||||
|
||||
if (isActiveTempFile(state, path, ".chroot")) return;
|
||||
}
|
||||
|
||||
/* The path is garbage, so delete it. */
|
||||
if (shouldDelete(state.options.action)) {
|
||||
PathSet visited;
|
||||
|
||||
/* If it's a valid path that's not a regular file or symlink,
|
||||
invalidate it, rename it, and schedule it for deletion.
|
||||
The renaming is to ensure that later (when we're not
|
||||
holding the global GC lock) we can delete the path without
|
||||
being afraid that the path has become alive again.
|
||||
Otherwise delete it right away. */
|
||||
if (isValidPath(path)) {
|
||||
if (S_ISDIR(st.st_mode)) {
|
||||
printMsg(lvlInfo, format("invalidating `%1%'") % path);
|
||||
// Estimate the amount freed using the narSize field.
|
||||
state.bytesInvalidated += queryPathInfo(path).narSize;
|
||||
invalidatePathChecked(path);
|
||||
makeMutable(path.c_str());
|
||||
// Mac OS X cannot rename directories if they are read-only.
|
||||
if (chmod(path.c_str(), st.st_mode | S_IWUSR) == -1)
|
||||
throw SysError(format("making `%1%' writable") % path);
|
||||
Path tmp = (format("%1%-gc-%2%") % path % getpid()).str();
|
||||
if (rename(path.c_str(), tmp.c_str()))
|
||||
throw SysError(format("unable to rename `%1%' to `%2%'") % path % tmp);
|
||||
state.invalidated.insert(tmp);
|
||||
} else {
|
||||
invalidatePathChecked(path);
|
||||
deleteGarbage(state, path);
|
||||
}
|
||||
} else
|
||||
deleteGarbage(state, path);
|
||||
if (canReachRoot(state, visited, path)) {
|
||||
printMsg(lvlDebug, format("cannot delete `%1%' because it's still reachable") % path);
|
||||
} else {
|
||||
/* No path we visited was a root, so everything is garbage.
|
||||
But we only delete ‘path’ and its referrers here so that
|
||||
‘nix-store --delete’ doesn't have the unexpected effect of
|
||||
recursing into derivations and outputs. */
|
||||
state.dead.insert(visited.begin(), visited.end());
|
||||
if (state.shouldDelete)
|
||||
deletePathRecursive(state, path);
|
||||
}
|
||||
}
|
||||
|
||||
if (state.options.maxFreed && state.results.bytesFreed + state.bytesInvalidated > state.options.maxFreed) {
|
||||
printMsg(lvlInfo, format("deleted or invalidated more than %1% bytes; stopping") % state.options.maxFreed);
|
||||
throw GCLimitReached();
|
||||
|
||||
/* Unlink all files in /nix/store/.links that have a link count of 1,
|
||||
which indicates that there are no other links and so they can be
|
||||
safely deleted. FIXME: race condition with optimisePath(): we
|
||||
might see a link count of 1 just before optimisePath() increases
|
||||
the link count. */
|
||||
void LocalStore::removeUnusedLinks(const GCState & state)
|
||||
{
|
||||
AutoCloseDir dir = opendir(linksDir.c_str());
|
||||
if (!dir) throw SysError(format("opening directory `%1%'") % linksDir);
|
||||
|
||||
long long actualSize = 0, unsharedSize = 0;
|
||||
|
||||
struct dirent * dirent;
|
||||
while (errno = 0, dirent = readdir(dir)) {
|
||||
checkInterrupt();
|
||||
string name = dirent->d_name;
|
||||
if (name == "." || name == "..") continue;
|
||||
Path path = linksDir + "/" + name;
|
||||
|
||||
struct stat st;
|
||||
if (lstat(path.c_str(), &st) == -1)
|
||||
throw SysError(format("statting `%1%'") % path);
|
||||
|
||||
if (st.st_nlink != 1) {
|
||||
unsigned long long size = st.st_blocks * 512ULL;
|
||||
actualSize += size;
|
||||
unsharedSize += (st.st_nlink - 1) * size;
|
||||
continue;
|
||||
}
|
||||
|
||||
} else
|
||||
printMsg(lvlTalkative, format("would delete `%1%'") % path);
|
||||
|
||||
state.deleted.insert(path);
|
||||
if (state.options.action != GCOptions::gcReturnLive)
|
||||
state.results.paths.insert(path);
|
||||
return true;
|
||||
printMsg(lvlTalkative, format("deleting unused link `%1%'") % path);
|
||||
|
||||
isLive:
|
||||
state.live.insert(path);
|
||||
if (state.options.action == GCOptions::gcReturnLive)
|
||||
state.results.paths.insert(path);
|
||||
return false;
|
||||
if (unlink(path.c_str()) == -1)
|
||||
throw SysError(format("deleting `%1%'") % path);
|
||||
|
||||
state.results.bytesFreed += st.st_blocks * 512;
|
||||
}
|
||||
|
||||
struct stat st;
|
||||
if (stat(linksDir.c_str(), &st) == -1)
|
||||
throw SysError(format("statting `%1%'") % linksDir);
|
||||
long long overhead = st.st_blocks * 512ULL;
|
||||
|
||||
printMsg(lvlInfo, format("note: currently hard linking saves %.2f MiB")
|
||||
% ((unsharedSize - actualSize - overhead) / (1024.0 * 1024.0)));
|
||||
}
|
||||
|
||||
|
||||
void LocalStore::collectGarbage(const GCOptions & options, GCResults & results)
|
||||
{
|
||||
GCState state(results);
|
||||
state.options = options;
|
||||
|
||||
state.gcKeepOutputs = queryBoolSetting("gc-keep-outputs", false);
|
||||
state.gcKeepDerivations = queryBoolSetting("gc-keep-derivations", true);
|
||||
state.options = options;
|
||||
state.trashDir = settings.nixStore + "/trash";
|
||||
state.gcKeepOutputs = settings.gcKeepOutputs;
|
||||
state.gcKeepDerivations = settings.gcKeepDerivations;
|
||||
|
||||
/* Using `--ignore-liveness' with `--delete' can have unintended
|
||||
consequences if `gc-keep-outputs' or `gc-keep-derivations' are
|
||||
@@ -585,7 +624,9 @@ void LocalStore::collectGarbage(const GCOptions & options, GCResults & results)
|
||||
state.gcKeepOutputs = false;
|
||||
state.gcKeepDerivations = false;
|
||||
}
|
||||
|
||||
|
||||
state.shouldDelete = options.action == GCOptions::gcDeleteDead || options.action == GCOptions::gcDeleteSpecific;
|
||||
|
||||
/* Acquire the global GC root. This prevents
|
||||
a) New roots from being added.
|
||||
b) Processes from creating new temporary root files. */
|
||||
@@ -594,7 +635,7 @@ void LocalStore::collectGarbage(const GCOptions & options, GCResults & results)
|
||||
/* Find the roots. Since we've grabbed the GC lock, the set of
|
||||
permanent roots cannot increase now. */
|
||||
printMsg(lvlError, format("finding garbage collector roots..."));
|
||||
Roots rootMap = options.ignoreLiveness ? Roots() : nix::findRoots(*this, true);
|
||||
Roots rootMap = options.ignoreLiveness ? Roots() : findRoots();
|
||||
|
||||
foreach (Roots::iterator, i, rootMap) state.roots.insert(i->second);
|
||||
|
||||
@@ -616,6 +657,11 @@ void LocalStore::collectGarbage(const GCOptions & options, GCResults & results)
|
||||
increase, since we hold locks on everything. So everything
|
||||
that is not reachable from `roots'. */
|
||||
|
||||
if (state.shouldDelete) {
|
||||
if (pathExists(state.trashDir)) deleteGarbage(state, state.trashDir);
|
||||
createDirs(state.trashDir);
|
||||
}
|
||||
|
||||
/* Now either delete all garbage paths, or just the specified
|
||||
paths (for gcDeleteSpecific). */
|
||||
|
||||
@@ -623,21 +669,22 @@ void LocalStore::collectGarbage(const GCOptions & options, GCResults & results)
|
||||
|
||||
foreach (PathSet::iterator, i, options.pathsToDelete) {
|
||||
assertStorePath(*i);
|
||||
if (!tryToDelete(state, *i))
|
||||
tryToDelete(state, *i);
|
||||
if (state.dead.find(*i) == state.dead.end())
|
||||
throw Error(format("cannot delete path `%1%' since it is still alive") % *i);
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
if (shouldDelete(state.options.action))
|
||||
|
||||
} else if (options.maxFreed > 0) {
|
||||
|
||||
if (state.shouldDelete)
|
||||
printMsg(lvlError, format("deleting garbage..."));
|
||||
else
|
||||
printMsg(lvlError, format("determining live/dead paths..."));
|
||||
|
||||
|
||||
try {
|
||||
|
||||
AutoCloseDir dir = opendir(nixStore.c_str());
|
||||
if (!dir) throw SysError(format("opening directory `%1%'") % nixStore);
|
||||
AutoCloseDir dir = opendir(settings.nixStore.c_str());
|
||||
if (!dir) throw SysError(format("opening directory `%1%'") % settings.nixStore);
|
||||
|
||||
/* Read the store and immediately delete all paths that
|
||||
aren't valid. When using --max-freed etc., deleting
|
||||
@@ -651,14 +698,14 @@ void LocalStore::collectGarbage(const GCOptions & options, GCResults & results)
|
||||
checkInterrupt();
|
||||
string name = dirent->d_name;
|
||||
if (name == "." || name == "..") continue;
|
||||
Path path = nixStore + "/" + name;
|
||||
Path path = settings.nixStore + "/" + name;
|
||||
if (isValidPath(path))
|
||||
entries.push_back(path);
|
||||
else
|
||||
tryToDelete(state, path);
|
||||
}
|
||||
|
||||
dir.close();
|
||||
dir.close();
|
||||
|
||||
/* Now delete the unreachable valid paths. Randomise the
|
||||
order in which we delete entries to make the collector
|
||||
@@ -675,13 +722,31 @@ void LocalStore::collectGarbage(const GCOptions & options, GCResults & results)
|
||||
}
|
||||
}
|
||||
|
||||
if (state.options.action == GCOptions::gcReturnLive) {
|
||||
state.results.paths = state.alive;
|
||||
return;
|
||||
}
|
||||
|
||||
if (state.options.action == GCOptions::gcReturnDead) {
|
||||
state.results.paths = state.dead;
|
||||
return;
|
||||
}
|
||||
|
||||
/* Allow other processes to add to the store from here on. */
|
||||
fdGCLock.close();
|
||||
|
||||
/* Delete the invalidated paths now that the lock has been
|
||||
released. */
|
||||
foreach (PathSet::iterator, i, state.invalidated)
|
||||
deleteGarbage(state, *i);
|
||||
/* Delete the trash directory. */
|
||||
printMsg(lvlInfo, format("deleting `%1%'") % state.trashDir);
|
||||
deleteGarbage(state, state.trashDir);
|
||||
|
||||
/* Clean up the links directory. */
|
||||
if (options.action == GCOptions::gcDeleteDead || options.action == GCOptions::gcDeleteSpecific) {
|
||||
printMsg(lvlError, format("deleting unused links..."));
|
||||
removeUnusedLinks(state);
|
||||
}
|
||||
|
||||
/* While we're at it, vacuum the database. */
|
||||
if (options.action == GCOptions::gcDeleteDead) vacuumDB();
|
||||
}
|
||||
|
||||
|
||||
|
||||
+141
-96
@@ -10,47 +10,80 @@
|
||||
namespace nix {
|
||||
|
||||
|
||||
string nixStore = "/UNINIT";
|
||||
string nixDataDir = "/UNINIT";
|
||||
string nixLogDir = "/UNINIT";
|
||||
string nixStateDir = "/UNINIT";
|
||||
string nixDBPath = "/UNINIT";
|
||||
string nixConfDir = "/UNINIT";
|
||||
string nixLibexecDir = "/UNINIT";
|
||||
string nixBinDir = "/UNINIT";
|
||||
|
||||
bool keepFailed = false;
|
||||
bool keepGoing = false;
|
||||
bool tryFallback = false;
|
||||
Verbosity buildVerbosity = lvlError;
|
||||
unsigned int maxBuildJobs = 1;
|
||||
unsigned int buildCores = 1;
|
||||
bool readOnlyMode = false;
|
||||
string thisSystem = "unset";
|
||||
time_t maxSilentTime = 0;
|
||||
time_t buildTimeout = 0;
|
||||
Paths substituters;
|
||||
bool useBuildHook = true;
|
||||
bool printBuildTrace = false;
|
||||
/* The default location of the daemon socket, relative to nixStateDir.
|
||||
The socket is in a directory to allow you to control access to the
|
||||
Nix daemon by setting the mode/ownership of the directory
|
||||
appropriately. (This wouldn't work on the socket itself since it
|
||||
must be deleted and recreated on startup.) */
|
||||
#define DEFAULT_SOCKET_PATH "/daemon-socket/socket"
|
||||
|
||||
|
||||
static bool settingsRead = false;
|
||||
|
||||
static std::map<string, Strings> settings;
|
||||
|
||||
/* Overriden settings. */
|
||||
std::map<string, Strings> settingsCmdline;
|
||||
Settings settings;
|
||||
|
||||
|
||||
string & at(Strings & ss, unsigned int n)
|
||||
Settings::Settings()
|
||||
{
|
||||
Strings::iterator i = ss.begin();
|
||||
advance(i, n);
|
||||
return *i;
|
||||
keepFailed = false;
|
||||
keepGoing = false;
|
||||
tryFallback = false;
|
||||
buildVerbosity = lvlError;
|
||||
maxBuildJobs = 1;
|
||||
buildCores = 1;
|
||||
readOnlyMode = false;
|
||||
thisSystem = SYSTEM;
|
||||
maxSilentTime = 0;
|
||||
buildTimeout = 0;
|
||||
useBuildHook = true;
|
||||
printBuildTrace = false;
|
||||
reservedSize = 1024 * 1024;
|
||||
fsyncMetadata = true;
|
||||
useSQLiteWAL = true;
|
||||
syncBeforeRegistering = false;
|
||||
useSubstitutes = true;
|
||||
useChroot = false;
|
||||
dirsInChroot.insert("/dev");
|
||||
dirsInChroot.insert("/dev/pts");
|
||||
impersonateLinux26 = false;
|
||||
keepLog = true;
|
||||
compressLog = true;
|
||||
maxLogSize = 0;
|
||||
cacheFailure = false;
|
||||
pollInterval = 5;
|
||||
checkRootReachability = false;
|
||||
gcKeepOutputs = false;
|
||||
gcKeepDerivations = true;
|
||||
autoOptimiseStore = false;
|
||||
envKeepDerivations = false;
|
||||
lockCPU = getEnv("NIX_AFFINITY_HACK", "1") == "1";
|
||||
}
|
||||
|
||||
|
||||
static void readSettings()
|
||||
void Settings::processEnvironment()
|
||||
{
|
||||
nixStore = canonPath(getEnv("NIX_STORE_DIR", getEnv("NIX_STORE", NIX_STORE_DIR)));
|
||||
nixDataDir = canonPath(getEnv("NIX_DATA_DIR", NIX_DATA_DIR));
|
||||
nixLogDir = canonPath(getEnv("NIX_LOG_DIR", NIX_LOG_DIR));
|
||||
nixStateDir = canonPath(getEnv("NIX_STATE_DIR", NIX_STATE_DIR));
|
||||
nixDBPath = getEnv("NIX_DB_DIR", nixStateDir + "/db");
|
||||
nixConfDir = canonPath(getEnv("NIX_CONF_DIR", NIX_CONF_DIR));
|
||||
nixLibexecDir = canonPath(getEnv("NIX_LIBEXEC_DIR", NIX_LIBEXEC_DIR));
|
||||
nixBinDir = canonPath(getEnv("NIX_BIN_DIR", NIX_BIN_DIR));
|
||||
nixDaemonSocketFile = canonPath(nixStateDir + DEFAULT_SOCKET_PATH);
|
||||
|
||||
string subs = getEnv("NIX_SUBSTITUTERS", "default");
|
||||
if (subs == "default") {
|
||||
#if 0
|
||||
if (getEnv("NIX_OTHER_STORES") != "")
|
||||
substituters.push_back(nixLibexecDir + "/nix/substituters/copy-from-other-stores.pl");
|
||||
#endif
|
||||
substituters.push_back(nixLibexecDir + "/nix/substituters/download-using-manifests.pl");
|
||||
substituters.push_back(nixLibexecDir + "/nix/substituters/download-from-binary-cache.pl");
|
||||
} else
|
||||
substituters = tokenizeString<Strings>(subs, ":");
|
||||
}
|
||||
|
||||
|
||||
void Settings::loadConfFile()
|
||||
{
|
||||
Path settingsFile = (format("%1%/%2%") % nixConfDir % "nix.conf").str();
|
||||
if (!pathExists(settingsFile)) return;
|
||||
@@ -68,105 +101,117 @@ static void readSettings()
|
||||
if (hash != string::npos)
|
||||
line = string(line, 0, hash);
|
||||
|
||||
Strings tokens = tokenizeString(line);
|
||||
vector<string> tokens = tokenizeString<vector<string> >(line);
|
||||
if (tokens.empty()) continue;
|
||||
|
||||
if (tokens.size() < 2 || at(tokens, 1) != "=")
|
||||
if (tokens.size() < 2 || tokens[1] != "=")
|
||||
throw Error(format("illegal configuration line `%1%' in `%2%'") % line % settingsFile);
|
||||
|
||||
string name = at(tokens, 0);
|
||||
string name = tokens[0];
|
||||
|
||||
Strings::iterator i = tokens.begin();
|
||||
vector<string>::iterator i = tokens.begin();
|
||||
advance(i, 2);
|
||||
settings[name] = Strings(i, tokens.end());
|
||||
settings[name] = concatStringsSep(" ", Strings(i, tokens.end())); // FIXME: slow
|
||||
};
|
||||
|
||||
settings.insert(settingsCmdline.begin(), settingsCmdline.end());
|
||||
|
||||
settingsRead = true;
|
||||
}
|
||||
|
||||
|
||||
Strings querySetting(const string & name, const Strings & def)
|
||||
void Settings::set(const string & name, const string & value)
|
||||
{
|
||||
if (!settingsRead) readSettings();
|
||||
std::map<string, Strings>::iterator i = settings.find(name);
|
||||
return i == settings.end() ? def : i->second;
|
||||
settings[name] = value;
|
||||
overrides[name] = value;
|
||||
}
|
||||
|
||||
|
||||
string querySetting(const string & name, const string & def)
|
||||
void Settings::update()
|
||||
{
|
||||
Strings defs;
|
||||
defs.push_back(def);
|
||||
|
||||
Strings value = querySetting(name, defs);
|
||||
if (value.size() != 1)
|
||||
throw Error(format("configuration option `%1%' should not be a list") % name);
|
||||
|
||||
return value.front();
|
||||
get(tryFallback, "build-fallback");
|
||||
get(maxBuildJobs, "build-max-jobs");
|
||||
get(buildCores, "build-cores");
|
||||
get(thisSystem, "system");
|
||||
get(maxSilentTime, "build-max-silent-time");
|
||||
get(buildTimeout, "build-timeout");
|
||||
get(reservedSize, "gc-reserved-space");
|
||||
get(fsyncMetadata, "fsync-metadata");
|
||||
get(useSQLiteWAL, "use-sqlite-wal");
|
||||
get(syncBeforeRegistering, "sync-before-registering");
|
||||
get(useSubstitutes, "build-use-substitutes");
|
||||
get(buildUsersGroup, "build-users-group");
|
||||
get(useChroot, "build-use-chroot");
|
||||
get(dirsInChroot, "build-chroot-dirs");
|
||||
get(impersonateLinux26, "build-impersonate-linux-26");
|
||||
get(keepLog, "build-keep-log");
|
||||
get(compressLog, "build-compress-log");
|
||||
get(maxLogSize, "build-max-log-size");
|
||||
get(cacheFailure, "build-cache-failure");
|
||||
get(pollInterval, "build-poll-interval");
|
||||
get(checkRootReachability, "gc-check-reachability");
|
||||
get(gcKeepOutputs, "gc-keep-outputs");
|
||||
get(gcKeepDerivations, "gc-keep-derivations");
|
||||
get(autoOptimiseStore, "auto-optimise-store");
|
||||
get(envKeepDerivations, "env-keep-derivations");
|
||||
}
|
||||
|
||||
|
||||
bool queryBoolSetting(const string & name, bool def)
|
||||
void Settings::get(string & res, const string & name)
|
||||
{
|
||||
string v = querySetting(name, def ? "true" : "false");
|
||||
if (v == "true") return true;
|
||||
else if (v == "false") return false;
|
||||
SettingsMap::iterator i = settings.find(name);
|
||||
if (i == settings.end()) return;
|
||||
res = i->second;
|
||||
}
|
||||
|
||||
|
||||
void Settings::get(bool & res, const string & name)
|
||||
{
|
||||
SettingsMap::iterator i = settings.find(name);
|
||||
if (i == settings.end()) return;
|
||||
if (i->second == "true") res = true;
|
||||
else if (i->second == "false") res = false;
|
||||
else throw Error(format("configuration option `%1%' should be either `true' or `false', not `%2%'")
|
||||
% name % v);
|
||||
% name % i->second);
|
||||
}
|
||||
|
||||
|
||||
unsigned int queryIntSetting(const string & name, unsigned int def)
|
||||
void Settings::get(StringSet & res, const string & name)
|
||||
{
|
||||
int n;
|
||||
if (!string2Int(querySetting(name, int2String(def)), n) || n < 0)
|
||||
SettingsMap::iterator i = settings.find(name);
|
||||
if (i == settings.end()) return;
|
||||
res.clear();
|
||||
Strings ss = tokenizeString<Strings>(i->second);
|
||||
res.insert(ss.begin(), ss.end());
|
||||
}
|
||||
|
||||
|
||||
template<class N> void Settings::get(N & res, const string & name)
|
||||
{
|
||||
SettingsMap::iterator i = settings.find(name);
|
||||
if (i == settings.end()) return;
|
||||
if (!string2Int(i->second, res))
|
||||
throw Error(format("configuration setting `%1%' should have an integer value") % name);
|
||||
return n;
|
||||
}
|
||||
|
||||
|
||||
void overrideSetting(const string & name, const Strings & value)
|
||||
string Settings::pack()
|
||||
{
|
||||
if (settingsRead) settings[name] = value;
|
||||
settingsCmdline[name] = value;
|
||||
string s;
|
||||
foreach (SettingsMap::iterator, i, settings) {
|
||||
if (i->first.find('\n') != string::npos ||
|
||||
i->first.find('=') != string::npos ||
|
||||
i->second.find('\n') != string::npos)
|
||||
throw Error("illegal option name/value");
|
||||
s += i->first; s += '='; s += i->second; s += '\n';
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
|
||||
void reloadSettings()
|
||||
Settings::SettingsMap Settings::getOverrides()
|
||||
{
|
||||
settingsRead = false;
|
||||
settings.clear();
|
||||
return overrides;
|
||||
}
|
||||
|
||||
|
||||
void setDefaultsFromEnvironment()
|
||||
{
|
||||
/* Setup Nix paths. */
|
||||
nixStore = canonPath(getEnv("NIX_STORE_DIR", getEnv("NIX_STORE", NIX_STORE_DIR)));
|
||||
nixDataDir = canonPath(getEnv("NIX_DATA_DIR", NIX_DATA_DIR));
|
||||
nixLogDir = canonPath(getEnv("NIX_LOG_DIR", NIX_LOG_DIR));
|
||||
nixStateDir = canonPath(getEnv("NIX_STATE_DIR", NIX_STATE_DIR));
|
||||
nixDBPath = getEnv("NIX_DB_DIR", nixStateDir + "/db");
|
||||
nixConfDir = canonPath(getEnv("NIX_CONF_DIR", NIX_CONF_DIR));
|
||||
nixLibexecDir = canonPath(getEnv("NIX_LIBEXEC_DIR", NIX_LIBEXEC_DIR));
|
||||
nixBinDir = canonPath(getEnv("NIX_BIN_DIR", NIX_BIN_DIR));
|
||||
const string nixVersion = NIX_VERSION;
|
||||
|
||||
string subs = getEnv("NIX_SUBSTITUTERS", "default");
|
||||
if (subs == "default") {
|
||||
substituters.push_back(nixLibexecDir + "/nix/substituters/copy-from-other-stores.pl");
|
||||
substituters.push_back(nixLibexecDir + "/nix/substituters/download-using-manifests.pl");
|
||||
} else
|
||||
substituters = tokenizeString(subs, ":");
|
||||
|
||||
/* Get some settings from the configuration file. */
|
||||
thisSystem = querySetting("system", SYSTEM);
|
||||
maxBuildJobs = queryIntSetting("build-max-jobs", 1);
|
||||
buildCores = queryIntSetting("build-cores", 1);
|
||||
maxSilentTime = queryIntSetting("build-max-silent-time", 0);
|
||||
buildTimeout = queryIntSetting("build-timeout", 0);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
+183
-96
@@ -2,118 +2,205 @@
|
||||
|
||||
#include "types.hh"
|
||||
|
||||
#include <map>
|
||||
#include <sys/types.h>
|
||||
|
||||
|
||||
namespace nix {
|
||||
|
||||
|
||||
/* Path names. */
|
||||
struct Settings {
|
||||
|
||||
/* nixStore is the directory where we generally store atomic and
|
||||
derived files. */
|
||||
extern string nixStore;
|
||||
typedef std::map<string, string> SettingsMap;
|
||||
|
||||
extern string nixDataDir; /* !!! fix */
|
||||
Settings();
|
||||
|
||||
/* nixLogDir is the directory where we log various operations. */
|
||||
extern string nixLogDir;
|
||||
void processEnvironment();
|
||||
|
||||
/* nixStateDir is the directory where state is stored. */
|
||||
extern string nixStateDir;
|
||||
void loadConfFile();
|
||||
|
||||
/* nixDBPath is the path name of our Berkeley DB environment. */
|
||||
extern string nixDBPath;
|
||||
void set(const string & name, const string & value);
|
||||
|
||||
/* nixConfDir is the directory where configuration files are
|
||||
stored. */
|
||||
extern string nixConfDir;
|
||||
void update();
|
||||
|
||||
/* nixLibexecDir is the directory where internal helper programs are
|
||||
stored. */
|
||||
extern string nixLibexecDir;
|
||||
string pack();
|
||||
|
||||
/* nixBinDir is the directory where the main programs are stored. */
|
||||
extern string nixBinDir;
|
||||
SettingsMap getOverrides();
|
||||
|
||||
/* The directory where we store sources and derived files. */
|
||||
Path nixStore;
|
||||
|
||||
Path nixDataDir; /* !!! fix */
|
||||
|
||||
/* The directory where we log various operations. */
|
||||
Path nixLogDir;
|
||||
|
||||
/* The directory where state is stored. */
|
||||
Path nixStateDir;
|
||||
|
||||
/* The directory where we keep the SQLite database. */
|
||||
Path nixDBPath;
|
||||
|
||||
/* The directory where configuration files are stored. */
|
||||
Path nixConfDir;
|
||||
|
||||
/* The directory where internal helper programs are stored. */
|
||||
Path nixLibexecDir;
|
||||
|
||||
/* The directory where the main programs are stored. */
|
||||
Path nixBinDir;
|
||||
|
||||
/* File name of the socket the daemon listens to. */
|
||||
Path nixDaemonSocketFile;
|
||||
|
||||
/* Whether to keep temporary directories of failed builds. */
|
||||
bool keepFailed;
|
||||
|
||||
/* Whether to keep building subgoals when a sibling (another
|
||||
subgoal of the same goal) fails. */
|
||||
bool keepGoing;
|
||||
|
||||
/* Whether, if we cannot realise the known closure corresponding
|
||||
to a derivation, we should try to normalise the derivation
|
||||
instead. */
|
||||
bool tryFallback;
|
||||
|
||||
/* Verbosity level for build output. */
|
||||
Verbosity buildVerbosity;
|
||||
|
||||
/* Maximum number of parallel build jobs. 0 means unlimited. */
|
||||
unsigned int maxBuildJobs;
|
||||
|
||||
/* Number of CPU cores to utilize in parallel within a build,
|
||||
i.e. by passing this number to Make via '-j'. 0 means that the
|
||||
number of actual CPU cores on the local host ought to be
|
||||
auto-detected. */
|
||||
unsigned int buildCores;
|
||||
|
||||
/* Read-only mode. Don't copy stuff to the store, don't change
|
||||
the database. */
|
||||
bool readOnlyMode;
|
||||
|
||||
/* The canonical system name, as returned by config.guess. */
|
||||
string thisSystem;
|
||||
|
||||
/* The maximum time in seconds that a builer can go without
|
||||
producing any output on stdout/stderr before it is killed. 0
|
||||
means infinity. */
|
||||
time_t maxSilentTime;
|
||||
|
||||
/* The maximum duration in seconds that a builder can run. 0
|
||||
means infinity. */
|
||||
time_t buildTimeout;
|
||||
|
||||
/* The substituters. There are programs that can somehow realise
|
||||
a store path without building, e.g., by downloading it or
|
||||
copying it from a CD. */
|
||||
Paths substituters;
|
||||
|
||||
/* Whether to use build hooks (for distributed builds). Sometimes
|
||||
users want to disable this from the command-line. */
|
||||
bool useBuildHook;
|
||||
|
||||
/* Whether buildDerivations() should print out lines on stderr in
|
||||
a fixed format to allow its progress to be monitored. Each
|
||||
line starts with a "@". The following are defined:
|
||||
|
||||
@ build-started <drvpath> <outpath> <system> <logfile>
|
||||
@ build-failed <drvpath> <outpath> <exitcode> <error text>
|
||||
@ build-succeeded <drvpath> <outpath>
|
||||
@ substituter-started <outpath> <substituter>
|
||||
@ substituter-failed <outpath> <exitcode> <error text>
|
||||
@ substituter-succeeded <outpath>
|
||||
|
||||
Best combined with --no-build-output, otherwise stderr might
|
||||
conceivably contain lines in this format printed by the
|
||||
builders. */
|
||||
bool printBuildTrace;
|
||||
|
||||
/* Amount of reserved space for the garbage collector
|
||||
(/nix/var/nix/db/reserved). */
|
||||
off_t reservedSize;
|
||||
|
||||
/* Whether SQLite should use fsync. */
|
||||
bool fsyncMetadata;
|
||||
|
||||
/* Whether SQLite should use WAL mode. */
|
||||
bool useSQLiteWAL;
|
||||
|
||||
/* Whether to call sync() before registering a path as valid. */
|
||||
bool syncBeforeRegistering;
|
||||
|
||||
/* Whether to use substitutes. */
|
||||
bool useSubstitutes;
|
||||
|
||||
/* The Unix group that contains the build users. */
|
||||
string buildUsersGroup;
|
||||
|
||||
/* Whether to build in chroot. */
|
||||
bool useChroot;
|
||||
|
||||
/* The directories from the host filesystem to be included in the
|
||||
chroot. */
|
||||
StringSet dirsInChroot;
|
||||
|
||||
/* Whether to impersonate a Linux 2.6 machine on newer kernels. */
|
||||
bool impersonateLinux26;
|
||||
|
||||
/* Whether to store build logs. */
|
||||
bool keepLog;
|
||||
|
||||
/* Whether to compress logs. */
|
||||
bool compressLog;
|
||||
|
||||
/* Maximum number of bytes a builder can write to stdout/stderr
|
||||
before being killed (0 means no limit). */
|
||||
unsigned long maxLogSize;
|
||||
|
||||
/* Whether to cache build failures. */
|
||||
bool cacheFailure;
|
||||
|
||||
/* How often (in seconds) to poll for locks. */
|
||||
unsigned int pollInterval;
|
||||
|
||||
/* Whether to check if new GC roots can in fact be found by the
|
||||
garbage collector. */
|
||||
bool checkRootReachability;
|
||||
|
||||
/* Whether the garbage collector should keep outputs of live
|
||||
derivations. */
|
||||
bool gcKeepOutputs;
|
||||
|
||||
/* Whether the garbage collector should keep derivers of live
|
||||
paths. */
|
||||
bool gcKeepDerivations;
|
||||
|
||||
/* Whether to automatically replace files with identical contents
|
||||
with hard links. */
|
||||
bool autoOptimiseStore;
|
||||
|
||||
/* Whether to add derivations as a dependency of user environments
|
||||
(to prevent them from being GCed). */
|
||||
bool envKeepDerivations;
|
||||
|
||||
/* Whether to lock the Nix client and worker to the same CPU. */
|
||||
bool lockCPU;
|
||||
|
||||
private:
|
||||
SettingsMap settings, overrides;
|
||||
|
||||
void get(string & res, const string & name);
|
||||
void get(bool & res, const string & name);
|
||||
void get(StringSet & res, const string & name);
|
||||
template<class N> void get(N & res, const string & name);
|
||||
};
|
||||
|
||||
|
||||
/* Misc. global flags. */
|
||||
|
||||
/* Whether to keep temporary directories of failed builds. */
|
||||
extern bool keepFailed;
|
||||
|
||||
/* Whether to keep building subgoals when a sibling (another subgoal
|
||||
of the same goal) fails. */
|
||||
extern bool keepGoing;
|
||||
|
||||
/* Whether, if we cannot realise the known closure corresponding to a
|
||||
derivation, we should try to normalise the derivation instead. */
|
||||
extern bool tryFallback;
|
||||
|
||||
/* Verbosity level for build output. */
|
||||
extern Verbosity buildVerbosity;
|
||||
|
||||
/* Maximum number of parallel build jobs. 0 means unlimited. */
|
||||
extern unsigned int maxBuildJobs;
|
||||
|
||||
/* Number of CPU cores to utilize in parallel within a build, i.e. by passing
|
||||
this number to Make via '-j'. 0 means that the number of actual CPU cores on
|
||||
the local host ought to be auto-detected. */
|
||||
extern unsigned int buildCores;
|
||||
|
||||
/* Read-only mode. Don't copy stuff to the store, don't change the
|
||||
database. */
|
||||
extern bool readOnlyMode;
|
||||
|
||||
/* The canonical system name, as returned by config.guess. */
|
||||
extern string thisSystem;
|
||||
|
||||
/* The maximum time in seconds that a builer can go without producing
|
||||
any output on stdout/stderr before it is killed. 0 means
|
||||
infinity. */
|
||||
extern time_t maxSilentTime;
|
||||
|
||||
/* The maximum duration in seconds that a builder can run. 0 means
|
||||
infinity. */
|
||||
extern time_t buildTimeout;
|
||||
|
||||
/* The substituters. There are programs that can somehow realise a
|
||||
store path without building, e.g., by downloading it or copying it
|
||||
from a CD. */
|
||||
extern Paths substituters;
|
||||
|
||||
/* Whether to use build hooks (for distributed builds). Sometimes
|
||||
users want to disable this from the command-line. */
|
||||
extern bool useBuildHook;
|
||||
|
||||
/* Whether buildDerivations() should print out lines on stderr in a
|
||||
fixed format to allow its progress to be monitored. Each line
|
||||
starts with a "@". The following are defined:
|
||||
|
||||
@ build-started <drvpath> <outpath> <system> <logfile>
|
||||
@ build-failed <drvpath> <outpath> <exitcode> <error text>
|
||||
@ build-succeeded <drvpath> <outpath>
|
||||
@ substituter-started <outpath> <substituter>
|
||||
@ substituter-failed <outpath> <exitcode> <error text>
|
||||
@ substituter-succeeded <outpath>
|
||||
|
||||
Best combined with --no-build-output, otherwise stderr might
|
||||
conceivably contain lines in this format printed by the builders.
|
||||
*/
|
||||
extern bool printBuildTrace;
|
||||
// FIXME: don't use a global variable.
|
||||
extern Settings settings;
|
||||
|
||||
|
||||
Strings querySetting(const string & name, const Strings & def);
|
||||
|
||||
string querySetting(const string & name, const string & def);
|
||||
|
||||
bool queryBoolSetting(const string & name, bool def);
|
||||
|
||||
unsigned int queryIntSetting(const string & name, unsigned int def);
|
||||
|
||||
void overrideSetting(const string & name, const Strings & value);
|
||||
|
||||
void reloadSettings();
|
||||
|
||||
void setDefaultsFromEnvironment();
|
||||
extern const string nixVersion;
|
||||
|
||||
|
||||
}
|
||||
|
||||
+517
-202
File diff suppressed because it is too large
Load Diff
+95
-57
@@ -17,8 +17,8 @@ namespace nix {
|
||||
/* Nix store and database schema version. Version 1 (or 0) was Nix <=
|
||||
0.7. Version 2 was Nix 0.8 and 0.9. Version 3 is Nix 0.10.
|
||||
Version 4 is Nix 0.11. Version 5 is Nix 0.12-0.16. Version 6 is
|
||||
Nix 1.0. */
|
||||
const int nixSchemaVersion = 6;
|
||||
Nix 1.0. Version 7 is Nix 1.3. */
|
||||
const int nixSchemaVersion = 7;
|
||||
|
||||
|
||||
extern string drvsLogDir;
|
||||
@@ -44,8 +44,12 @@ struct OptimiseStats
|
||||
|
||||
struct RunningSubstituter
|
||||
{
|
||||
Path program;
|
||||
Pid pid;
|
||||
AutoCloseFD to, from;
|
||||
AutoCloseFD to, from, error;
|
||||
FdSource fromBuf;
|
||||
bool disabled;
|
||||
RunningSubstituter() : disabled(false) { };
|
||||
};
|
||||
|
||||
|
||||
@@ -75,17 +79,16 @@ struct SQLiteStmt
|
||||
void bind64(long long value);
|
||||
void bind();
|
||||
};
|
||||
|
||||
|
||||
|
||||
class LocalStore : public StoreAPI
|
||||
{
|
||||
private:
|
||||
bool substitutablePathsLoaded;
|
||||
PathSet substitutablePaths;
|
||||
|
||||
typedef std::map<Path, RunningSubstituter> RunningSubstituters;
|
||||
RunningSubstituters runningSubstituters;
|
||||
|
||||
|
||||
Path linksDir;
|
||||
|
||||
public:
|
||||
|
||||
/* Initialise the local store, upgrading the schema if
|
||||
@@ -93,13 +96,15 @@ public:
|
||||
LocalStore(bool reserveSpace = true);
|
||||
|
||||
~LocalStore();
|
||||
|
||||
|
||||
/* Implementations of abstract store API methods. */
|
||||
|
||||
|
||||
bool isValidPath(const Path & path);
|
||||
|
||||
PathSet queryValidPaths();
|
||||
|
||||
PathSet queryValidPaths(const PathSet & paths);
|
||||
|
||||
PathSet queryAllValidPaths();
|
||||
|
||||
ValidPathInfo queryPathInfo(const Path & path);
|
||||
|
||||
Hash queryPathHash(const Path & path);
|
||||
@@ -110,55 +115,49 @@ public:
|
||||
|
||||
Path queryDeriver(const Path & path);
|
||||
|
||||
/* Return all currently valid derivations that have `path' as an
|
||||
output. (Note that the result of `queryDeriver()' is the
|
||||
derivation that was actually used to produce `path', which may
|
||||
not exist anymore.) */
|
||||
PathSet queryValidDerivers(const Path & path);
|
||||
|
||||
PathSet queryDerivationOutputs(const Path & path);
|
||||
|
||||
StringSet queryDerivationOutputNames(const Path & path);
|
||||
|
||||
Path queryPathFromHashPart(const string & hashPart);
|
||||
|
||||
PathSet querySubstitutablePaths();
|
||||
|
||||
bool hasSubstitutes(const Path & path);
|
||||
|
||||
bool querySubstitutablePathInfo(const Path & path,
|
||||
SubstitutablePathInfo & info);
|
||||
|
||||
bool querySubstitutablePathInfo(const Path & substituter,
|
||||
const Path & path, SubstitutablePathInfo & info);
|
||||
|
||||
Path queryPathFromHashPart(const string & hashPart);
|
||||
|
||||
PathSet querySubstitutablePaths(const PathSet & paths);
|
||||
|
||||
void querySubstitutablePathInfos(const Path & substituter,
|
||||
PathSet & paths, SubstitutablePathInfos & infos);
|
||||
|
||||
void querySubstitutablePathInfos(const PathSet & paths,
|
||||
SubstitutablePathInfos & infos);
|
||||
|
||||
Path addToStore(const Path & srcPath,
|
||||
bool recursive = true, HashType hashAlgo = htSHA256,
|
||||
PathFilter & filter = defaultPathFilter);
|
||||
PathFilter & filter = defaultPathFilter, bool repair = false);
|
||||
|
||||
/* Like addToStore(), but the contents of the path are contained
|
||||
in `dump', which is either a NAR serialisation (if recursive ==
|
||||
true) or simply the contents of a regular file (if recursive ==
|
||||
false). */
|
||||
Path addToStoreFromDump(const string & dump, const string & name,
|
||||
bool recursive = true, HashType hashAlgo = htSHA256);
|
||||
bool recursive = true, HashType hashAlgo = htSHA256, bool repair = false);
|
||||
|
||||
Path addTextToStore(const string & name, const string & s,
|
||||
const PathSet & references);
|
||||
const PathSet & references, bool repair = false);
|
||||
|
||||
void exportPath(const Path & path, bool sign,
|
||||
Sink & sink);
|
||||
|
||||
Paths importPaths(bool requireSignature, Source & source);
|
||||
|
||||
void buildPaths(const PathSet & paths);
|
||||
|
||||
void buildPaths(const PathSet & paths, bool repair = false);
|
||||
|
||||
void ensurePath(const Path & path);
|
||||
|
||||
void addTempRoot(const Path & path);
|
||||
|
||||
void addIndirectRoot(const Path & path);
|
||||
|
||||
|
||||
void syncWithGC();
|
||||
|
||||
Roots findRoots();
|
||||
@@ -167,14 +166,18 @@ public:
|
||||
|
||||
/* Optimise the disk space usage of the Nix store by hard-linking
|
||||
files with the same contents. */
|
||||
void optimiseStore(bool dryRun, OptimiseStats & stats);
|
||||
void optimiseStore(OptimiseStats & stats);
|
||||
|
||||
/* Check the integrity of the Nix store. */
|
||||
void verifyStore(bool checkContents);
|
||||
/* Optimise a single store path. */
|
||||
void optimisePath(const Path & path);
|
||||
|
||||
/* Check the integrity of the Nix store. Returns true if errors
|
||||
remain. */
|
||||
bool verifyStore(bool checkContents, bool repair);
|
||||
|
||||
/* Register the validity of a path, i.e., that `path' exists, that
|
||||
the paths referenced by it exists, and in the case of an output
|
||||
path of a derivation, that it has been produced by a succesful
|
||||
path of a derivation, that it has been produced by a successful
|
||||
execution of the derivation (or something equivalent). Also
|
||||
register the hash of the file system contents of the path. The
|
||||
hash must be a SHA-256 hash. */
|
||||
@@ -193,6 +196,20 @@ public:
|
||||
|
||||
void clearFailedPaths(const PathSet & paths);
|
||||
|
||||
void vacuumDB();
|
||||
|
||||
/* Repair the contents of the given path by redownloading it using
|
||||
a substituter (if available). */
|
||||
void repairPath(const Path & path);
|
||||
|
||||
/* Check whether the given valid path exists and has the right
|
||||
contents. */
|
||||
bool pathContentsGood(const Path & path);
|
||||
|
||||
void markContentsGood(const Path & path);
|
||||
|
||||
void setSubstituterEnv();
|
||||
|
||||
private:
|
||||
|
||||
Path schemaPath;
|
||||
@@ -220,56 +237,80 @@ private:
|
||||
SQLiteStmt stmtQueryDerivationOutputs;
|
||||
SQLiteStmt stmtQueryPathFromHashPart;
|
||||
|
||||
/* Cache for pathContentsGood(). */
|
||||
std::map<Path, bool> pathContentsGoodCache;
|
||||
|
||||
bool didSetSubstituterEnv;
|
||||
|
||||
int getSchema();
|
||||
|
||||
void openDB(bool create);
|
||||
|
||||
void makeStoreWritable();
|
||||
|
||||
unsigned long long queryValidPathId(const Path & path);
|
||||
|
||||
unsigned long long addValidPath(const ValidPathInfo & info, bool checkOutputs = true);
|
||||
|
||||
|
||||
void addReference(unsigned long long referrer, unsigned long long reference);
|
||||
|
||||
|
||||
void appendReferrer(const Path & from, const Path & to, bool lock);
|
||||
|
||||
|
||||
void rewriteReferrers(const Path & path, bool purge, PathSet referrers);
|
||||
|
||||
void invalidatePath(const Path & path);
|
||||
|
||||
/* Delete a path from the Nix store. */
|
||||
void invalidatePathChecked(const Path & path);
|
||||
|
||||
|
||||
void verifyPath(const Path & path, const PathSet & store,
|
||||
PathSet & done, PathSet & validPaths);
|
||||
PathSet & done, PathSet & validPaths, bool repair, bool & errors);
|
||||
|
||||
void updatePathInfo(const ValidPathInfo & info);
|
||||
|
||||
void upgradeStore6();
|
||||
void upgradeStore7();
|
||||
PathSet queryValidPathsOld();
|
||||
ValidPathInfo queryPathInfoOld(const Path & path);
|
||||
|
||||
struct GCState;
|
||||
|
||||
void deleteGarbage(GCState & state, const Path & path);
|
||||
|
||||
bool tryToDelete(GCState & state, const Path & path);
|
||||
|
||||
|
||||
void tryToDelete(GCState & state, const Path & path);
|
||||
|
||||
bool canReachRoot(GCState & state, PathSet & visited, const Path & path);
|
||||
|
||||
void deletePathRecursive(GCState & state, const Path & path);
|
||||
|
||||
bool isActiveTempFile(const GCState & state,
|
||||
const Path & path, const string & suffix);
|
||||
|
||||
|
||||
int openGCLock(LockType lockType);
|
||||
|
||||
|
||||
void removeUnusedLinks(const GCState & state);
|
||||
|
||||
void startSubstituter(const Path & substituter,
|
||||
RunningSubstituter & runningSubstituter);
|
||||
|
||||
string getLineFromSubstituter(RunningSubstituter & run);
|
||||
|
||||
template<class T> T getIntLineFromSubstituter(RunningSubstituter & run);
|
||||
|
||||
Path createTempDirInStore();
|
||||
|
||||
Path importPath(bool requireSignature, Source & source);
|
||||
|
||||
|
||||
void checkDerivationOutputs(const Path & drvPath, const Derivation & drv);
|
||||
|
||||
void optimisePath_(OptimiseStats & stats, const Path & path);
|
||||
};
|
||||
|
||||
|
||||
typedef std::pair<dev_t, ino_t> Inode;
|
||||
typedef set<Inode> InodesSeen;
|
||||
|
||||
|
||||
/* "Fix", or canonicalise, the meta-data of the files in a store path
|
||||
after it has been built. In particular:
|
||||
- the last modification date on each file is set to 1 (i.e.,
|
||||
@@ -278,15 +319,13 @@ private:
|
||||
without execute permission; setuid bits etc. are cleared)
|
||||
- the owner and group are set to the Nix user and group, if we're
|
||||
in a setuid Nix installation. */
|
||||
void canonicalisePathMetaData(const Path & path);
|
||||
void canonicalisePathMetaData(const Path & path, uid_t fromUid, InodesSeen & inodesSeen);
|
||||
void canonicalisePathMetaData(const Path & path, uid_t fromUid);
|
||||
|
||||
void canonicalisePathMetaData(const Path & path, bool recurse);
|
||||
void canonicaliseTimestampAndPermissions(const Path & path);
|
||||
|
||||
MakeError(PathInUse, Error);
|
||||
|
||||
/* Whether we are in build users mode. */
|
||||
bool haveBuildUsers();
|
||||
|
||||
/* Whether we are root. */
|
||||
bool amPrivileged();
|
||||
|
||||
@@ -295,9 +334,8 @@ void getOwnership(const Path & path);
|
||||
|
||||
/* Like deletePath(), but changes the ownership of `path' using the
|
||||
setuid wrapper if necessary (and possible). */
|
||||
void deletePathWrapped(const Path & path,
|
||||
unsigned long long & bytesFreed, unsigned long long & blocksFreed);
|
||||
void deletePathWrapped(const Path & path, unsigned long long & bytesFreed);
|
||||
|
||||
void deletePathWrapped(const Path & path);
|
||||
|
||||
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user