Compare commits
708 Commits
0.11-relea
...
sqlite
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
543988572e | ||
|
|
c0340eec5a | ||
|
|
0304fda3cf | ||
|
|
aeae0beba4 | ||
|
|
3854fc9b42 | ||
|
|
9db190eb31 | ||
|
|
d6c8b995c5 | ||
|
|
c931a7aec5 | ||
|
|
b1eb252172 | ||
|
|
eac93d6efe | ||
|
|
a0be433fec | ||
|
|
f1a6b97639 | ||
|
|
3dd02580e3 | ||
|
|
d787285af9 | ||
|
|
5833243c92 | ||
|
|
100becf8d1 | ||
|
|
d7ca6f44eb | ||
|
|
542fc69062 | ||
|
|
4d57776813 | ||
|
|
e4720b1a79 | ||
|
|
7d0444e244 | ||
|
|
8062d3af30 | ||
|
|
de79d23f76 | ||
|
|
365f3028dd | ||
|
|
f42a505ab7 | ||
|
|
77f7a6d591 | ||
|
|
d3bba0c2d8 | ||
|
|
9737a7eba0 | ||
|
|
bd48fd97f6 | ||
|
|
703e5a2ce2 | ||
|
|
812fae424e | ||
|
|
d92ccbf1ac | ||
|
|
1a211d812f | ||
|
|
a4f0365b2d | ||
|
|
3d38a49840 | ||
|
|
f69626ed3e | ||
|
|
a07c68f05e | ||
|
|
bf658f016f | ||
|
|
1e24cbaba3 | ||
|
|
bdf089f463 | ||
|
|
06699d4219 | ||
|
|
5693b8a7e2 | ||
|
|
e60c962fb8 | ||
|
|
1db6259076 | ||
|
|
a3883cbd28 | ||
|
|
fb9368b5a0 | ||
|
|
4aced7f8d0 | ||
|
|
26def5392f | ||
|
|
3d71c8013e | ||
|
|
14fbf85380 | ||
|
|
0c4828ea05 | ||
|
|
e11e6fb1c6 | ||
|
|
8a788e38ac | ||
|
|
11ccd44e95 | ||
|
|
43535499f3 | ||
|
|
e0b7fb8f27 | ||
|
|
2dc6d50941 | ||
|
|
0b305c534f | ||
|
|
a247d20604 | ||
|
|
02934b1200 | ||
|
|
b2ba62170c | ||
|
|
8ac06726b9 | ||
|
|
3f66cfb96b | ||
|
|
4dee289550 | ||
|
|
cf7e645a48 | ||
|
|
41c45a9b31 | ||
|
|
64c3325b0b | ||
|
|
76feaf016a | ||
|
|
e879a0371b | ||
|
|
b0c11cda7e | ||
|
|
64fd29855a | ||
|
|
8dadcede65 | ||
|
|
7119d38287 | ||
|
|
705868a8a9 | ||
|
|
95f4f2cf61 | ||
|
|
36a23e86b6 | ||
|
|
bfa6ee7d91 | ||
|
|
71dfe4b90b | ||
|
|
450837bcc8 | ||
|
|
4aa9245083 | ||
|
|
923736df38 | ||
|
|
e4907411c2 | ||
|
|
bf0dde9597 | ||
|
|
e2e168f7c2 | ||
|
|
80e722278c | ||
|
|
20acd43c25 | ||
|
|
766f708418 | ||
|
|
df50916e46 | ||
|
|
e41ecbf730 | ||
|
|
e437b08250 | ||
|
|
1a396f3789 | ||
|
|
95deba581d | ||
|
|
1e5f5ea2e9 | ||
|
|
034f608e00 | ||
|
|
f58f51f380 | ||
|
|
955d11aae7 | ||
|
|
c67eccc26d | ||
|
|
2c8e070e5d | ||
|
|
ed133e6e64 | ||
|
|
2de17f4edc | ||
|
|
86f65edf4e | ||
|
|
b75e1043a3 | ||
|
|
8ec6594d6d | ||
|
|
12721a3a9a | ||
|
|
5fb824e896 | ||
|
|
5c5ab2bc12 | ||
|
|
6846ed8b44 | ||
|
|
5f9aad44ca | ||
|
|
d7875d1648 | ||
|
|
587dc8aa00 | ||
|
|
fd9c77dfc7 | ||
|
|
750be19ae8 | ||
|
|
7f893b7a43 | ||
|
|
315d8fbd75 | ||
|
|
6d6200f37a | ||
|
|
7af6a2fd71 | ||
|
|
532d766c27 | ||
|
|
7e043d28a6 | ||
|
|
60b632b173 | ||
|
|
a0d29040f7 | ||
|
|
af09fe12dd | ||
|
|
d63375d529 | ||
|
|
4c21c016c5 | ||
|
|
bcec46057c | ||
|
|
a17071fef1 | ||
|
|
560ab22f7d | ||
|
|
8b7f8b56f1 | ||
|
|
87ef5907e9 | ||
|
|
819548d92f | ||
|
|
3e5e0faf9c | ||
|
|
bf87cc44b4 | ||
|
|
b57189174f | ||
|
|
f16fe2af8d | ||
|
|
d1f6c0cbe3 | ||
|
|
07ca66cf24 | ||
|
|
1ab67cf437 | ||
|
|
89865da76d | ||
|
|
a443c7573b | ||
|
|
8bcdd36f10 | ||
|
|
da52f8bea0 | ||
|
|
7343e6c8ae | ||
|
|
b92a2e5cc2 | ||
|
|
93cd5a4a13 | ||
|
|
32539e41d5 | ||
|
|
b2235d81d1 | ||
|
|
aa45027818 | ||
|
|
8032f26ca0 | ||
|
|
bd25ac2260 | ||
|
|
81a4b4e49b | ||
|
|
1a8eb6e3ec | ||
|
|
83dfa89870 | ||
|
|
01e58adce0 | ||
|
|
83d7b89660 | ||
|
|
e2d5e40f4f | ||
|
|
84ce7ac76f | ||
|
|
4750065ada | ||
|
|
a0e3b84fac | ||
|
|
f92c9a0ac5 | ||
|
|
4bab25a28d | ||
|
|
7fa338f4ba | ||
|
|
c82782f9a5 | ||
|
|
c778ed1768 | ||
|
|
ef337f7089 | ||
|
|
6199f9b93e | ||
|
|
2398af13c5 | ||
|
|
d77331d32f | ||
|
|
2be6118f4c | ||
|
|
0bc468f195 | ||
|
|
ee0384fb96 | ||
|
|
ebade9ff8b | ||
|
|
2d7636529f | ||
|
|
6bbfe95e30 | ||
|
|
7148df7971 | ||
|
|
6f0f16497a | ||
|
|
cae4efdca3 | ||
|
|
0777448ca6 | ||
|
|
fe2d869e04 | ||
|
|
d66ea83a76 | ||
|
|
f3b8833a48 | ||
|
|
efc7a579e8 | ||
|
|
55b5ddd3ca | ||
|
|
b7ff69eb7c | ||
|
|
5c31995bb8 | ||
|
|
8bb0210fea | ||
|
|
8ca4a001cb | ||
|
|
497e4ad126 | ||
|
|
02c1dac909 | ||
|
|
04c4bd3624 | ||
|
|
e41b5828db | ||
|
|
d39d3c6264 | ||
|
|
267dc693d2 | ||
|
|
81de12bc8f | ||
|
|
110d155778 | ||
|
|
9985230c00 | ||
|
|
816dd3f061 | ||
|
|
011b5da0f4 | ||
|
|
85d13c8f93 | ||
|
|
816f9c0f6f | ||
|
|
7d47498b5e | ||
|
|
ac1e8f40d4 | ||
|
|
10e8b1fd15 | ||
|
|
0d272fca79 | ||
|
|
d4f0b0fc6c | ||
|
|
a60317f20f | ||
|
|
4d6ad5be17 | ||
|
|
ed711f73bc | ||
|
|
db90b88e65 | ||
|
|
4e49002576 | ||
|
|
c3f228f296 | ||
|
|
aac5fcfbb5 | ||
|
|
f3dc7ab877 | ||
|
|
b7b3dd55f9 | ||
|
|
7e048eddf5 | ||
|
|
af2a372bb0 | ||
|
|
9a64454faa | ||
|
|
fc92244ba8 | ||
|
|
a353aef0b1 | ||
|
|
a5ece7d016 | ||
|
|
c172274e17 | ||
|
|
7b851915bf | ||
|
|
95cc417d76 | ||
|
|
71f026292b | ||
|
|
dc31305b38 | ||
|
|
979f163615 | ||
|
|
d8cd3115d8 | ||
|
|
55e207b2dc | ||
|
|
3d94be61ea | ||
|
|
5187678913 | ||
|
|
f061086a93 | ||
|
|
09381cccff | ||
|
|
13c2adc897 | ||
|
|
4c53ca2692 | ||
|
|
471419d1fa | ||
|
|
eb07a4f1ee | ||
|
|
7f19e03c65 | ||
|
|
47df476daa | ||
|
|
c9170be2bd | ||
|
|
c3aa615a5f | ||
|
|
5b72d8a749 | ||
|
|
d78a05ab40 | ||
|
|
31428c3a06 | ||
|
|
52090d2418 | ||
|
|
e3f32ac5af | ||
|
|
807a67bc74 | ||
|
|
392811eb8f | ||
|
|
d96cdcea6b | ||
|
|
3d2b835f30 | ||
|
|
45d822f29c | ||
|
|
cad8726b2c | ||
|
|
8da118e4d0 | ||
|
|
c2ba4313fb | ||
|
|
25eedf085d | ||
|
|
3c9f8fc9b6 | ||
|
|
f450384ded | ||
|
|
ef8bd919fc | ||
|
|
8a10360c91 | ||
|
|
7482349fe8 | ||
|
|
0fd3648d34 | ||
|
|
b70bd8fe56 | ||
|
|
d31c59eb17 | ||
|
|
e8f7978274 | ||
|
|
0910ae9568 | ||
|
|
90039e0863 | ||
|
|
71be50cc25 | ||
|
|
3bfd3a4e95 | ||
|
|
f71ea9c911 | ||
|
|
141294ff38 | ||
|
|
74299c1cfb | ||
|
|
c4cfb392d3 | ||
|
|
8e3d98eb41 | ||
|
|
f0c473c5f7 | ||
|
|
fe1b8781ae | ||
|
|
741b7577c1 | ||
|
|
2fb0df83e9 | ||
|
|
13cce8ec45 | ||
|
|
e020d80e4e | ||
|
|
05e15049a5 | ||
|
|
070057c1b9 | ||
|
|
c752c9f41a | ||
|
|
2e8eaca573 | ||
|
|
03afc34805 | ||
|
|
1a65142ec4 | ||
|
|
4c356acd04 | ||
|
|
44f6e6de77 | ||
|
|
2e4ef03aa3 | ||
|
|
05fbf61f0e | ||
|
|
04791840f4 | ||
|
|
bc6f7fc139 | ||
|
|
fb6e223ddc | ||
|
|
5ff87c982e | ||
|
|
e14e2399ed | ||
|
|
158aa89317 | ||
|
|
56af8e86e3 | ||
|
|
3f9e647ae8 | ||
|
|
d8c5745c41 | ||
|
|
e07d7284a2 | ||
|
|
5414b3b2db | ||
|
|
594eaddd11 | ||
|
|
966ffb29a7 | ||
|
|
24035b98b1 | ||
|
|
e42401ee7b | ||
|
|
af565c348a | ||
|
|
e33f67ff0b | ||
|
|
84a4dd5ff0 | ||
|
|
cfe742cfc5 | ||
|
|
6baa2a2f5e | ||
|
|
9fd85c94de | ||
|
|
fefd467539 | ||
|
|
21b134b4e5 | ||
|
|
a3c63d0d6c | ||
|
|
90b6352d0a | ||
|
|
fae0427324 | ||
|
|
fa6a4fcb11 | ||
|
|
5954eadf67 | ||
|
|
bb82310dba | ||
|
|
69d9df7fe6 | ||
|
|
462bd50aef | ||
|
|
8520de4720 | ||
|
|
dc6d1ec67e | ||
|
|
63b09c5e41 | ||
|
|
0efc986ba1 | ||
|
|
b4e6d98fc3 | ||
|
|
7db2831d3a | ||
|
|
5ccb6f64f4 | ||
|
|
2b20318b0e | ||
|
|
9cda616949 | ||
|
|
c4d388add4 | ||
|
|
103cfee056 | ||
|
|
299ff64812 | ||
|
|
1930570ad9 | ||
|
|
9c9a88e9e2 | ||
|
|
762cee72cc | ||
|
|
268f9aaf28 | ||
|
|
836e5b6f57 | ||
|
|
77cb9e3fb1 | ||
|
|
885e22b16e | ||
|
|
cfb09e0fad | ||
|
|
e0305bb7a8 | ||
|
|
a053d2d8e5 | ||
|
|
dbddac0fe9 | ||
|
|
c1a07f9445 | ||
|
|
eaaa13ce47 | ||
|
|
68e55cd9da | ||
|
|
3b3e1025c3 | ||
|
|
20186a4079 | ||
|
|
d0bf4adb1f | ||
|
|
e08dbff9a8 | ||
|
|
e1e91a3731 | ||
|
|
817f4f7908 | ||
|
|
f36b7e7579 | ||
|
|
719cebcac2 | ||
|
|
443673620d | ||
|
|
7ec5a65925 | ||
|
|
c9b2d80bcd | ||
|
|
3cfe65e516 | ||
|
|
e51a276907 | ||
|
|
408913bbaf | ||
|
|
4e17be7981 | ||
|
|
f859a8d3c3 | ||
|
|
c45de33c67 | ||
|
|
d0c32dc135 | ||
|
|
f56a039775 | ||
|
|
bc1e478db1 | ||
|
|
4d8a85b8f5 | ||
|
|
3ce5f07793 | ||
|
|
e839802720 | ||
|
|
4bbbe25802 | ||
|
|
2723d9b56e | ||
|
|
07ffdc2862 | ||
|
|
ad529fb89f | ||
|
|
fdcaf37361 | ||
|
|
50e34891f0 | ||
|
|
5388944e8d | ||
|
|
f0c0277970 | ||
|
|
ef92a14bfe | ||
|
|
945d8218fb | ||
|
|
d8a5dc02fc | ||
|
|
3a78af1e24 | ||
|
|
7ca9972636 | ||
|
|
c4c84d1edb | ||
|
|
bcd6cdf0d8 | ||
|
|
13618b191e | ||
|
|
aa5a768720 | ||
|
|
9b8fda796b | ||
|
|
8022015552 | ||
|
|
f9e766db98 | ||
|
|
ca50c83fbb | ||
|
|
3d55f1eb57 | ||
|
|
ae6bf87273 | ||
|
|
c364d5d1e3 | ||
|
|
4f7e5f5810 | ||
|
|
8824d60fe5 | ||
|
|
997db91e07 | ||
|
|
3392d32e8b | ||
|
|
327a232c85 | ||
|
|
c60d796f04 | ||
|
|
e8bad77c7c | ||
|
|
7680904839 | ||
|
|
58f3338bfa | ||
|
|
268d90a03e | ||
|
|
1ff8758f76 | ||
|
|
8520542071 | ||
|
|
6b9f6b0222 | ||
|
|
deb342fb08 | ||
|
|
437077c39d | ||
|
|
6f7d7bc1de | ||
|
|
53a4981fa2 | ||
|
|
18f0ff003d | ||
|
|
96f1517831 | ||
|
|
1a8f8fd86f | ||
|
|
0f79ad47c5 | ||
|
|
c7057fc1f2 | ||
|
|
193f59e077 | ||
|
|
0ae2be5692 | ||
|
|
1332dd1ed3 | ||
|
|
63a17d4bd5 | ||
|
|
676e07902e | ||
|
|
64e89980e8 | ||
|
|
48b58617e9 | ||
|
|
51ad64cc07 | ||
|
|
df05a759e4 | ||
|
|
d3de71efc9 | ||
|
|
e1df4ef73c | ||
|
|
86408b3f47 | ||
|
|
57e0d73c77 | ||
|
|
0dbd4638e0 | ||
|
|
3bca8931e8 | ||
|
|
5e9a4e5101 | ||
|
|
9b46d1ae6f | ||
|
|
20b6f94b65 | ||
|
|
d413612029 | ||
|
|
1f169f43b3 | ||
|
|
5e2e2f10ef | ||
|
|
c6890d6b5c | ||
|
|
749dd97a54 | ||
|
|
f2c3fc5191 | ||
|
|
a2fc3a53ba | ||
|
|
d53603c928 | ||
|
|
14bc3ce3d6 | ||
|
|
f24cf5d303 | ||
|
|
c6cb792d48 | ||
|
|
a64bbe049e | ||
|
|
d407d572fd | ||
|
|
e42975490f | ||
|
|
50d11b90ca | ||
|
|
c34e6d71bc | ||
|
|
9536ba19d4 | ||
|
|
83bd320b39 | ||
|
|
f751c2966b | ||
|
|
404996ca1f | ||
|
|
7660e2a068 | ||
|
|
52a9ba96f5 | ||
|
|
6a5599fd01 | ||
|
|
098cb9d233 | ||
|
|
c710fe540e | ||
|
|
d5eab2fc82 | ||
|
|
8f1bf28505 | ||
|
|
4e646b0ddb | ||
|
|
0460ea4c39 | ||
|
|
dfb863f333 | ||
|
|
435a93b5d8 | ||
|
|
8b2a01a8c2 | ||
|
|
50cb6f9782 | ||
|
|
351bf658f9 | ||
|
|
cff2b2a13a | ||
|
|
160a60d663 | ||
|
|
805144b705 | ||
|
|
096affb55b | ||
|
|
cbc1f57b48 | ||
|
|
7377195297 | ||
|
|
737423a89c | ||
|
|
47706e3924 | ||
|
|
6e946c8e72 | ||
|
|
c7152c8f97 | ||
|
|
3a2bbe7f8a | ||
|
|
7fb548aa26 | ||
|
|
f54e800366 | ||
|
|
38f98b3282 | ||
|
|
e23a7a8b7b | ||
|
|
cb85bc396e | ||
|
|
61686926d1 | ||
|
|
9856efc7b0 | ||
|
|
5dd8fb2069 | ||
|
|
92f525ecf4 | ||
|
|
7024a1ef07 | ||
|
|
8146a0c731 | ||
|
|
a046858a22 | ||
|
|
12c8c64efa | ||
|
|
7aedcf9460 | ||
|
|
3a87163b24 | ||
|
|
cacff1be88 | ||
|
|
58969fa2bf | ||
|
|
d7b2d11255 | ||
|
|
7a57b2920b | ||
|
|
77d272623f | ||
|
|
7e05b8b75e | ||
|
|
b88460bcbc | ||
|
|
2897286487 | ||
|
|
e530e0a350 | ||
|
|
9485ec31ea | ||
|
|
13df3915ef | ||
|
|
c183ee5c79 | ||
|
|
1dcf208f56 | ||
|
|
93b6926054 | ||
|
|
51e7e32c3b | ||
|
|
2d5114452d | ||
|
|
33ecb42991 | ||
|
|
be88248add | ||
|
|
a96cac0d18 | ||
|
|
d4753c944f | ||
|
|
0e6f604178 | ||
|
|
7f254706b0 | ||
|
|
1273d355ac | ||
|
|
d4ca5c3952 | ||
|
|
fd2e14b3c8 | ||
|
|
60cb7de336 | ||
|
|
8ab6bc5a49 | ||
|
|
f052c10eed | ||
|
|
a7cee528c5 | ||
|
|
041717eda3 | ||
|
|
621093cb1c | ||
|
|
824b154ce8 | ||
|
|
2ef579d1aa | ||
|
|
1407a1ec99 | ||
|
|
1bb0f1e84b | ||
|
|
b682fae9d9 | ||
|
|
6f8c96d123 | ||
|
|
110606d470 | ||
|
|
061141e632 | ||
|
|
019176137f | ||
|
|
4ce692df88 | ||
|
|
c504d90c11 | ||
|
|
8e39d9bdb3 | ||
|
|
28355dafcf | ||
|
|
8fce03e0ad | ||
|
|
60ec75048a | ||
|
|
6f6bb1fdea | ||
|
|
92cb7c4dfe | ||
|
|
0008b0006d | ||
|
|
ac36c6cd44 | ||
|
|
2b70a8e7c9 | ||
|
|
9122dcecbb | ||
|
|
6776a52bb3 | ||
|
|
d86bd22d24 | ||
|
|
ac5478eb52 | ||
|
|
652817046b | ||
|
|
5a569509b4 | ||
|
|
7c54f1603f | ||
|
|
07cdfb09fb | ||
|
|
a0766eca27 | ||
|
|
cd16d5dc3d | ||
|
|
5b949241a5 | ||
|
|
67958f21df | ||
|
|
d91dc086bb | ||
|
|
9ac3f5df9c | ||
|
|
bcfe98acff | ||
|
|
9850262a72 | ||
|
|
909fbb9de1 | ||
|
|
5dfba0b4db | ||
|
|
9ccdb80de3 | ||
|
|
9fd9c4c635 | ||
|
|
63b8f09d8d | ||
|
|
f8713e1287 | ||
|
|
82ae85de27 | ||
|
|
5eaf644c99 | ||
|
|
cdee317419 | ||
|
|
d95b68fde3 | ||
|
|
ff762fb499 | ||
|
|
1307b22223 | ||
|
|
64519cfd65 | ||
|
|
09bc0c502c | ||
|
|
5d4eb9dd07 | ||
|
|
0c478d2f4d | ||
|
|
5024bde8f4 | ||
|
|
2ab09a55cf | ||
|
|
6bbff48079 | ||
|
|
b8eb32f4d2 | ||
|
|
bba87589cc | ||
|
|
f3e2e6076a | ||
|
|
53cca4a445 | ||
|
|
4213b8d8ec | ||
|
|
eb86b6f5a5 | ||
|
|
3d2035ea86 | ||
|
|
285d26374a | ||
|
|
a55113411f | ||
|
|
c202523e53 | ||
|
|
aab530e971 | ||
|
|
60564410ef | ||
|
|
2668a43388 | ||
|
|
63ccd72496 | ||
|
|
e13da525a7 | ||
|
|
5d250ad1ea | ||
|
|
7509d70f9d | ||
|
|
2369b122d1 | ||
|
|
6c2c771af7 | ||
|
|
07d3a38726 | ||
|
|
f5325d292d | ||
|
|
fa791116a3 | ||
|
|
3f4ed681c2 | ||
|
|
6fedb7aa0f | ||
|
|
a519bb0635 | ||
|
|
96598e7b06 | ||
|
|
4166b11a53 | ||
|
|
2b7c839b4e | ||
|
|
709b55ee02 | ||
|
|
c98ea254dc | ||
|
|
9d6d50269b | ||
|
|
fa61ee70ee | ||
|
|
f32fef1b07 | ||
|
|
4af2fdba6d | ||
|
|
c987061aa4 | ||
|
|
77afd97a99 | ||
|
|
7ab68961e4 | ||
|
|
2b2aa8a820 | ||
|
|
7933cdc6dc | ||
|
|
0f0dbe8c0c | ||
|
|
311c222f47 | ||
|
|
2a01d06da6 | ||
|
|
7718b19389 | ||
|
|
9cc0da8453 | ||
|
|
99dc3e613a | ||
|
|
d06be428f6 | ||
|
|
abec1c0004 | ||
|
|
c4f1c2114b | ||
|
|
49829da8b4 | ||
|
|
b428adc267 | ||
|
|
cc826dc03e | ||
|
|
51e2dda58c | ||
|
|
b7ff182b6e | ||
|
|
ca07f3e370 | ||
|
|
9279174dde | ||
|
|
db4f4a8425 | ||
|
|
1b962fc720 | ||
|
|
e818838412 | ||
|
|
efe4b690ae | ||
|
|
c03b729319 | ||
|
|
5664b6d7ba | ||
|
|
b455c4c45c | ||
|
|
72f3ea7358 | ||
|
|
98b07466fb | ||
|
|
339c142009 | ||
|
|
a87b5256e2 | ||
|
|
001b3f06ec | ||
|
|
7592f48c83 | ||
|
|
a1d310b6b5 | ||
|
|
42043953c3 | ||
|
|
5adbb0aabe | ||
|
|
5b1052663a | ||
|
|
c4f98941ed | ||
|
|
03427e76f1 | ||
|
|
b3c26180e3 | ||
|
|
3c92ea399d | ||
|
|
fc691e1cbd | ||
|
|
096198d11f | ||
|
|
660244f65f | ||
|
|
e139d7fc68 | ||
|
|
989176c56e | ||
|
|
8bc591a6f0 | ||
|
|
15f39aba8c | ||
|
|
7cd88b1dec | ||
|
|
d567baabbd | ||
|
|
b3b0b2a29e | ||
|
|
6c8641a542 | ||
|
|
5af84139a8 | ||
|
|
d3aa183beb | ||
|
|
a8f3b02092 | ||
|
|
a72709afd8 | ||
|
|
934c58aa38 | ||
|
|
ee8f15930d | ||
|
|
f351834f77 | ||
|
|
94fd46fa1c | ||
|
|
955b8841cd | ||
|
|
18e27629d3 | ||
|
|
826b271d9a | ||
|
|
30c9f909b2 | ||
|
|
194c66eeeb | ||
|
|
f903d86740 | ||
|
|
ce85b55cf0 | ||
|
|
4532e4b90d | ||
|
|
2818b7cee6 | ||
|
|
997b95a4af | ||
|
|
b0e92f6d47 | ||
|
|
4ed01ed791 | ||
|
|
c41a3ec3a9 | ||
|
|
bd955e15e1 | ||
|
|
9819bb20da | ||
|
|
b4bc8b7616 | ||
|
|
b1e321d6ce | ||
|
|
658816ddc9 | ||
|
|
72034ab35d | ||
|
|
f8985d195e | ||
|
|
329025253d | ||
|
|
5bb08db55b | ||
|
|
98968fbb63 | ||
|
|
2f1e2cf632 | ||
|
|
f106868110 | ||
|
|
f789ea1d09 | ||
|
|
d7caac3e81 | ||
|
|
11d512e7a8 | ||
|
|
0a84137c45 | ||
|
|
0ed89c569f | ||
|
|
4066f450c2 | ||
|
|
e7bdde981f | ||
|
|
66c51dc215 | ||
|
|
5b5a3af983 | ||
|
|
5eb5c23447 | ||
|
|
7d0f6aed59 | ||
|
|
895c953817 |
262
.gitignore
vendored
Normal file
262
.gitignore
vendored
Normal file
@@ -0,0 +1,262 @@
|
||||
# START "git svn show-ignore"
|
||||
|
||||
# /
|
||||
/Makefile
|
||||
/Makefile.in
|
||||
/aclocal.m4
|
||||
/autom4te.cache
|
||||
/config.*
|
||||
/configure
|
||||
/nix.spec
|
||||
/stamp-h1
|
||||
/svn-revision
|
||||
/NEWS
|
||||
/libtool
|
||||
|
||||
# /config/
|
||||
/config/config.guess
|
||||
/config/config.sub
|
||||
/config/depcomp
|
||||
/config/install-sh
|
||||
/config/missing
|
||||
/config/mkinstalldirs
|
||||
/config/ltmain.sh
|
||||
|
||||
# /corepkgs/
|
||||
/corepkgs/Makefile
|
||||
/corepkgs/Makefile.in
|
||||
|
||||
# /corepkgs/buildenv/
|
||||
/corepkgs/buildenv/Makefile.in
|
||||
/corepkgs/buildenv/Makefile
|
||||
/corepkgs/buildenv/builder.pl
|
||||
|
||||
# /corepkgs/channels/
|
||||
/corepkgs/channels/Makefile.in
|
||||
/corepkgs/channels/Makefile
|
||||
/corepkgs/channels/unpack.sh
|
||||
|
||||
# /corepkgs/nar/
|
||||
/corepkgs/nar/Makefile
|
||||
/corepkgs/nar/Makefile.in
|
||||
/corepkgs/nar/nar.sh
|
||||
/corepkgs/nar/unnar.sh
|
||||
|
||||
# /doc/
|
||||
/doc/Makefile
|
||||
/doc/Makefile.in
|
||||
|
||||
# /doc/manual/
|
||||
/doc/manual/Makefile
|
||||
/doc/manual/Makefile.in
|
||||
/doc/manual/manual.html
|
||||
/doc/manual/manual.is-valid
|
||||
/doc/manual/*.1
|
||||
/doc/manual/*.8
|
||||
/doc/manual/images
|
||||
/doc/manual/version.txt
|
||||
/doc/manual/NEWS.html
|
||||
/doc/manual/NEWS.txt
|
||||
|
||||
# /externals/
|
||||
/externals/Makefile
|
||||
/externals/Makefile.in
|
||||
/externals/aterm-*
|
||||
/externals/have-aterm
|
||||
/externals/build-aterm
|
||||
/externals/inst-aterm
|
||||
/externals/bzip2-*
|
||||
/externals/have-bzip2
|
||||
/externals/build-bzip2
|
||||
/externals/inst-bzip2
|
||||
|
||||
# /make/examples/aterm/
|
||||
/make/examples/aterm/result*
|
||||
|
||||
# /make/examples/aterm/aterm/
|
||||
/make/examples/aterm/aterm/*
|
||||
|
||||
# /make/examples/aterm/test/
|
||||
/make/examples/aterm/test/*
|
||||
|
||||
# /misc/
|
||||
/misc/Makefile.in
|
||||
/misc/Makefile
|
||||
|
||||
# /misc/emacs/
|
||||
/misc/emacs/Makefile.in
|
||||
/misc/emacs/Makefile
|
||||
|
||||
# /scripts/
|
||||
/scripts/Makefile
|
||||
/scripts/Makefile.in
|
||||
/scripts/nix-profile.sh
|
||||
/scripts/nix-pull
|
||||
/scripts/nix-push
|
||||
/scripts/nix-switch
|
||||
/scripts/nix-collect-garbage
|
||||
/scripts/nix-prefetch-url
|
||||
/scripts/nix-install-package
|
||||
/scripts/nix-channel
|
||||
/scripts/nix-build
|
||||
/scripts/nix-copy-closure
|
||||
/scripts/readmanifest.pm
|
||||
/scripts/readconfig.pm
|
||||
/scripts/download-using-manifests.pl
|
||||
/scripts/copy-from-other-stores.pl
|
||||
/scripts/generate-patches.pl
|
||||
/scripts/find-runtime-roots.pl
|
||||
/scripts/build-remote.pl
|
||||
|
||||
# /src/
|
||||
/src/Makefile
|
||||
/src/Makefile.in
|
||||
|
||||
# /src/bin2c/
|
||||
/src/bin2c/Makefile.in
|
||||
/src/bin2c/Makefile
|
||||
/src/bin2c/bin2c
|
||||
/src/bin2c/.deps
|
||||
/src/bin2c/.libs
|
||||
|
||||
# /src/boost/
|
||||
/src/boost/Makefile
|
||||
/src/boost/Makefile.in
|
||||
|
||||
# /src/boost/format/
|
||||
/src/boost/format/Makefile
|
||||
/src/boost/format/Makefile.in
|
||||
/src/boost/format/.deps
|
||||
/src/boost/format/libformat.a
|
||||
/src/boost/format/.libs
|
||||
|
||||
# /src/bsdiff-4.3/
|
||||
/src/bsdiff-4.3/Makefile
|
||||
/src/bsdiff-4.3/Makefile.in
|
||||
/src/bsdiff-4.3/bsdiff
|
||||
/src/bsdiff-4.3/bspatch
|
||||
/src/bsdiff-4.3/.deps
|
||||
/src/bsdiff-4.3/.libs
|
||||
|
||||
# /src/libexpr/
|
||||
/src/libexpr/Makefile
|
||||
/src/libexpr/Makefile.in
|
||||
/src/libexpr/.deps
|
||||
/src/libexpr/libexpr.a
|
||||
/src/libexpr/lexer-tab.cc
|
||||
/src/libexpr/lexer-tab.hh
|
||||
/src/libexpr/parser-tab.cc
|
||||
/src/libexpr/parser-tab.hh
|
||||
/src/libexpr/parser-tab.output
|
||||
/src/libexpr/nixexpr-ast.hh
|
||||
/src/libexpr/nixexpr-ast.cc
|
||||
/src/libexpr/.libs
|
||||
/src/libexpr/nix.tbl
|
||||
|
||||
# /src/libmain/
|
||||
/src/libmain/Makefile
|
||||
/src/libmain/Makefile.in
|
||||
/src/libmain/.deps
|
||||
/src/libmain/libmain.a
|
||||
/src/libmain/.libs
|
||||
|
||||
# /src/libstore/
|
||||
/src/libstore/Makefile
|
||||
/src/libstore/Makefile.in
|
||||
/src/libstore/.deps
|
||||
/src/libstore/libstore.a
|
||||
/src/libstore/derivations-ast.cc
|
||||
/src/libstore/derivations-ast.hh
|
||||
/src/libstore/.libs
|
||||
|
||||
# /src/libutil/
|
||||
/src/libutil/Makefile
|
||||
/src/libutil/Makefile.in
|
||||
/src/libutil/.deps
|
||||
/src/libutil/libutil.a
|
||||
/src/libutil/.libs
|
||||
|
||||
# /src/nix-env/
|
||||
/src/nix-env/Makefile.in
|
||||
/src/nix-env/Makefile
|
||||
/src/nix-env/.deps
|
||||
/src/nix-env/nix-env
|
||||
/src/nix-env/help.txt.hh
|
||||
/src/nix-env/.libs
|
||||
|
||||
# /src/nix-hash/
|
||||
/src/nix-hash/Makefile
|
||||
/src/nix-hash/Makefile.in
|
||||
/src/nix-hash/.deps
|
||||
/src/nix-hash/.libs
|
||||
/src/nix-hash/nix-hash
|
||||
/src/nix-hash/help.txt.hh
|
||||
|
||||
# /src/nix-instantiate/
|
||||
/src/nix-instantiate/Makefile.in
|
||||
/src/nix-instantiate/Makefile
|
||||
/src/nix-instantiate/.deps
|
||||
/src/nix-instantiate/nix-instantiate
|
||||
/src/nix-instantiate/help.txt.hh
|
||||
/src/nix-instantiate/.libs
|
||||
|
||||
# /src/nix-log2xml/
|
||||
/src/nix-log2xml/Makefile.in
|
||||
/src/nix-log2xml/Makefile
|
||||
/src/nix-log2xml/.deps
|
||||
/src/nix-log2xml/nix-log2xml
|
||||
/src/nix-log2xml/test*.*
|
||||
/src/nix-log2xml/.libs
|
||||
/src/nix-log2xml/*.log
|
||||
/src/nix-log2xml/*.xml
|
||||
/src/nix-log2xml/*.html
|
||||
|
||||
# /src/nix-setuid-helper/
|
||||
/src/nix-setuid-helper/Makefile.in
|
||||
/src/nix-setuid-helper/Makefile
|
||||
/src/nix-setuid-helper/.deps
|
||||
/src/nix-setuid-helper/nix-setuid-helper
|
||||
/src/nix-setuid-helper/help.txt.hh
|
||||
/src/nix-setuid-helper/.libs
|
||||
|
||||
# /src/nix-store/
|
||||
/src/nix-store/Makefile
|
||||
/src/nix-store/Makefile.in
|
||||
/src/nix-store/.deps
|
||||
/src/nix-store/help.txt.hh
|
||||
/src/nix-store/nix-store
|
||||
/src/nix-store/.libs
|
||||
|
||||
# /src/nix-worker/
|
||||
/src/nix-worker/Makefile.in
|
||||
/src/nix-worker/Makefile
|
||||
/src/nix-worker/.deps
|
||||
/src/nix-worker/nix-worker
|
||||
/src/nix-worker/help.txt.hh
|
||||
/src/nix-worker/.libs
|
||||
|
||||
# /tests/
|
||||
/tests/Makefile
|
||||
/tests/Makefile.in
|
||||
/tests/test-tmp
|
||||
/tests/config.nix
|
||||
/tests/common.sh
|
||||
/tests/dummy
|
||||
|
||||
# /tests/lang/
|
||||
/tests/lang/*.out
|
||||
/tests/lang/*.out.xml
|
||||
/tests/lang/*.ast
|
||||
|
||||
# END "git svn show-ignore"
|
||||
|
||||
*.lo
|
||||
*.la
|
||||
*.o
|
||||
*~
|
||||
|
||||
# GNU Global
|
||||
GPATH
|
||||
GRTAGS
|
||||
GSYMS
|
||||
GTAGS
|
||||
27
Makefile.am
27
Makefile.am
@@ -1,23 +1,21 @@
|
||||
SUBDIRS = externals src scripts corepkgs doc misc tests
|
||||
EXTRA_DIST = substitute.mk nix.spec nix.spec.in bootstrap.sh \
|
||||
svn-revision nix.conf.example NEWS
|
||||
nix.conf.example NEWS version
|
||||
|
||||
pkginclude_HEADERS = config.h
|
||||
|
||||
include ./substitute.mk
|
||||
|
||||
nix.spec: nix.spec.in
|
||||
|
||||
rpm: nix.spec dist
|
||||
rpm $(EXTRA_RPM_FLAGS) -ta $(distdir).tar.gz
|
||||
|
||||
relname:
|
||||
echo -n $(distdir) > relname
|
||||
|
||||
install-data-local: init-state
|
||||
$(INSTALL) -d $(DESTDIR)$(sysconfdir)/nix
|
||||
$(INSTALL_DATA) $(srcdir)/nix.conf.example $(DESTDIR)$(sysconfdir)/nix
|
||||
if ! test -e $(DESTDIR)$(sysconfdir)/nix/nix.conf; then \
|
||||
$(INSTALL_DATA) $(srcdir)/nix.conf.example $(DESTDIR)$(sysconfdir)/nix/nix.conf; \
|
||||
fi
|
||||
$(INSTALL) -d $(DESTDIR)$(docdir)
|
||||
$(INSTALL_DATA) README $(DESTDIR)$(docdir)/
|
||||
|
||||
if INIT_STATE
|
||||
|
||||
@@ -33,23 +31,18 @@ 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
|
||||
$(INSTALL) $(INIT_FLAGS) $(GROUP_WRITABLE) -d $(DESTDIR)$(localstatedir)/nix/gcroots/tmp
|
||||
$(INSTALL) $(INIT_FLAGS) $(GROUP_WRITABLE) -d $(DESTDIR)$(localstatedir)/nix/gcroots/channels
|
||||
ln -sfn $(localstatedir)/nix/profiles $(DESTDIR)$(localstatedir)/nix/gcroots/profiles
|
||||
$(INSTALL) $(INIT_FLAGS) -d $(DESTDIR)$(localstatedir)/nix/userpool
|
||||
$(INSTALL) $(INIT_FLAGS) -m 1777 -d $(DESTDIR)$(prefix)/store
|
||||
-$(INSTALL) $(INIT_FLAGS) -m 1777 -d $(DESTDIR)$(storedir)
|
||||
$(INSTALL) $(INIT_FLAGS) $(GROUP_WRITABLE) -d $(DESTDIR)$(localstatedir)/nix/manifests
|
||||
ln -sfn $(localstatedir)/nix/manifests $(DESTDIR)$(localstatedir)/nix/gcroots/manifests
|
||||
# $(bindir)/nix-store --init
|
||||
|
||||
else
|
||||
|
||||
init-state:
|
||||
|
||||
endif
|
||||
|
||||
svn-revision:
|
||||
svnversion . > svn-revision
|
||||
|
||||
all-local: NEWS
|
||||
|
||||
NEWS: doc/manual/NEWS.txt
|
||||
NEWS:
|
||||
$(MAKE) -C doc/manual NEWS.txt
|
||||
cp $(srcdir)/doc/manual/NEWS.txt NEWS
|
||||
|
||||
9
README
9
README
@@ -1,9 +1,10 @@
|
||||
For installation and usage instructions, please read the manual, which
|
||||
can be found in `docs/manual/manual.html', and additionally at the Nix
|
||||
website at <http://www.cs.uu.nl/groups/ST/Trace/Nix>.
|
||||
Nix is a purely functional package manager. For installation and
|
||||
usage instructions, please read the manual, which can be found in
|
||||
`docs/manual/manual.html', and additionally at the Nix website at
|
||||
<http://nixos.org/>.
|
||||
|
||||
|
||||
Acknowledgments
|
||||
|
||||
This product includes software developed by the OpenSSL Project for
|
||||
use in the OpenSSL Toolkit (http://www.OpenSSL.org/)
|
||||
use in the OpenSSL Toolkit (http://www.OpenSSL.org/).
|
||||
|
||||
267
aterm-gc.supp
267
aterm-gc.supp
@@ -1,184 +1,149 @@
|
||||
{
|
||||
ATerm library conservatively scans for GC roots
|
||||
Memcheck:Cond
|
||||
fun:mark_memory_young
|
||||
fun:mark_phase_young
|
||||
fun:*
|
||||
fun:AT_collect_minor
|
||||
}
|
||||
|
||||
{
|
||||
ATerm library conservatively scans for GC roots
|
||||
Memcheck:Cond
|
||||
fun:AT_isValidSymbol
|
||||
fun:mark_memory_young
|
||||
fun:mark_phase_young
|
||||
}
|
||||
|
||||
{
|
||||
ATerm library conservatively scans for GC roots
|
||||
Memcheck:Value4
|
||||
fun:AT_isValidSymbol
|
||||
fun:mark_memory_young
|
||||
fun:mark_phase_young
|
||||
}
|
||||
|
||||
{
|
||||
ATerm library conservatively scans for GC roots
|
||||
Memcheck:Value4
|
||||
fun:mark_memory_young
|
||||
fun:mark_phase_young
|
||||
}
|
||||
|
||||
{
|
||||
ATerm library conservatively scans for GC roots
|
||||
Memcheck:Value4
|
||||
fun:AT_isInsideValidTerm
|
||||
fun:mark_memory_young
|
||||
fun:mark_phase_young
|
||||
}
|
||||
|
||||
{
|
||||
ATerm library conservatively scans for GC roots
|
||||
Memcheck:Cond
|
||||
fun:AT_isInsideValidTerm
|
||||
fun:mark_memory_young
|
||||
fun:mark_phase_young
|
||||
}
|
||||
|
||||
{
|
||||
ATerm library conservatively scans for GC roots
|
||||
Memcheck:Value4
|
||||
fun:mark_memory_young
|
||||
fun:mark_phase_young
|
||||
}
|
||||
|
||||
{
|
||||
ATerm library conservatively scans for GC roots
|
||||
Memcheck:Value4
|
||||
fun:AT_markTerm_young
|
||||
fun:mark_memory_young
|
||||
fun:mark_phase_young
|
||||
}
|
||||
|
||||
{
|
||||
ATerm library conservatively scans for GC roots
|
||||
Memcheck:Cond
|
||||
fun:AT_markTerm_young
|
||||
fun:mark_memory_young
|
||||
fun:mark_phase_young
|
||||
}
|
||||
|
||||
{
|
||||
ATerm library conservatively scans for GC roots
|
||||
Memcheck:Cond
|
||||
fun:mark_memory
|
||||
fun:mark_phase
|
||||
}
|
||||
|
||||
{
|
||||
ATerm library conservatively scans for GC roots
|
||||
Memcheck:Value4
|
||||
fun:mark_memory
|
||||
fun:mark_phase
|
||||
}
|
||||
|
||||
{
|
||||
ATerm library conservatively scans for GC roots
|
||||
Memcheck:Cond
|
||||
fun:AT_isValidSymbol
|
||||
fun:mark_memory
|
||||
fun:mark_phase
|
||||
}
|
||||
|
||||
{
|
||||
ATerm library conservatively scans for GC roots
|
||||
Memcheck:Value4
|
||||
fun:AT_isValidSymbol
|
||||
fun:mark_memory
|
||||
fun:mark_phase
|
||||
}
|
||||
|
||||
{
|
||||
ATerm library conservatively scans for GC roots
|
||||
Memcheck:Value4
|
||||
fun:AT_isInsideValidTerm
|
||||
fun:mark_memory
|
||||
fun:mark_phase
|
||||
}
|
||||
|
||||
{
|
||||
ATerm library conservatively scans for GC roots
|
||||
Memcheck:Cond
|
||||
fun:AT_isInsideValidTerm
|
||||
fun:mark_memory
|
||||
fun:mark_phase
|
||||
}
|
||||
|
||||
{
|
||||
ATerm library conservatively scans for GC roots
|
||||
Memcheck:Cond
|
||||
fun:AT_markTerm
|
||||
fun:mark_memory
|
||||
fun:mark_phase
|
||||
}
|
||||
|
||||
{
|
||||
ATerm library conservatively scans for GC roots
|
||||
Memcheck:Value4
|
||||
fun:AT_markTerm
|
||||
fun:mark_memory
|
||||
fun:mark_phase
|
||||
}
|
||||
|
||||
{
|
||||
ATerm library conservatively scans for GC roots
|
||||
Memcheck:Value4
|
||||
fun:AT_markTerm
|
||||
fun:mark_memory
|
||||
fun:mark_phase
|
||||
}
|
||||
|
||||
{
|
||||
ATerm library conservatively scans for GC roots
|
||||
Memcheck:Cond
|
||||
fun:mark_phase_young
|
||||
fun:*
|
||||
fun:*
|
||||
fun:AT_collect_minor
|
||||
}
|
||||
|
||||
{
|
||||
ATerm library conservatively scans for GC roots
|
||||
Memcheck:Value4
|
||||
fun:mark_phase_young
|
||||
fun:*
|
||||
fun:AT_collect_minor
|
||||
}
|
||||
|
||||
{
|
||||
<insert a suppression name here>
|
||||
Memcheck:Cond
|
||||
fun:AT_isValidSymbol
|
||||
fun:mark_phase_young
|
||||
ATerm library conservatively scans for GC roots
|
||||
Memcheck:Value8
|
||||
fun:*
|
||||
fun:AT_collect_minor
|
||||
}
|
||||
|
||||
{
|
||||
<insert a suppression name here>
|
||||
ATerm library conservatively scans for GC roots
|
||||
Memcheck:Value4
|
||||
fun:AT_isValidSymbol
|
||||
fun:mark_phase_young
|
||||
fun:*
|
||||
fun:*
|
||||
fun:AT_collect_minor
|
||||
}
|
||||
|
||||
{
|
||||
<insert a suppression name here>
|
||||
Memcheck:Value4
|
||||
fun:AT_isInsideValidTerm
|
||||
fun:mark_phase_young
|
||||
ATerm library conservatively scans for GC roots
|
||||
Memcheck:Value8
|
||||
fun:*
|
||||
fun:*
|
||||
fun:AT_collect_minor
|
||||
}
|
||||
|
||||
{
|
||||
<insert a suppression name here>
|
||||
ATerm library conservatively scans for GC roots
|
||||
Memcheck:Addr4
|
||||
fun:*
|
||||
fun:AT_collect_minor
|
||||
}
|
||||
|
||||
{
|
||||
ATerm library conservatively scans for GC roots
|
||||
Memcheck:Addr8
|
||||
fun:*
|
||||
fun:AT_collect_minor
|
||||
}
|
||||
|
||||
{
|
||||
ATerm library conservatively scans for GC roots
|
||||
Memcheck:Cond
|
||||
fun:AT_isInsideValidTerm
|
||||
fun:mark_phase_young
|
||||
fun:AT_collect_minor
|
||||
fun:*
|
||||
fun:AT_collect
|
||||
}
|
||||
|
||||
{
|
||||
ATerm library conservatively scans for GC roots
|
||||
Memcheck:Value4
|
||||
fun:*
|
||||
fun:AT_collect
|
||||
}
|
||||
|
||||
{
|
||||
ATerm library conservatively scans for GC roots
|
||||
Memcheck:Value8
|
||||
fun:*
|
||||
fun:AT_collect
|
||||
}
|
||||
|
||||
{
|
||||
ATerm library conservatively scans for GC roots
|
||||
Memcheck:Addr4
|
||||
fun:*
|
||||
fun:AT_collect
|
||||
}
|
||||
|
||||
{
|
||||
ATerm library conservatively scans for GC roots
|
||||
Memcheck:Addr8
|
||||
fun:*
|
||||
fun:AT_collect
|
||||
}
|
||||
|
||||
{
|
||||
ATerm library conservatively scans for GC roots
|
||||
Memcheck:Value4
|
||||
fun:*
|
||||
fun:*
|
||||
fun:AT_collect
|
||||
}
|
||||
|
||||
{
|
||||
ATerm library conservatively scans for GC roots
|
||||
Memcheck:Value8
|
||||
fun:*
|
||||
fun:*
|
||||
fun:AT_collect
|
||||
}
|
||||
|
||||
{
|
||||
ATerm library conservatively scans for GC roots
|
||||
Memcheck:Cond
|
||||
fun:*
|
||||
fun:*
|
||||
fun:AT_collect
|
||||
}
|
||||
|
||||
{
|
||||
ATerm library conservatively scans for GC roots
|
||||
Memcheck:Value4
|
||||
fun:*
|
||||
fun:*
|
||||
fun:mark_phase
|
||||
}
|
||||
|
||||
{
|
||||
ATerm library conservatively scans for GC roots
|
||||
Memcheck:Cond
|
||||
fun:*
|
||||
fun:*
|
||||
fun:mark_phase
|
||||
}
|
||||
|
||||
{
|
||||
ATerm library conservatively scans for GC roots
|
||||
Memcheck:Value4
|
||||
fun:*
|
||||
fun:*
|
||||
fun:mark_phase_young
|
||||
}
|
||||
|
||||
{
|
||||
ATerm library conservatively scans for GC roots
|
||||
Memcheck:Cond
|
||||
fun:*
|
||||
fun:*
|
||||
fun:mark_phase_young
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
#! /bin/sh -e
|
||||
rm -f aclocal.m4
|
||||
mkdir -p config
|
||||
libtoolize --copy
|
||||
aclocal
|
||||
|
||||
169
configure.ac
169
configure.ac
@@ -1,32 +1,17 @@
|
||||
AC_INIT(nix, 0.11)
|
||||
AC_INIT(nix, m4_esyscmd([echo -n $(cat ./version)$VERSION_SUFFIX]))
|
||||
AC_CONFIG_SRCDIR(README)
|
||||
AC_CONFIG_AUX_DIR(config)
|
||||
AM_INIT_AUTOMAKE([dist-bzip2 foreign])
|
||||
|
||||
# Change to `1' to produce a `stable' release (i.e., the `preREVISION'
|
||||
# suffix is not added).
|
||||
STABLE=0
|
||||
|
||||
# Put the revision number in the version.
|
||||
if test "$STABLE" != "1"; then
|
||||
if REVISION=`test -d $srcdir/.svn && svnversion -n $srcdir 2> /dev/null`; then
|
||||
VERSION=${VERSION}pre${REVISION}
|
||||
elif REVISION=`cat $srcdir/svn-revision 2> /dev/null`; then
|
||||
VERSION=${VERSION}pre${REVISION}
|
||||
fi
|
||||
fi
|
||||
|
||||
AC_DEFINE_UNQUOTED(NIX_VERSION, ["$VERSION"], [version])
|
||||
|
||||
AC_PREFIX_DEFAULT(/nix)
|
||||
AC_DEFINE_UNQUOTED(NIX_VERSION, ["$VERSION"], [Nix version.])
|
||||
|
||||
AC_CANONICAL_HOST
|
||||
|
||||
|
||||
# Construct a Nix system name (like "i686-linux").
|
||||
AC_MSG_CHECKING([for the canonical Nix system name])
|
||||
cpu_name=$(uname -p | tr 'A-Z ' 'a-z_')
|
||||
machine_name=$(uname -m | tr 'A-Z ' 'a-z_')
|
||||
cpu_name=$(uname -p | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ ' 'abcdefghijklmnopqrstuvwxyz_')
|
||||
machine_name=$(uname -m | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ ' 'abcdefghijklmnopqrstuvwxyz_')
|
||||
|
||||
case $machine_name in
|
||||
i*86)
|
||||
@@ -45,7 +30,7 @@ case $machine_name in
|
||||
;;
|
||||
esac
|
||||
|
||||
sys_name=$(uname -s | tr 'A-Z ' 'a-z_')
|
||||
sys_name=$(uname -s | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ ' 'abcdefghijklmnopqrstuvwxyz_')
|
||||
|
||||
case $sys_name in
|
||||
cygwin*)
|
||||
@@ -54,37 +39,63 @@ case $sys_name in
|
||||
esac
|
||||
|
||||
AC_ARG_WITH(system, AC_HELP_STRING([--with-system=SYSTEM],
|
||||
[platform identifier (e.g., `i686-linux')]),
|
||||
[Platform identifier (e.g., `i686-linux').]),
|
||||
system=$withval, system="${machine_name}-${sys_name}")
|
||||
AC_MSG_RESULT($system)
|
||||
AC_SUBST(system)
|
||||
AC_DEFINE_UNQUOTED(SYSTEM, ["$system"], [platform identifier (`cpu-os')])
|
||||
|
||||
|
||||
# Windows-specific stuff.
|
||||
if test "$sys_name" = "cygwin"; then
|
||||
# We cannot delete open files.
|
||||
AC_DEFINE(CANNOT_DELETE_OPEN_FILES, 1, [Whether it is impossible to delete open files.])
|
||||
# State should be stored in /nix/var, unless the user overrides it explicitly.
|
||||
test "$localstatedir" = '${prefix}/var' && localstatedir=/nix/var
|
||||
|
||||
# Shared libraries don't work, currently.
|
||||
|
||||
# Windows-specific stuff. On Cygwin, dynamically linking against the
|
||||
# ATerm DLL works, except that it requires the ATerm "lib" directory
|
||||
# to be in $PATH, as Windows doesn't have anything like an RPATH
|
||||
# embedded in executable. Since this is kind of annoying, we use
|
||||
# static libraries for now.
|
||||
if test "$sys_name" = "cygwin"; then
|
||||
AC_DISABLE_SHARED
|
||||
AC_ENABLE_STATIC
|
||||
fi
|
||||
|
||||
|
||||
# Solaris-specific stuff.
|
||||
if test "$sys_name" = "sunos"; then
|
||||
# Solaris requires -lsocket -lnsl for network functions
|
||||
LIBS="-lsocket -lnsl $LIBS"
|
||||
fi
|
||||
|
||||
|
||||
AC_PROG_CC
|
||||
AC_PROG_CXX
|
||||
|
||||
# To build programs to be run in the build machine
|
||||
if test "$CC_FOR_BUILD" = ""; then
|
||||
if test "$cross_compiling" = "yes"; then
|
||||
AC_CHECK_PROGS(CC_FOR_BUILD, gcc cc)
|
||||
else
|
||||
CC_FOR_BUILD="$CC"
|
||||
fi
|
||||
fi
|
||||
AC_SUBST([CC_FOR_BUILD])
|
||||
|
||||
# We are going to use libtool.
|
||||
AC_DISABLE_STATIC
|
||||
AC_ENABLE_SHARED
|
||||
AC_PROG_LIBTOOL
|
||||
|
||||
if test "$enable_shared" = yes; then
|
||||
SUB_CONFIGURE_FLAGS="--enable-shared --disable-static"
|
||||
else
|
||||
SUB_CONFIGURE_FLAGS="--enable-static --disable-shared"
|
||||
fi
|
||||
AC_SUBST(SUB_CONFIGURE_FLAGS)
|
||||
|
||||
|
||||
# Use 64-bit file system calls so that we can support files > 2 GiB.
|
||||
CFLAGS="-D_FILE_OFFSET_BITS=64 $CFLAGS"
|
||||
CXXFLAGS="-D_FILE_OFFSET_BITS=64 $CXXFLAGS"
|
||||
AC_SYS_LARGEFILE
|
||||
|
||||
|
||||
# Check for pubsetbuf.
|
||||
@@ -94,13 +105,15 @@ AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[#include <iostream>
|
||||
using namespace std;
|
||||
static char buf[1024];]],
|
||||
[[cerr.rdbuf()->pubsetbuf(buf, sizeof(buf));]])],
|
||||
[AC_MSG_RESULT(yes) AC_DEFINE(HAVE_PUBSETBUF, 1, [whether pubsetbuf is available])],
|
||||
[AC_MSG_RESULT(yes) AC_DEFINE(HAVE_PUBSETBUF, 1, [Whether pubsetbuf is available.])],
|
||||
AC_MSG_RESULT(no))
|
||||
AC_LANG_POP(C++)
|
||||
|
||||
|
||||
# Check for chroot support (requires chroot() and bind mounts).
|
||||
AC_CHECK_FUNCS([chroot])
|
||||
AC_CHECK_FUNCS([unshare])
|
||||
AC_CHECK_HEADERS([sched.h], [], [], [])
|
||||
AC_CHECK_HEADERS([sys/param.h], [], [], [])
|
||||
AC_CHECK_HEADERS([sys/mount.h], [], [],
|
||||
[#ifdef HAVE_SYS_PARAM_H
|
||||
@@ -109,12 +122,28 @@ AC_CHECK_HEADERS([sys/mount.h], [], [],
|
||||
])
|
||||
|
||||
|
||||
# Check for <locale>
|
||||
# Check for <locale>.
|
||||
AC_LANG_PUSH(C++)
|
||||
AC_CHECK_HEADERS([locale], [], [], [])
|
||||
AC_LANG_POP(C++)
|
||||
|
||||
|
||||
# Check for <err.h>.
|
||||
AC_CHECK_HEADER([err.h], [], [bsddiff_compat_include="-Icompat-include"])
|
||||
AC_SUBST([bsddiff_compat_include])
|
||||
|
||||
|
||||
# Check whether we have the personality() syscall, which allows us to
|
||||
# do i686-linux builds on x86_64-linux machines.
|
||||
AC_CHECK_HEADERS([sys/personality.h])
|
||||
|
||||
|
||||
# Check for tr1/unordered_set.
|
||||
AC_LANG_PUSH(C++)
|
||||
AC_CHECK_HEADERS([tr1/unordered_set], [], [], [])
|
||||
AC_LANG_POP(C++)
|
||||
|
||||
|
||||
AC_DEFUN([NEED_PROG],
|
||||
[
|
||||
AC_PATH_PROG($1, $2)
|
||||
@@ -124,18 +153,19 @@ fi
|
||||
])
|
||||
|
||||
NEED_PROG(curl, curl)
|
||||
NEED_PROG(shell, bash)
|
||||
NEED_PROG(bash, bash)
|
||||
NEED_PROG(patch, patch)
|
||||
AC_PATH_PROG(xmllint, xmllint, false)
|
||||
AC_PATH_PROG(xsltproc, xsltproc, false)
|
||||
AC_PATH_PROG(jing, jing, false) # needed because xmllint --relaxng seems broken
|
||||
AC_PATH_PROG(w3m, w3m, false)
|
||||
AC_PATH_PROG(flex, flex, false)
|
||||
AC_PATH_PROG(bison, bison, false)
|
||||
NEED_PROG(perl, perl)
|
||||
NEED_PROG(sed, sed)
|
||||
NEED_PROG(tar, tar)
|
||||
AC_PATH_PROG(dot, dot)
|
||||
AC_PATH_PROG(dblatex, dblatex)
|
||||
AC_PATH_PROG(gzip, gzip)
|
||||
|
||||
AC_PATH_PROG(openssl_prog, openssl, openssl) # if not found, call openssl in $PATH
|
||||
AC_SUBST(openssl_prog)
|
||||
@@ -173,40 +203,9 @@ AC_SUBST(xmlflags)
|
||||
|
||||
AC_ARG_WITH(store-dir, AC_HELP_STRING([--with-store-dir=PATH],
|
||||
[path of the Nix store]),
|
||||
storedir=$withval, storedir='${prefix}/store')
|
||||
storedir=$withval, storedir='/nix/store')
|
||||
AC_SUBST(storedir)
|
||||
|
||||
AC_ARG_WITH(bdb, AC_HELP_STRING([--with-bdb=PATH],
|
||||
[prefix of Berkeley DB]),
|
||||
bdb=$withval, bdb=)
|
||||
AM_CONDITIONAL(HAVE_BDB, test -n "$bdb")
|
||||
if test -z "$bdb"; then
|
||||
bdb_lib='-L${top_builddir}/externals/inst-bdb/lib -ldb_cxx'
|
||||
bdb_include='-I${top_builddir}/externals/inst-bdb/include'
|
||||
else
|
||||
bdb_lib="-L$bdb/lib -ldb_cxx"
|
||||
bdb_include="-I$bdb/include"
|
||||
fi
|
||||
AC_SUBST(bdb_lib)
|
||||
AC_SUBST(bdb_include)
|
||||
|
||||
AC_ARG_WITH(aterm, AC_HELP_STRING([--with-aterm=PATH],
|
||||
[prefix of CWI ATerm library]),
|
||||
aterm=$withval, aterm=)
|
||||
AM_CONDITIONAL(HAVE_ATERM, test -n "$aterm")
|
||||
if test -z "$aterm"; then
|
||||
aterm_lib='-L${top_builddir}/externals/inst-aterm/lib -lATerm'
|
||||
aterm_include='-I${top_builddir}/externals/inst-aterm/include'
|
||||
aterm_bin='${top_builddir}/externals/inst-aterm/bin'
|
||||
else
|
||||
aterm_lib="-L$aterm/lib -lATerm"
|
||||
aterm_include="-I$aterm/include"
|
||||
aterm_bin="$aterm/bin"
|
||||
fi
|
||||
AC_SUBST(aterm_lib)
|
||||
AC_SUBST(aterm_include)
|
||||
AC_SUBST(aterm_bin)
|
||||
|
||||
AC_ARG_WITH(openssl, AC_HELP_STRING([--with-openssl=PATH],
|
||||
[prefix of the OpenSSL library]),
|
||||
openssl=$withval, openssl=)
|
||||
@@ -215,20 +214,22 @@ if test -n "$openssl"; then
|
||||
LDFLAGS="-L$openssl/lib -lcrypto $LDFLAGS"
|
||||
CFLAGS="-I$openssl/include $CFLAGS"
|
||||
CXXFLAGS="-I$openssl/include $CXXFLAGS"
|
||||
AC_DEFINE(HAVE_OPENSSL, 1, [whether to use OpenSSL])
|
||||
AC_DEFINE(HAVE_OPENSSL, 1, [Whether to use OpenSSL.])
|
||||
fi
|
||||
|
||||
AC_ARG_WITH(bzip2, AC_HELP_STRING([--with-bzip2=PATH],
|
||||
[prefix of bzip2]),
|
||||
bzip2=$withval, bzip2=)
|
||||
AM_CONDITIONAL(HAVE_BZIP2, test -n "$bzip2")
|
||||
ATERM_VERSION=2.5
|
||||
AC_SUBST(ATERM_VERSION)
|
||||
if test -z "$bzip2"; then
|
||||
# Headers and libraries will be used from the temporary installation
|
||||
# in externals/inst-bzip2.
|
||||
bzip2_lib='-L${top_builddir}/externals/inst-bzip2/lib -lbz2'
|
||||
bzip2_include='-I${top_builddir}/externals/inst-bzip2/include'
|
||||
# The binary will be copied to $libexecdir.
|
||||
bzip2_bin='${libexecdir}'
|
||||
bzip2_bin='${libexecdir}/nix'
|
||||
# But for testing, we have to use the temporary copy :-(
|
||||
bzip2_bin_test='${top_builddir}/externals/inst-bzip2/bin'
|
||||
else
|
||||
@@ -236,14 +237,42 @@ else
|
||||
bzip2_include="-I$bzip2/include"
|
||||
bzip2_bin="$bzip2/bin"
|
||||
bzip2_bin_test="$bzip2/bin"
|
||||
fi
|
||||
fi
|
||||
AC_SUBST(bzip2_lib)
|
||||
AC_SUBST(bzip2_include)
|
||||
AC_SUBST(bzip2_bin)
|
||||
AC_SUBST(bzip2_bin_test)
|
||||
|
||||
AC_ARG_WITH(sqlite, AC_HELP_STRING([--with-sqlite=PATH],
|
||||
[prefix of SQLite]),
|
||||
sqlite=$withval, sqlite=)
|
||||
AM_CONDITIONAL(HAVE_SQLITE, test -n "$sqlite")
|
||||
SQLITE_VERSION=3070500
|
||||
AC_SUBST(SQLITE_VERSION)
|
||||
if test -z "$sqlite"; then
|
||||
sqlite_lib='${top_builddir}/externals/sqlite-autoconf-$(SQLITE_VERSION)/libsqlite3.la'
|
||||
sqlite_include='-I${top_builddir}/externals/sqlite-autoconf-$(SQLITE_VERSION)'
|
||||
sqlite_bin='${top_builddir}/externals/sqlite-autoconf-$(SQLITE_VERSION)'
|
||||
else
|
||||
sqlite_lib="-L$sqlite/lib -lsqlite3"
|
||||
sqlite_include="-I$sqlite/include"
|
||||
sqlite_bin="$sqlite/bin"
|
||||
fi
|
||||
AC_SUBST(sqlite_lib)
|
||||
AC_SUBST(sqlite_include)
|
||||
AC_SUBST(sqlite_bin)
|
||||
|
||||
AC_CHECK_LIB(pthread, pthread_mutex_init)
|
||||
# Whether to use the Boehm garbage collector.
|
||||
AC_ARG_ENABLE(gc, AC_HELP_STRING([--enable-gc],
|
||||
[enable garbage collection in the Nix expression evaluator (requires Boehm GC)]),
|
||||
gc=$enableval, gc=)
|
||||
if test -n "$gc"; then
|
||||
PKG_CHECK_MODULES([BDW_GC], [bdw-gc])
|
||||
boehmgc_lib="-L$boehmgc/lib -lgc"
|
||||
CXXFLAGS="$BDW_GC_CFLAGS $CXXFLAGS"
|
||||
AC_DEFINE(HAVE_BOEHMGC, 1, [Whether to use the Boehm garbage collector.])
|
||||
fi
|
||||
AC_SUBST(boehmgc_lib)
|
||||
|
||||
|
||||
AC_ARG_ENABLE(init-state, AC_HELP_STRING([--disable-init-state],
|
||||
@@ -257,10 +286,10 @@ AC_CHECK_FUNCS([setresuid setreuid lchown])
|
||||
|
||||
|
||||
# Nice to have, but not essential.
|
||||
AC_CHECK_FUNCS([strsignal])
|
||||
AC_CHECK_FUNCS([strsignal posix_fallocate nanosleep])
|
||||
|
||||
|
||||
# This is needed if ATerm, Berkeley DB or bzip2 are static libraries,
|
||||
# This is needed if ATerm or bzip2 are static libraries,
|
||||
# and the Nix libraries are dynamic.
|
||||
if test "$(uname)" = "Darwin"; then
|
||||
LDFLAGS="-all_load $LDFLAGS"
|
||||
|
||||
@@ -29,10 +29,18 @@ sub createLinks {
|
||||
$baseName =~ s/^.*\///g; # strip directory
|
||||
my $dstFile = "$dstDir/$baseName";
|
||||
|
||||
# The files below are special-cased so that they don't show up
|
||||
# in user profiles, either because they are useless, or
|
||||
# because they would cause pointless collisions (e.g., each
|
||||
# Python package brings its own
|
||||
# `$out/lib/pythonX.Y/site-packages/easy-install.pth'.)
|
||||
# Urgh, hacky...
|
||||
if ($srcFile =~ /\/propagated-build-inputs$/ ||
|
||||
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$/)
|
||||
{
|
||||
@@ -160,4 +168,4 @@ while (scalar(keys %postponed) > 0) {
|
||||
print STDERR "created $symlinks symlinks in user environment\n";
|
||||
|
||||
|
||||
symlink($ENV{"manifest"}, "$out/manifest") or die "cannot create manifest";
|
||||
symlink($ENV{"manifest"}, "$out/manifest.nix") or die "cannot create manifest";
|
||||
|
||||
@@ -11,4 +11,8 @@ derivation {
|
||||
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;
|
||||
|
||||
# Building user environments remotely just causes huge amounts of
|
||||
# network traffic, so don't do that.
|
||||
preferLocalBuild = true;
|
||||
}
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
#! @shell@ -e
|
||||
|
||||
# Cygwin compatibility hack: bunzip2 expects cygwin.dll in $PATH.
|
||||
export PATH=@coreutils@
|
||||
|
||||
@coreutils@/mkdir $out
|
||||
@coreutils@/mkdir $out/tmp
|
||||
cd $out/tmp
|
||||
@@ -8,9 +11,15 @@ inputs=($inputs)
|
||||
for ((n = 0; n < ${#inputs[*]}; n += 2)); do
|
||||
channelName=${inputs[n]}
|
||||
channelTarball=${inputs[n+1]}
|
||||
|
||||
echo "unpacking channel $channelName"
|
||||
|
||||
@bunzip2@ < $channelTarball | @tar@ xf -
|
||||
|
||||
if test -e */channel-name; then
|
||||
channelName="$(@coreutils@/cat */channel-name)"
|
||||
fi
|
||||
|
||||
nr=1
|
||||
attrName=$(echo $channelName | @tr@ -- '- ' '__')
|
||||
dirName=$attrName
|
||||
|
||||
@@ -7,8 +7,6 @@ dst=$out/tmp.nar.bz2
|
||||
|
||||
@bzip2@ < tmp > $dst
|
||||
|
||||
@bindir@/nix-hash -vvvvv --flat --type $hashAlgo --base32 tmp > $out/nar-hash
|
||||
|
||||
@bindir@/nix-hash --flat --type $hashAlgo --base32 $dst > $out/narbz2-hash
|
||||
|
||||
@coreutils@/mv $out/tmp.nar.bz2 $out/$(@coreutils@/cat $out/narbz2-hash).nar.bz2
|
||||
|
||||
@@ -6,7 +6,12 @@ XSLTPROC = $(xsltproc) $(xmlflags) \
|
||||
--param xref.with.number.and.title 1 \
|
||||
--param toc.section.depth 3 \
|
||||
--param admon.style \'\' \
|
||||
--param callout.graphics.extension \'.gif\'
|
||||
--param callout.graphics.extension \'.gif\' \
|
||||
--param contrib.inline.enabled 0
|
||||
|
||||
dblatex_opts = \
|
||||
-P doc.collab.show=0 \
|
||||
-P latex.output.revhistory=0
|
||||
|
||||
# Note: we use GIF for now, since the PNGs shipped with Docbook aren't
|
||||
# transparent.
|
||||
@@ -14,27 +19,24 @@ XSLTPROC = $(xsltproc) $(xmlflags) \
|
||||
man1_MANS = nix-env.1 nix-build.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-pack-closure.1 nix-unpack-closure.1 \
|
||||
nix-install-package.1 nix-hash.1 nix-copy-closure.1
|
||||
|
||||
man8_MANS = nix-worker.8
|
||||
|
||||
FIGURES = figures/user-environments.png
|
||||
|
||||
MANUAL_SRCS = manual.xml introduction.xml installation.xml \
|
||||
package-management.xml writing-nix-expressions.xml builtins.xml \
|
||||
build-farm.xml \
|
||||
$(man1_MANS:.1=.xml) \
|
||||
$(man1_MANS:.1=.xml) $(man8_MANS:.8=.xml) \
|
||||
troubleshooting.xml bugs.xml opt-common.xml opt-common-syn.xml \
|
||||
env-common.xml quick-start.xml nix-lang-ref.xml glossary.xml \
|
||||
conf-file.xml release-notes.xml \
|
||||
style.css images
|
||||
|
||||
# Note: RelaxNG validation requires xmllint >= 2.7.4.
|
||||
manual.is-valid: $(MANUAL_SRCS) version.txt
|
||||
# $(XMLLINT) --xinclude $< | $(XMLLINT) --noout --nonet --relaxng $(docbookrng)/docbook.rng -
|
||||
if test "$(jing)" != "false"; then \
|
||||
$(XMLLINT) --xinclude $< | $(jing) $(docbookrng)/docbook.rng /dev/fd/0; \
|
||||
else \
|
||||
echo "Not validating."; \
|
||||
fi
|
||||
$(XMLLINT) --noout --nonet --xinclude --noxincludenode --relaxng $(docbookrng)/docbook.rng $<
|
||||
touch $@
|
||||
|
||||
version.txt:
|
||||
@@ -49,7 +51,7 @@ manual.html: $(MANUAL_SRCS) manual.is-valid images
|
||||
|
||||
manual.pdf: $(MANUAL_SRCS) manual.is-valid images
|
||||
if test "$(dblatex)" != ""; then \
|
||||
$(dblatex) manual.xml; \
|
||||
$(dblatex) $(dblatex_opts) manual.xml; \
|
||||
else \
|
||||
echo "Please install dblatex and rerun configure."; \
|
||||
exit 1; \
|
||||
@@ -76,12 +78,16 @@ NEWS.txt: release-notes.xml
|
||||
all-local: manual.html NEWS.html NEWS.txt
|
||||
|
||||
install-data-local: manual.html
|
||||
$(INSTALL) -d $(DESTDIR)$(datadir)/nix/manual
|
||||
$(INSTALL_DATA) manual.html $(DESTDIR)$(datadir)/nix/manual
|
||||
$(INSTALL_DATA) style.css $(DESTDIR)$(datadir)/nix/manual
|
||||
cp -r images $(DESTDIR)$(datadir)/nix/manual/images
|
||||
$(INSTALL) -d $(DESTDIR)$(datadir)/nix/manual/figures
|
||||
$(INSTALL_DATA) $(FIGURES) $(DESTDIR)$(datadir)/nix/manual/figures
|
||||
$(INSTALL) -d $(DESTDIR)$(docdir)/manual
|
||||
$(INSTALL_DATA) manual.html $(DESTDIR)$(docdir)/manual
|
||||
ln -sf manual.html $(DESTDIR)$(docdir)/manual/index.html
|
||||
$(INSTALL_DATA) style.css $(DESTDIR)$(docdir)/manual
|
||||
cp -r images $(DESTDIR)$(docdir)/manual/images
|
||||
$(INSTALL) -d $(DESTDIR)$(docdir)/manual/figures
|
||||
$(INSTALL_DATA) $(FIGURES) $(DESTDIR)$(docdir)/manual/figures
|
||||
$(INSTALL) -d $(DESTDIR)$(docdir)/release-notes
|
||||
$(INSTALL_DATA) NEWS.html $(DESTDIR)$(docdir)/release-notes/index.html
|
||||
$(INSTALL_DATA) style.css $(DESTDIR)$(docdir)/release-notes/
|
||||
|
||||
images:
|
||||
mkdir images
|
||||
|
||||
@@ -56,7 +56,7 @@ build farm, since:
|
||||
<para>TODO</para>
|
||||
|
||||
<para>The sources of the Nix build farm are at <link
|
||||
xlink:href='https://svn.cs.uu.nl:12443/repos/trace/release/trunk'/>.</para>
|
||||
xlink:href='https://svn.nixos.org/repos/nix/release/trunk'/>.</para>
|
||||
|
||||
</section>
|
||||
|
||||
@@ -85,7 +85,7 @@ nix@scratchy.labs.cs.uu.nl i686-linux /home/nix/.ssh/id_scratchy_auto
|
||||
|
||||
<para>An example build hook can be found in the Nix build farm
|
||||
sources: <link
|
||||
xlink:href='https://svn.cs.uu.nl:12443/repos/trace/release/trunk/common/distributed/build-remote.pl'
|
||||
xlink:href='https://svn.nixos.org/repos/nix/release/trunk/common/distributed/build-remote.pl'
|
||||
/>. It should be suitable for most purposes, with maybe some minor
|
||||
adjustments. It uses <command>ssh</command> and
|
||||
<command>rsync</command> to copy the build inputs and outputs and
|
||||
|
||||
@@ -77,18 +77,25 @@ attrValues = attrs: map (name: builtins.getAttr name attrs) (builtins.attrNames
|
||||
if builtins ? getEnv then builtins.getEnv "PATH" else ""</programlisting>
|
||||
|
||||
This allows a Nix expression to fall back gracefully on older Nix
|
||||
installations that don’t have the desired built-in function.
|
||||
However, in that case you should not write
|
||||
installations that don’t have the desired built-in
|
||||
function.</para></listitem>
|
||||
|
||||
<programlisting>
|
||||
if builtins ? getEnv then __getEnv "PATH" else ""</programlisting>
|
||||
</varlistentry>
|
||||
|
||||
This Nix expression will trigger an “undefined variable” error on
|
||||
older Nix versions since <function>__getEnv</function> doesn’t
|
||||
exist. <literal>builtins.getEnv</literal>, on the other hand, is
|
||||
safe since <literal>builtins</literal> always exists and attribute
|
||||
selection is lazy, so it’s only performed if the test
|
||||
succeeds.</para></listitem>
|
||||
|
||||
<varlistentry><term><function>builtins.compareVersions</function>
|
||||
<replaceable>s1</replaceable> <replaceable>s2</replaceable></term>
|
||||
|
||||
<listitem><para>Compare two strings representing versions and
|
||||
return <literal>-1</literal> if version
|
||||
<replaceable>s1</replaceable> is older than version
|
||||
<replaceable>s2</replaceable>, <literal>0</literal> if they are
|
||||
the same, and <literal>1</literal> if
|
||||
<replaceable>s1</replaceable> is newer than
|
||||
<replaceable>s2</replaceable>. The version comparison algorithm
|
||||
is the same as the one used by <link
|
||||
linkend="ssec-version-comparisons"><command>nix-env
|
||||
-u</command></link>.</para></listitem>
|
||||
|
||||
</varlistentry>
|
||||
|
||||
@@ -146,6 +153,16 @@ if builtins ? getEnv then __getEnv "PATH" else ""</programlisting>
|
||||
</varlistentry>
|
||||
|
||||
|
||||
<varlistentry><term><function>builtins.div</function>
|
||||
<replaceable>e1</replaceable> <replaceable>e2</replaceable></term>
|
||||
|
||||
<listitem><para>Return the quotient of the integers
|
||||
<replaceable>e1</replaceable> and
|
||||
<replaceable>e2</replaceable>.</para></listitem>
|
||||
|
||||
</varlistentry>
|
||||
|
||||
|
||||
<varlistentry><term><function>builtins.filterSource</function>
|
||||
<replaceable>e1</replaceable> <replaceable>e2</replaceable></term>
|
||||
|
||||
@@ -307,6 +324,16 @@ x: x + 456</programlisting>
|
||||
</varlistentry>
|
||||
|
||||
|
||||
<varlistentry><term><function>builtins.intersectAttrs</function>
|
||||
<replaceable>e1</replaceable> <replaceable>e2</replaceable></term>
|
||||
|
||||
<listitem><para>Return an attribute set consisting of the
|
||||
attributes in the set <replaceable>e2</replaceable> that also
|
||||
exist in the set <replaceable>e1</replaceable>.</para></listitem>
|
||||
|
||||
</varlistentry>
|
||||
|
||||
|
||||
<varlistentry><term><function>builtins.isAttrs</function>
|
||||
<replaceable>e</replaceable></term>
|
||||
|
||||
@@ -337,6 +364,36 @@ x: x + 456</programlisting>
|
||||
</varlistentry>
|
||||
|
||||
|
||||
<varlistentry><term><function>builtins.isString</function>
|
||||
<replaceable>e</replaceable></term>
|
||||
|
||||
<listitem><para>Return <literal>true</literal> if
|
||||
<replaceable>e</replaceable> evaluates to a string, and
|
||||
<literal>false</literal> otherwise.</para></listitem>
|
||||
|
||||
</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
|
||||
<literal>false</literal> otherwise.</para></listitem>
|
||||
|
||||
</varlistentry>
|
||||
|
||||
|
||||
<varlistentry><term><function>builtins.isBool</function>
|
||||
<replaceable>e</replaceable></term>
|
||||
|
||||
<listitem><para>Return <literal>true</literal> if
|
||||
<replaceable>e</replaceable> evaluates to a bool, and
|
||||
<literal>false</literal> otherwise.</para></listitem>
|
||||
|
||||
</varlistentry>
|
||||
|
||||
|
||||
<varlistentry><term><function>isNull</function>
|
||||
<replaceable>e</replaceable></term>
|
||||
|
||||
@@ -352,6 +409,15 @@ x: x + 456</programlisting>
|
||||
</varlistentry>
|
||||
|
||||
|
||||
<varlistentry><term><function>builtins.length</function>
|
||||
<replaceable>e</replaceable></term>
|
||||
|
||||
<listitem><para>Return the length of the list
|
||||
<replaceable>e</replaceable>.</para></listitem>
|
||||
|
||||
</varlistentry>
|
||||
|
||||
|
||||
<varlistentry><term><function>builtins.lessThan</function>
|
||||
<replaceable>e1</replaceable> <replaceable>e2</replaceable></term>
|
||||
|
||||
@@ -408,6 +474,31 @@ map (x: "foo" + x) ["bar" "bla" "abc"]</programlisting>
|
||||
</varlistentry>
|
||||
|
||||
|
||||
<varlistentry><term><function>builtins.mul</function>
|
||||
<replaceable>e1</replaceable> <replaceable>e2</replaceable></term>
|
||||
|
||||
<listitem><para>Return the product of the integers
|
||||
<replaceable>e1</replaceable> and
|
||||
<replaceable>e2</replaceable>.</para></listitem>
|
||||
|
||||
</varlistentry>
|
||||
|
||||
|
||||
<varlistentry><term><function>builtins.parseDrvName</function>
|
||||
<replaceable>s</replaceable></term>
|
||||
|
||||
<listitem><para>Split the string <replaceable>s</replaceable> into
|
||||
a package name and version. The package name is everything up to
|
||||
but not including the first dash followed by a digit, and the
|
||||
version is everything following that dash. The result is returned
|
||||
in an attribute set <literal>{name, version}</literal>. Thus,
|
||||
<literal>builtins.parseDrvName "nix-0.12pre12876"</literal>
|
||||
returns <literal>{name = "nix"; version =
|
||||
"0.12pre12876";}</literal>.</para></listitem>
|
||||
|
||||
</varlistentry>
|
||||
|
||||
|
||||
<varlistentry><term><function>builtins.pathExists</function>
|
||||
<replaceable>path</replaceable></term>
|
||||
|
||||
|
||||
@@ -60,27 +60,6 @@ env-keep-derivations = false
|
||||
</varlistentry>
|
||||
|
||||
|
||||
<varlistentry xml:id="conf-gc-reserved-space"><term><literal>gc-reserved-space</literal></term>
|
||||
|
||||
<listitem><para>This option specifies how much space should be
|
||||
reserved in normal use so that the garbage collector can run
|
||||
succesfully. Since the garbage collector must perform Berkeley DB
|
||||
transactions, it needs some disk space for itself. However, when
|
||||
the disk is full, this space is not available, so the collector
|
||||
would not be able to run precisely when it is most needed.</para>
|
||||
|
||||
<para>For this reason, when Nix is run, it allocates a file
|
||||
<filename>/nix/var/nix/db/reserved</filename> of the size
|
||||
specified by this option. When the garbage collector is run, this
|
||||
file is deleted before the Berkeley DB environment is opened.
|
||||
This should give it enough room to proceed.</para>
|
||||
|
||||
<para>The default is <literal>1048576</literal> (1
|
||||
MiB).</para></listitem>
|
||||
|
||||
</varlistentry>
|
||||
|
||||
|
||||
<varlistentry><term><literal>env-keep-derivations</literal></term>
|
||||
|
||||
<listitem><para>If <literal>false</literal> (default), derivations
|
||||
@@ -118,6 +97,25 @@ env-keep-derivations = false
|
||||
</varlistentry>
|
||||
|
||||
|
||||
<varlistentry xml:id="conf-build-cores"><term><literal>build-cores</literal></term>
|
||||
|
||||
<listitem><para>Sets the value of the
|
||||
<envar>NIX_BUILD_CORES</envar> environment variable in the
|
||||
invocation of builders. Builders can use this variable at their
|
||||
discretion to control the maximum amount of parallelism. For
|
||||
instance, in Nixpkgs, if the derivation attribute
|
||||
<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
|
||||
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
|
||||
system.</para></listitem>
|
||||
|
||||
</varlistentry>
|
||||
|
||||
|
||||
<varlistentry xml:id="conf-build-max-silent-time"><term><literal>build-max-silent-time</literal></term>
|
||||
|
||||
<listitem>
|
||||
@@ -254,7 +252,17 @@ build-use-chroot = /dev /proc /bin</programlisting>
|
||||
<filename>configure</filename> at build time.</para></listitem>
|
||||
|
||||
</varlistentry>
|
||||
|
||||
|
||||
|
||||
<varlistentry><term><literal>fsync-metadata</literal></term>
|
||||
|
||||
<listitem><para>If set to <literal>true</literal>, changes to the
|
||||
Nix store metadata (in <filename>/nix/var/nix/db</filename>) are
|
||||
synchronously flushed to disk. This improves robustness in case
|
||||
of system crashes, but reduces performance. The default is
|
||||
<literal>true</literal>.</para></listitem>
|
||||
|
||||
</varlistentry>
|
||||
|
||||
</variablelist>
|
||||
|
||||
|
||||
@@ -151,12 +151,12 @@ $ mount -o bind /mnt/otherdisk/nix /nix</screen>
|
||||
<para>On the basis of this information, and whatever persistent
|
||||
state the build hook keeps about other machines and their current
|
||||
load, it has to decide what to do with the build. It should print
|
||||
out on file descriptor 3 one of the following responses (terminated
|
||||
by a newline, <literal>"\n"</literal>):
|
||||
out on standard error one of the following responses (terminated by
|
||||
a newline, <literal>"\n"</literal>):
|
||||
|
||||
<variablelist>
|
||||
|
||||
<varlistentry><term><literal>decline</literal></term>
|
||||
<varlistentry><term><literal># decline</literal></term>
|
||||
|
||||
<listitem><para>The build hook is not willing or able to perform
|
||||
the build; the calling Nix process should do the build itself,
|
||||
@@ -164,7 +164,7 @@ $ mount -o bind /mnt/otherdisk/nix /nix</screen>
|
||||
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry><term><literal>postpone</literal></term>
|
||||
<varlistentry><term><literal># postpone</literal></term>
|
||||
|
||||
<listitem><para>The build hook cannot perform the build now, but
|
||||
can do so in the future (e.g., because all available build slots
|
||||
@@ -174,7 +174,7 @@ $ mount -o bind /mnt/otherdisk/nix /nix</screen>
|
||||
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry><term><literal>accept</literal></term>
|
||||
<varlistentry><term><literal># accept</literal></term>
|
||||
|
||||
<listitem><para>The build hook has accepted the
|
||||
build.</para></listitem>
|
||||
@@ -185,37 +185,12 @@ $ mount -o bind /mnt/otherdisk/nix /nix</screen>
|
||||
|
||||
</para>
|
||||
|
||||
<para>If the build hook accepts the build, it is possible that it is
|
||||
no longer necessary to do the build because some other process has
|
||||
performed the build in the meantime. To prevent races, the hook
|
||||
must read from file descriptor 4 a single line that tells it whether
|
||||
to continue:
|
||||
|
||||
<variablelist>
|
||||
|
||||
<varlistentry><term><literal>cancel</literal></term>
|
||||
|
||||
<listitem><para>The build has already been done, so the hook
|
||||
should exit.</para></listitem>
|
||||
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry><term><literal>okay</literal></term>
|
||||
|
||||
<listitem><para>The hook should proceed with the build. At this
|
||||
point, the calling Nix process has acquired locks on the output
|
||||
path, so no other Nix process will perform the
|
||||
build.</para></listitem>
|
||||
|
||||
</varlistentry>
|
||||
|
||||
</variablelist>
|
||||
|
||||
</para>
|
||||
|
||||
<para>If the hook has been told to proceed, Nix will store in the
|
||||
hook’s current directory a number of text files that contain
|
||||
information about the derivation:
|
||||
<para>After sending <literal># accept</literal>, the hook should
|
||||
read one line from standard input, which will be the string
|
||||
<literal>okay</literal>. It can then proceed with the build.
|
||||
Before sending <literal>okay</literal>, Nix will store in the hook’s
|
||||
current directory a number of text files that contain information
|
||||
about the derivation:
|
||||
|
||||
<variablelist>
|
||||
|
||||
@@ -255,7 +230,9 @@ $ mount -o bind /mnt/otherdisk/nix /nix</screen>
|
||||
<para>The hook should copy the inputs to the remote machine,
|
||||
register the validity of the inputs, perform the remote build, and
|
||||
copy the outputs back to the local machine. An exit code other than
|
||||
<literal>0</literal> indicates that the hook has failed.</para>
|
||||
<literal>0</literal> indicates that the hook has failed. An exit
|
||||
code equal to 100 means that the remote build failed (as opposed to,
|
||||
e.g., a network error).</para>
|
||||
|
||||
</listitem>
|
||||
|
||||
@@ -274,6 +251,38 @@ $ mount -o bind /mnt/otherdisk/nix /nix</screen>
|
||||
</varlistentry>
|
||||
|
||||
|
||||
<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.
|
||||
See <xref linkend="sec-sharing-packages" /> for details. Each path
|
||||
should be the <filename>/nix</filename> directory of a remote Nix
|
||||
installation (i.e., not the <filename>/nix/store</filename>
|
||||
directory). The paths are subject to globbing, so you can set it so
|
||||
something like <literal>/var/run/nix/remote-stores/*/nix</literal>
|
||||
and mount multiple remote filesystems in
|
||||
<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
|
||||
this variable that matters is the one that the
|
||||
<command>nix-worker</command> process uses. So if you want to
|
||||
change it, you have to restart the daemon.</para></listitem>
|
||||
|
||||
</varlistentry>
|
||||
|
||||
|
||||
<varlistentry><term><envar>GC_INITIAL_HEAP_SIZE</envar></term>
|
||||
|
||||
<listitem><para>If Nix has been configured to use the Boehm garbage
|
||||
collector, this variable sets the initial size of the heap in bytes.
|
||||
It defaults to 384 MiB. Setting it to a low value reduces memory
|
||||
consumption, but will increase runtime due to the overhead of
|
||||
garbage collection.</para></listitem>
|
||||
|
||||
</varlistentry>
|
||||
|
||||
|
||||
</variablelist>
|
||||
|
||||
|
||||
|
||||
@@ -42,25 +42,22 @@ platforms as well.</para>
|
||||
<section><title>Obtaining Nix</title>
|
||||
|
||||
<para>The easiest way to obtain Nix is to download a <link
|
||||
xlink:href="http://nix.cs.uu.nl/">source distribution</link>. RPMs
|
||||
xlink:href="http://nixos.org/">source distribution</link>. RPMs
|
||||
for Red Hat, SuSE, and Fedora Core are also available.</para>
|
||||
|
||||
<para>Alternatively, the most recent sources of Nix can be obtained
|
||||
from its <link
|
||||
xlink:href="https://svn.cs.uu.nl:12443/repos/trace/nix/trunk">Subversion
|
||||
xlink:href="https://svn.nixos.org/repos/nix/nix/trunk">Subversion
|
||||
repository</link>. For example, the following command will check out
|
||||
the latest revision into a directory called
|
||||
<filename>nix</filename>:</para>
|
||||
|
||||
<screen>
|
||||
$ svn checkout https://svn.cs.uu.nl:12443/repos/trace/nix/trunk nix</screen>
|
||||
$ svn checkout https://svn.nixos.org/repos/nix/nix/trunk nix</screen>
|
||||
|
||||
<para>Likewise, specific releases can be obtained from the <link
|
||||
xlink:href="https://svn.cs.uu.nl:12443/repos/trace/nix/tags">tags
|
||||
directory</link> of the repository. If you don't have Subversion, you
|
||||
can also download an automatically generated <link
|
||||
xlink:href="https://svn.cs.uu.nl:12443/dist/trace/">compressed
|
||||
tar-file</link> of the head revision of the trunk.</para>
|
||||
xlink:href="https://svn.nixos.org/repos/nix/nix/tags">tags
|
||||
directory</link> of the repository.</para>
|
||||
|
||||
</section>
|
||||
|
||||
@@ -99,18 +96,21 @@ ubiquitous 2.5.4a won't. Note that these are only required if you
|
||||
modify the parser or when you are building from the Subversion
|
||||
repository.</para>
|
||||
|
||||
<para>Nix uses Sleepycat's Berkeley DB, CWI's ATerm library and the
|
||||
bzip2 compressor (including the bzip2 library). These are included in
|
||||
the Nix source distribution. If you build from the Subversion
|
||||
repository, you must download them yourself and place them in the
|
||||
<filename>externals/</filename> directory. See
|
||||
<para>Nix uses the bzip2 compressor (including the bzip2 library). It
|
||||
is included in the Nix source distribution. If you build from the
|
||||
Subversion repository, you must download it yourself and place it in
|
||||
the <filename>externals/</filename> directory. See
|
||||
<filename>externals/Makefile.am</filename> for the precise URLs of
|
||||
these packages. Alternatively, if you already have them installed,
|
||||
you can use <command>configure</command>'s
|
||||
<option>--with-bdb</option>, <option>--with-aterm</option> and
|
||||
<option>--with-bzip2</option> options to point to their respective
|
||||
locations. Note that Berkeley DB <emphasis>must</emphasis> be version
|
||||
4.5; other versions may not have compatible database formats.</para>
|
||||
this packages. Alternatively, if you already have it installed, you
|
||||
can use <command>configure</command>'s <option>--with-bzip2</option>
|
||||
options to point to their respective locations.</para>
|
||||
|
||||
<para>Nix can optionally use the <link
|
||||
xlink:href="http://www.hpl.hp.com/personal/Hans_Boehm/gc/">Boehm
|
||||
garbage collector</link> to reduce the evaluator’s memory consumption.
|
||||
To enable it, install <literal>pkgconfig</literal> and the Boehm
|
||||
garbage collector, and pass the flag <option>--enable-gc</option> to
|
||||
<command>configure</command>.</para>
|
||||
|
||||
</section>
|
||||
|
||||
@@ -131,23 +131,32 @@ $ make install</screen>
|
||||
preceded by the command:
|
||||
|
||||
<screen>
|
||||
$ ./bootstrap</screen>
|
||||
$ ./bootstrap.sh</screen>
|
||||
|
||||
</para>
|
||||
|
||||
<para>The installation path can be specified by passing the
|
||||
<option>--prefix=<replaceable>prefix</replaceable></option> to
|
||||
<command>configure</command>. The default installation directory is
|
||||
<filename>/nix</filename>. You can change this to any location you
|
||||
like. You must have write permission to the
|
||||
<filename>/usr/local</filename>. You can change this to any location
|
||||
you like. You must have write permission to the
|
||||
<replaceable>prefix</replaceable> path.</para>
|
||||
|
||||
<warning><para>It is best <emphasis>not</emphasis> to change the
|
||||
installation prefix from its default, since doing so makes it
|
||||
impossible to use pre-built binaries from the standard Nixpkgs
|
||||
channels.</para></warning>
|
||||
<para>Nix keeps its <emphasis>store</emphasis> (the place where
|
||||
packages are stored) in <filename>/nix/store</filename> by default.
|
||||
This can be changed using
|
||||
<option>--with-store-dir=<replaceable>path</replaceable></option>.</para>
|
||||
|
||||
<para>If you want to rebuilt the documentation, pass the full path to
|
||||
<warning><para>It is best <emphasis>not</emphasis> to change the Nix
|
||||
store from its default, since doing so makes it impossible to use
|
||||
pre-built binaries from the standard Nixpkgs channels — that is, all
|
||||
packages will need to be built from source.</para></warning>
|
||||
|
||||
<para>Nix keeps state (such as its database and log files) in
|
||||
<filename>/nix/var</filename> by default. This can be changed using
|
||||
<option>--localstatedir=<replaceable>path</replaceable></option>.</para>
|
||||
|
||||
<para>If you want to rebuild the documentation, pass the full path to
|
||||
the DocBook RELAX NG schemas and to the DocBook XSL stylesheets using
|
||||
the
|
||||
<option>--with-docbook-rng=<replaceable>path</replaceable></option>
|
||||
@@ -158,27 +167,26 @@ options.</para>
|
||||
</section>
|
||||
|
||||
|
||||
<section><title>Installing from RPMs</title>
|
||||
<section><title>Installing a binary distribution</title>
|
||||
|
||||
<para>RPM packages of Nix can be downloaded from <link
|
||||
xlink:href="http://nix.cs.uu.nl/" />. These RPMs should work for most
|
||||
fairly recent releases of SuSE and Red Hat Linux. They have been
|
||||
known to work work on SuSE Linux 8.1 and 9.0, and Red Hat 9.0. In
|
||||
fact, it should work on any RPM-based Linux distribution based on
|
||||
<literal>glibc</literal> 2.3 or later.</para>
|
||||
|
||||
<para>Once downloaded, the RPMs can be installed or upgraded using
|
||||
<command>rpm -U</command>. For example,
|
||||
<para>RPM and Deb packages of Nix for a number of different versions
|
||||
of Fedora, openSUSE, Debian and Ubuntu can be downloaded from <link
|
||||
xlink:href="http://nixos.org/" />. Once downloaded, the RPMs can be
|
||||
installed or upgraded using <command>rpm -U</command>. For example,
|
||||
|
||||
<screen>
|
||||
$ rpm -U nix-0.5pre664-1.i386.rpm</screen>
|
||||
$ rpm -U nix-0.13pre18104-1.i386.rpm</screen>
|
||||
|
||||
Likewise, for a Deb package:
|
||||
|
||||
<screen>
|
||||
$ dpkg -i nix_0.13pre18104-1_amd64.deb</screen>
|
||||
|
||||
</para>
|
||||
|
||||
<para>The RPMs install into the directory <filename>/nix</filename>.
|
||||
Nix can be uninstalled using <command>rpm -e nix</command>. After
|
||||
this it will be necessary to manually remove the Nix store and other
|
||||
auxiliary data:
|
||||
<para>Nix can be uninstalled using <command>rpm -e nix</command> or
|
||||
<command>dpkg -r nix</command>. After this you should manually remove
|
||||
the Nix store and other auxiliary data, if desired:
|
||||
|
||||
<screen>
|
||||
$ rm -rf /nix/store
|
||||
@@ -189,18 +197,20 @@ $ rm -rf /nix/var</screen>
|
||||
</section>
|
||||
|
||||
|
||||
<!-- TODO: should be updated
|
||||
<section><title>Upgrading Nix through Nix</title>
|
||||
|
||||
<para>You can install the latest stable version of Nix through Nix
|
||||
itself by subscribing to the channel <link
|
||||
xlink:href="http://nix.cs.uu.nl/dist/nix/channels-v3/nix-stable" />,
|
||||
xlink:href="http://nixos.org/releases/nix/channels/nix-stable" />,
|
||||
or the latest unstable version by subscribing to the channel <link
|
||||
xlink:href="http://nix.cs.uu.nl/dist/nix/channels-v3/nix-unstable" />.
|
||||
xlink:href="http://nixos.org/releases/nix/channels/nix-unstable" />.
|
||||
You can also do a <link linkend="sec-one-click">one-click
|
||||
installation</link> by clicking on the package links at <link
|
||||
xlink:href="http://nix.cs.uu.nl/dist/nix/" />.</para>
|
||||
xlink:href="http://nixos.org/releases/full-index-nix.html" />.</para>
|
||||
|
||||
</section>
|
||||
-->
|
||||
|
||||
|
||||
<section><title>Security</title>
|
||||
@@ -334,8 +344,8 @@ $ chown -R root /nix/store /nix/var/nix</screen>
|
||||
|
||||
</para>
|
||||
|
||||
<para>The Nix daemon should be started as follows (as
|
||||
<literal>root</literal>):
|
||||
<para>The <link linkend="sec-nix-worker">Nix daemon</link> should be
|
||||
started as follows (as <literal>root</literal>):
|
||||
|
||||
<screen>
|
||||
$ nix-worker --daemon</screen>
|
||||
|
||||
@@ -113,7 +113,7 @@ $ nix-env --rollback
|
||||
|
||||
<simplesect><title>Garbage collection</title>
|
||||
|
||||
<para>When you install a package like this…
|
||||
<para>When you uninstall a package like this…
|
||||
|
||||
<screen>
|
||||
$ nix-env --uninstall firefox
|
||||
@@ -240,7 +240,7 @@ configuration (e.g., to build configuration files in
|
||||
possible to easily roll back the entire configuration of the system to
|
||||
an earlier state. Also, users can install software without root
|
||||
privileges. For more information and downloads, see the <link
|
||||
xlink:href="http://nix.cs.uu.nl/nixos/">NixOS homepage</link>.</para>
|
||||
xlink:href="http://nixos.org/">NixOS homepage</link>.</para>
|
||||
|
||||
</simplesect>
|
||||
|
||||
@@ -257,14 +257,15 @@ xlink:href="http://nix.cs.uu.nl/nixos/">NixOS homepage</link>.</para>
|
||||
|
||||
<section><title>About us</title>
|
||||
|
||||
<para>Nix was developed at the <link
|
||||
<para>Nix was originally developed at the <link
|
||||
xlink:href="http://www.cs.uu.nl/">Department of Information and
|
||||
Computing Sciences</link>, Utrecht University by the <link
|
||||
xlink:href="http://www.cs.uu.nl/wiki/Trace/WebHome">TraCE
|
||||
project</link>. The project is funded by the Software Engineering
|
||||
Research Program <link
|
||||
project</link> (2003-2008). The project was funded by the Software
|
||||
Engineering Research Program <link
|
||||
xlink:href="http://www.jacquard.nl/">Jacquard</link> to improve the
|
||||
support for variability in software systems.</para>
|
||||
support for variability in software systems. Further funding is now
|
||||
provided by the NIRICT LaQuSo Build Farm project.</para>
|
||||
|
||||
</section>
|
||||
|
||||
@@ -299,27 +300,27 @@ Lesser General Public License for more details.</para>
|
||||
|
||||
<para>Some background information on Nix can be found in a number of
|
||||
papers. The ICSE 2004 paper <citetitle
|
||||
xlink:href='http://www.cs.uu.nl/~eelco/pubs/immdsd-icse2004-final.pdf'>Imposing
|
||||
xlink:href='http://www.st.ewi.tudelft.nl/~dolstra/pubs/immdsd-icse2004-final.pdf'>Imposing
|
||||
a Memory Management Discipline on Software Deployment</citetitle>
|
||||
discusses the hashing mechanism used to ensure reliable dependency
|
||||
identification and non-interference between different versions and
|
||||
variants of packages. The LISA 2004 paper <citetitle
|
||||
xlink:href='http://www.cs.uu.nl/~eelco/pubs/nspfssd-lisa2004-final.pdf'>Nix:
|
||||
xlink:href='http://www.st.ewi.tudelft.nl/~dolstra/pubs/nspfssd-lisa2004-final.pdf'>Nix:
|
||||
A Safe and Policy-Free System for Software Deployment</citetitle>
|
||||
gives a more general discussion of Nix from a system-administration
|
||||
perspective. The CBSE 2005 paper <citetitle
|
||||
xlink:href='http://www.cs.uu.nl/~eelco/pubs/eupfcdm-cbse2005-final.pdf'>Efficient
|
||||
xlink:href='http://www.st.ewi.tudelft.nl/~dolstra/pubs/eupfcdm-cbse2005-final.pdf'>Efficient
|
||||
Upgrading in a Purely Functional Component Deployment Model
|
||||
</citetitle> is about transparent patch deployment in Nix. The SCM-12
|
||||
paper <citetitle
|
||||
xlink:href='http://www.cs.uu.nl/~eelco/pubs/servicecm-scm12-final.pdf'>
|
||||
xlink:href='http://www.st.ewi.tudelft.nl/~dolstra/pubs/servicecm-scm12-final.pdf'>
|
||||
Service Configuration Management</citetitle> shows how services (e.g.,
|
||||
web servers) can be deployed and managed through Nix. A short
|
||||
overview of NixOS is given in the HotOS XI paper <citetitle
|
||||
xlink:href="http://www.cs.uu.nl/~eelco/pubs/hotos-final.pdf">Purely
|
||||
xlink:href="http://www.st.ewi.tudelft.nl/~dolstra/pubs/hotos-final.pdf">Purely
|
||||
Functional System Configuration Management</citetitle>. The Nix
|
||||
homepage has <link
|
||||
xlink:href="http://nix.cs.uu.nl/docs/papers.html">an up-to-date list
|
||||
xlink:href="http://nixos.org/docs/papers.html">an up-to-date list
|
||||
of Nix-related papers</link>.</para>
|
||||
|
||||
<para>Nix is the subject of Eelco Dolstra’s PhD thesis <citetitle
|
||||
@@ -328,7 +329,7 @@ Purely Functional Software Deployment Model</citetitle>, which
|
||||
contains most of the papers listed above.</para>
|
||||
|
||||
<para>Nix has a homepage at <link
|
||||
xlink:href="http://nix.cs.uu.nl/"/>.</para>
|
||||
xlink:href="http://nixos.org/"/>.</para>
|
||||
|
||||
</section>
|
||||
|
||||
|
||||
@@ -14,9 +14,10 @@
|
||||
<surname>Dolstra</surname>
|
||||
</personname>
|
||||
<affiliation>
|
||||
<orgname>Utrecht University</orgname>
|
||||
<orgdiv>Faculty of Science, Department of Information and Computing Sciences</orgdiv>
|
||||
<orgname>Delft University of Technology</orgname>
|
||||
<orgdiv>Department of Software Technology</orgdiv>
|
||||
</affiliation>
|
||||
<contrib>Author</contrib>
|
||||
</author>
|
||||
|
||||
<copyright>
|
||||
@@ -24,10 +25,13 @@
|
||||
<year>2005</year>
|
||||
<year>2006</year>
|
||||
<year>2007</year>
|
||||
<year>2008</year>
|
||||
<year>2009</year>
|
||||
<year>2010</year>
|
||||
<holder>Eelco Dolstra</holder>
|
||||
</copyright>
|
||||
|
||||
<date>September 2007</date>
|
||||
<date>August 2010</date>
|
||||
|
||||
</info>
|
||||
|
||||
@@ -48,72 +52,29 @@
|
||||
|
||||
<section>
|
||||
<title>Main commands</title>
|
||||
<section xml:id="sec-nix-env">
|
||||
<title>nix-env</title>
|
||||
<xi:include href="nix-env.xml" />
|
||||
</section>
|
||||
<section xml:id="sec-nix-instantiate">
|
||||
<title>nix-instantiate</title>
|
||||
<xi:include href="nix-instantiate.xml" />
|
||||
</section>
|
||||
<section xml:id="sec-nix-store">
|
||||
<title>nix-store</title>
|
||||
<xi:include href="nix-store.xml" />
|
||||
</section>
|
||||
<xi:include href="nix-env.xml" />
|
||||
<xi:include href="nix-instantiate.xml" />
|
||||
<xi:include href="nix-store.xml" />
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<title>Utilities</title>
|
||||
<section xml:id="sec-nix-build">
|
||||
<title>nix-build</title>
|
||||
<xi:include href="nix-build.xml" />
|
||||
</section>
|
||||
<section xml:id="sec-nix-channel">
|
||||
<title>nix-channel</title>
|
||||
<xi:include href="nix-channel.xml" />
|
||||
</section>
|
||||
<section xml:id="sec-nix-collect-garbage">
|
||||
<title>nix-collect-garbage</title>
|
||||
<xi:include href="nix-collect-garbage.xml" />
|
||||
</section>
|
||||
<section xml:id="sec-nix-copy-closure">
|
||||
<title>nix-copy-closure</title>
|
||||
<xi:include href="nix-copy-closure.xml" />
|
||||
</section>
|
||||
<section xml:id="sec-nix-hash">
|
||||
<title>nix-hash</title>
|
||||
<xi:include href="nix-hash.xml" />
|
||||
</section>
|
||||
<section xml:id="sec-nix-install-package">
|
||||
<title>nix-install-package</title>
|
||||
<xi:include href="nix-install-package.xml" />
|
||||
</section>
|
||||
<section xml:id="sec-nix-pack-closure">
|
||||
<title>nix-pack-closure</title>
|
||||
<xi:include href="nix-pack-closure.xml" />
|
||||
</section>
|
||||
<section xml:id="sec-nix-prefetch-url">
|
||||
<title>nix-prefetch-url</title>
|
||||
<xi:include href="nix-prefetch-url.xml" />
|
||||
</section>
|
||||
<section xml:id="sec-nix-pull">
|
||||
<title>nix-pull</title>
|
||||
<xi:include href="nix-pull.xml" />
|
||||
</section>
|
||||
<section xml:id="sec-nix-push">
|
||||
<title>nix-push</title>
|
||||
<xi:include href="nix-push.xml" />
|
||||
</section>
|
||||
<section xml:id="sec-nix-unpack-closure">
|
||||
<title>nix-unpack-closure</title>
|
||||
<xi:include href="nix-unpack-closure.xml" />
|
||||
</section>
|
||||
<xi:include href="nix-build.xml" />
|
||||
<xi:include href="nix-channel.xml" />
|
||||
<xi:include href="nix-collect-garbage.xml" />
|
||||
<xi:include href="nix-copy-closure.xml" />
|
||||
<xi:include href="nix-hash.xml" />
|
||||
<xi:include href="nix-install-package.xml" />
|
||||
<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" />
|
||||
</section>
|
||||
|
||||
|
||||
</appendix>
|
||||
|
||||
<xi:include href="troubleshooting.xml" />
|
||||
<xi:include href="bugs.xml" />
|
||||
<!-- <xi:include href="bugs.xml" /> -->
|
||||
<xi:include href="glossary.xml" />
|
||||
|
||||
<appendix>
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
<refentry xmlns="http://docbook.org/ns/docbook"
|
||||
xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||
xmlns:xi="http://www.w3.org/2001/XInclude">
|
||||
xmlns:xi="http://www.w3.org/2001/XInclude"
|
||||
xml:id="sec-nix-build">
|
||||
|
||||
<refmeta>
|
||||
<refentrytitle>nix-build</refentrytitle>
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
<refentry xmlns="http://docbook.org/ns/docbook"
|
||||
xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||
xmlns:xi="http://www.w3.org/2001/XInclude">
|
||||
xmlns:xi="http://www.w3.org/2001/XInclude"
|
||||
xml:id="sec-nix-channel">
|
||||
|
||||
<refmeta>
|
||||
<refentrytitle>nix-channel</refentrytitle>
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
<refentry xmlns="http://docbook.org/ns/docbook"
|
||||
xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||
xmlns:xi="http://www.w3.org/2001/XInclude">
|
||||
xmlns:xi="http://www.w3.org/2001/XInclude"
|
||||
xml:id="sec-nix-collect-garbage">
|
||||
|
||||
<refmeta>
|
||||
<refentrytitle>nix-collect-garbage</refentrytitle>
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
<refentry xmlns="http://docbook.org/ns/docbook"
|
||||
xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||
xmlns:xi="http://www.w3.org/2001/XInclude">
|
||||
xmlns:xi="http://www.w3.org/2001/XInclude"
|
||||
xml:id="sec-nix-copy-closure">
|
||||
|
||||
<refmeta>
|
||||
<refentrytitle>nix-copy-closure</refentrytitle>
|
||||
@@ -128,7 +129,7 @@ those paths. If this bothers you, use
|
||||
<para>Copy Firefox with all its dependencies to a remote machine:
|
||||
|
||||
<screen>
|
||||
$ nix-copy-closure alice@itchy.labs $(type -tP firefox)</screen>
|
||||
$ nix-copy-closure --to alice@itchy.labs $(type -tP firefox)</screen>
|
||||
|
||||
</para>
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
<refentry xmlns="http://docbook.org/ns/docbook"
|
||||
xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||
xmlns:xi="http://www.w3.org/2001/XInclude">
|
||||
xmlns:xi="http://www.w3.org/2001/XInclude"
|
||||
xml:id="sec-nix-env">
|
||||
|
||||
<refmeta>
|
||||
<refentrytitle>nix-env</refentrytitle>
|
||||
@@ -526,7 +527,7 @@ upgrading `mozilla-1.2' to `mozilla-1.4'</screen>
|
||||
|
||||
</refsection>
|
||||
|
||||
<refsection><title>Versions</title>
|
||||
<refsection xml:id="ssec-version-comparisons"><title>Versions</title>
|
||||
|
||||
<para>The upgrade operation determines whether a derivation
|
||||
<varname>y</varname> is an upgrade of a derivation
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
<refentry xmlns="http://docbook.org/ns/docbook"
|
||||
xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||
xmlns:xi="http://www.w3.org/2001/XInclude">
|
||||
xmlns:xi="http://www.w3.org/2001/XInclude"
|
||||
xml:id="sec-nix-hash">
|
||||
|
||||
<refmeta>
|
||||
<refentrytitle>nix-hash</refentrytitle>
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
<refentry xmlns="http://docbook.org/ns/docbook"
|
||||
xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||
xmlns:xi="http://www.w3.org/2001/XInclude">
|
||||
xmlns:xi="http://www.w3.org/2001/XInclude"
|
||||
xml:id="sec-nix-install-package">
|
||||
|
||||
<refmeta>
|
||||
<refentrytitle>nix-install-package</refentrytitle>
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
<refentry xmlns="http://docbook.org/ns/docbook"
|
||||
xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||
xmlns:xi="http://www.w3.org/2001/XInclude">
|
||||
xmlns:xi="http://www.w3.org/2001/XInclude"
|
||||
xml:id="sec-nix-instantiate">
|
||||
|
||||
<refmeta>
|
||||
<refentrytitle>nix-instantiate</refentrytitle>
|
||||
|
||||
@@ -178,100 +178,5 @@
|
||||
</productionset>
|
||||
|
||||
</sect1>
|
||||
|
||||
|
||||
|
||||
<sect1>
|
||||
<title>Semantics</title>
|
||||
|
||||
|
||||
|
||||
<sect2>
|
||||
<title>Built-in functions</title>
|
||||
|
||||
<para>
|
||||
The Nix language provides the following built-in function
|
||||
(<quote>primops</quote>):
|
||||
</para>
|
||||
|
||||
<variablelist>
|
||||
|
||||
<varlistentry>
|
||||
<term><function>import</function>
|
||||
<replaceable>e</replaceable></term>
|
||||
<listitem>
|
||||
<para>
|
||||
Evaluates the expression <replaceable>e</replaceable>,
|
||||
which must yield a path value. The Nix expression
|
||||
stored at this path in the file system is then read,
|
||||
parsed, and evaluated. Returns the result of the
|
||||
evaluation of the Nix expression just read.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
Example: <literal>import ./foo.nix</literal> evaluates
|
||||
the expression stored in <filename>foo.nix</filename>
|
||||
(in the directory containing the expression in which the
|
||||
<function>import</function> occurs).
|
||||
</para>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term><function>derivation</function>
|
||||
<replaceable>e</replaceable></term>
|
||||
<listitem>
|
||||
<para>
|
||||
Evaluates the expression <replaceable>e</replaceable>,
|
||||
which must yield an attribute set. [...]
|
||||
</para>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term><function>baseNameOf</function>
|
||||
<replaceable>e</replaceable></term>
|
||||
<listitem>
|
||||
<para>
|
||||
Evaluates the expression <replaceable>e</replaceable>,
|
||||
which must yield a string value, and returns a string
|
||||
representing its <emphasis>base name</emphasis>. This
|
||||
is the substring following the last path separator
|
||||
(<literal>/</literal>).
|
||||
</para>
|
||||
|
||||
<para>
|
||||
Example: <literal>baseNameOf "/foo/bar"</literal>
|
||||
returns <literal>"bar"</literal>, and
|
||||
<literal>baseNameOf "/foo/bar/"</literal> returns
|
||||
<literal>""</literal>.
|
||||
</para>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term><function>toString</function>
|
||||
<replaceable>e</replaceable></term>
|
||||
<listitem>
|
||||
<para>
|
||||
Evaluates the expression <replaceable>e</replaceable>
|
||||
and coerces it into a string, if possible. Only
|
||||
strings, paths, and URIs can be so coerced.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
Example: <literal>toString
|
||||
http://www.cs.uu.nl/</literal> returns
|
||||
<literal>"http://www.cs.uu.nl/"</literal>.
|
||||
</para>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
|
||||
</variablelist>
|
||||
|
||||
</sect2>
|
||||
|
||||
</sect1>
|
||||
|
||||
|
||||
</appendix>
|
||||
|
||||
@@ -1,82 +0,0 @@
|
||||
<refentry xmlns="http://docbook.org/ns/docbook"
|
||||
xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||
xmlns:xi="http://www.w3.org/2001/XInclude">
|
||||
|
||||
<refmeta>
|
||||
<refentrytitle>nix-pack-closure</refentrytitle>
|
||||
<manvolnum>1</manvolnum>
|
||||
<refmiscinfo class="source">Nix</refmiscinfo>
|
||||
<refmiscinfo class="version"><xi:include href="version.txt" parse="text"/></refmiscinfo>
|
||||
</refmeta>
|
||||
|
||||
<refnamediv>
|
||||
<refname>nix-pack-closure</refname>
|
||||
<refpurpose>pack the closure of a store path into a single file that
|
||||
can be unpacked with
|
||||
<command>nix-unpack-closure</command></refpurpose>
|
||||
</refnamediv>
|
||||
|
||||
<refsynopsisdiv>
|
||||
<cmdsynopsis>
|
||||
<command>nix-pack-closure</command>
|
||||
<arg choice='plain' rep='repeat'><replaceable>paths</replaceable></arg>
|
||||
</cmdsynopsis>
|
||||
</refsynopsisdiv>
|
||||
|
||||
|
||||
<refsection><title>Description</title>
|
||||
|
||||
<para>The command <command>nix-pack-closure</command> packs the
|
||||
contents of the store paths <replaceable>paths</replaceable> and
|
||||
<emphasis>all their dependencies</emphasis> into a single file, which
|
||||
is written to standard output. (That is, it
|
||||
<emphasis>serialises</emphasis> <replaceable>paths</replaceable>.)
|
||||
The output can then be unpacked into the Nix store of another machine
|
||||
using <command>nix-unpack-closure</command>.</para>
|
||||
|
||||
<para>Together, <command>nix-pack-closure</command> and
|
||||
<command>nix-unpack-closure</command> provide a quick and easy way to
|
||||
deploy a package to a different machine. However, as the output of
|
||||
<command>nix-pack-closure</command> tends to be rather large (since it
|
||||
contains all dependencies), it’s not very efficient.
|
||||
<command>nix-push</command> and <command>nix-pull</command> are more
|
||||
efficient, but are also a bit more cumbersome to use.</para>
|
||||
|
||||
</refsection>
|
||||
|
||||
|
||||
<refsection><title>Examples</title>
|
||||
|
||||
<para>To copy some instance of Subversion with all its dependencies to
|
||||
another machine:
|
||||
|
||||
<screen>
|
||||
$ nix-pack-closure /nix/store/hj232g1r...-subversion-1.3.0 > svn.closure
|
||||
|
||||
<lineannotation>Copy <!-- !!! <filename> -->svn.closure to the remote machine, then on the remote machine do:</lineannotation>
|
||||
$ nix-unpack-closure < svn.closure</screen>
|
||||
|
||||
</para>
|
||||
|
||||
<para>Copy the program <command>azureus</command> with all its
|
||||
dependencies to the machine <literal>scratchy</literal>:
|
||||
|
||||
<screen>
|
||||
$ nix-pack-closure $(which azureus) | ssh scratchy nix-unpack-closure</screen>
|
||||
|
||||
</para>
|
||||
|
||||
<para>As a variation on the previous example, copy
|
||||
<command>azureus</command>, and also install it in the user’s profile
|
||||
on the target machine:
|
||||
|
||||
<screen>
|
||||
$ nix-pack-closure $(which azureus) | ssh scratchy 'nix-env -i $(nix-unpack-closure)'</screen>
|
||||
|
||||
</para>
|
||||
|
||||
|
||||
</refsection>
|
||||
|
||||
|
||||
</refentry>
|
||||
@@ -1,6 +1,7 @@
|
||||
<refentry xmlns="http://docbook.org/ns/docbook"
|
||||
xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||
xmlns:xi="http://www.w3.org/2001/XInclude">
|
||||
xmlns:xi="http://www.w3.org/2001/XInclude"
|
||||
xml:id="sec-nix-prefetch-url">
|
||||
|
||||
<refmeta>
|
||||
<refentrytitle>nix-prefetch-url</refentrytitle>
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
<refentry xmlns="http://docbook.org/ns/docbook"
|
||||
xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||
xmlns:xi="http://www.w3.org/2001/XInclude">
|
||||
xmlns:xi="http://www.w3.org/2001/XInclude"
|
||||
xml:id="sec-nix-pull">
|
||||
|
||||
<refmeta>
|
||||
<refentrytitle>nix-pull</refentrytitle>
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
<refentry xmlns="http://docbook.org/ns/docbook"
|
||||
xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||
xmlns:xi="http://www.w3.org/2001/XInclude">
|
||||
xmlns:xi="http://www.w3.org/2001/XInclude"
|
||||
xml:id="sec-nix-push">
|
||||
|
||||
<refmeta>
|
||||
<refentrytitle>nix-push</refentrytitle>
|
||||
@@ -119,7 +120,7 @@ dependencies used in the build, such as compilers).</para>
|
||||
dependencies, we can do:
|
||||
|
||||
<screen>
|
||||
$ nix-push <replaceable>urls</replaceable> $(nix-instantiate $(nix-store -r foo.nix))</screen>
|
||||
$ nix-push <replaceable>urls</replaceable> $(nix-store -r $(nix-instantiate foo.nix))</screen>
|
||||
|
||||
</para>
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
<refentry xmlns="http://docbook.org/ns/docbook"
|
||||
xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||
xmlns:xi="http://www.w3.org/2001/XInclude">
|
||||
xmlns:xi="http://www.w3.org/2001/XInclude"
|
||||
xml:id="sec-nix-store">
|
||||
|
||||
<refmeta>
|
||||
<refentrytitle>nix-store</refentrytitle>
|
||||
@@ -128,6 +129,7 @@ lrwxrwxrwx 1 ... 2005-03-13 21:10 /home/eelco/bla/result -> /nix/store/1r1134
|
||||
<arg choice='plain'><option>-r</option></arg>
|
||||
</group>
|
||||
<arg choice='plain' rep='repeat'><replaceable>paths</replaceable></arg>
|
||||
<arg><option>--dry-run</option></arg>
|
||||
</cmdsynopsis>
|
||||
|
||||
</refsection>
|
||||
@@ -166,6 +168,11 @@ 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>
|
||||
|
||||
</refsection>
|
||||
|
||||
|
||||
@@ -204,6 +211,8 @@ linkend="sec-nix-build"><command>nix-build</command></link> does.</para>
|
||||
<arg choice='plain'><option>--print-dead</option></arg>
|
||||
<arg choice='plain'><option>--delete</option></arg>
|
||||
</group>
|
||||
<arg><option>--max-freed</option> <replaceable>bytes</replaceable></arg>
|
||||
<arg><option>--max-links</option> <replaceable>nrlinks</replaceable></arg>
|
||||
</cmdsynopsis>
|
||||
|
||||
</refsection>
|
||||
@@ -258,7 +267,34 @@ the Nix store not reachable via file system references from a set of
|
||||
|
||||
</variablelist>
|
||||
|
||||
<para>The behaviour of the collector is influenced by the <link
|
||||
<para>By default, all unreachable paths are deleted. The following
|
||||
options control what gets deleted and in what order:
|
||||
|
||||
<variablelist>
|
||||
|
||||
<varlistentry><term><option>--max-freed</option> <replaceable>bytes</replaceable></term>
|
||||
|
||||
<listitem><para>Keep deleting paths until at least
|
||||
<replaceable>bytes</replaceable> bytes have been
|
||||
deleted, then stop.</para></listitem>
|
||||
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry><term><option>--max-links</option> <replaceable>nrlinks</replaceable></term>
|
||||
|
||||
<listitem><para>Keep deleting paths until the hard link count on
|
||||
<filename>/nix/store</filename> is less than
|
||||
<replaceable>nrlinks</replaceable>, then stop. This is useful for
|
||||
very large Nix stores on filesystems with a 32000 subdirectories
|
||||
limit (like <literal>ext3</literal>).</para></listitem>
|
||||
|
||||
</varlistentry>
|
||||
|
||||
</variablelist>
|
||||
|
||||
</para>
|
||||
|
||||
<para>The behaviour of the collector is also influenced by the <link
|
||||
linkend="conf-gc-keep-outputs"><literal>gc-keep-outputs</literal></link>
|
||||
and <link
|
||||
linkend="conf-gc-keep-derivations"><literal>gc-keep-derivations</literal></link>
|
||||
@@ -284,6 +320,13 @@ deleting `/nix/store/kq82idx6g0nyzsp2s14gfsc38npai7lf-cairo-1.0.4.tar.gz.drv'
|
||||
|
||||
</para>
|
||||
|
||||
<para>To delete at least 100 MiBs of unreachable paths:
|
||||
|
||||
<screen>
|
||||
$ nix-store --gc --max-freed $((100 * 1024 * 1024))</screen>
|
||||
|
||||
</para>
|
||||
|
||||
</refsection>
|
||||
|
||||
|
||||
@@ -299,7 +342,7 @@ deleting `/nix/store/kq82idx6g0nyzsp2s14gfsc38npai7lf-cairo-1.0.4.tar.gz.drv'
|
||||
|
||||
<cmdsynopsis>
|
||||
<command>nix-store</command>
|
||||
<arg choice='plain'><option>--gc</option></arg>
|
||||
<arg choice='plain'><option>--delete</option></arg>
|
||||
<arg><option>--ignore-liveness</option></arg>
|
||||
<arg choice='plain' rep='repeat'><replaceable>paths</replaceable></arg>
|
||||
</cmdsynopsis>
|
||||
@@ -361,6 +404,8 @@ error: cannot delete path `/nix/store/zq0h41l75vlb4z45kzgjjmsjxvcv1qk7-mesa-6.4'
|
||||
<arg choice='plain'><option>--tree</option></arg>
|
||||
<arg choice='plain'><option>--binding</option> <replaceable>name</replaceable></arg>
|
||||
<arg choice='plain'><option>--hash</option></arg>
|
||||
<arg choice='plain'><option>--size</option></arg>
|
||||
<arg choice='plain'><option>--roots</option></arg>
|
||||
</group>
|
||||
<arg><option>--use-output</option></arg>
|
||||
<arg><option>-u</option></arg>
|
||||
@@ -543,9 +588,29 @@ query is applied to the target of the symlink.</para>
|
||||
<varlistentry><term><option>--hash</option></term>
|
||||
|
||||
<listitem><para>Prints the SHA-256 hash of the contents of the
|
||||
store path <replaceable>paths</replaceable>. Since the hash is
|
||||
stored in the Nix database, this is a fast
|
||||
operation.</para></listitem>
|
||||
store paths <replaceable>paths</replaceable> (that is, the hash of
|
||||
the output of <command>nix-store --dump</command> on the given
|
||||
paths). Since the hash is stored in the Nix database, this is a
|
||||
fast operation.</para></listitem>
|
||||
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry><term><option>--size</option></term>
|
||||
|
||||
<listitem><para>Prints the size in bytes of the contents of the
|
||||
store paths <replaceable>paths</replaceable> — to be precise, the
|
||||
size of the output of <command>nix-store --dump</command> on the
|
||||
given paths. Note that the actual disk space required by the
|
||||
store paths may be higher, especially on filesystems with large
|
||||
cluster sizes.</para></listitem>
|
||||
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry><term><option>--roots</option></term>
|
||||
|
||||
<listitem><para>Prints the garbage collector roots that point,
|
||||
directly or indirectly, at the store paths
|
||||
<replaceable>paths</replaceable>.</para></listitem>
|
||||
|
||||
</varlistentry>
|
||||
|
||||
@@ -626,6 +691,18 @@ $ gv graph.ps</screen>
|
||||
|
||||
</para>
|
||||
|
||||
<para>Show every garbage collector root that points to a store path
|
||||
that depends on <command>svn</command>:
|
||||
|
||||
<screen>
|
||||
$ nix-store -q --roots $(which svn)
|
||||
/nix/var/nix/profiles/default-81-link
|
||||
/nix/var/nix/profiles/default-82-link
|
||||
/nix/var/nix/profiles/per-user/eelco/profile-97-link
|
||||
</screen>
|
||||
|
||||
</para>
|
||||
|
||||
</refsection>
|
||||
|
||||
|
||||
@@ -950,7 +1027,51 @@ ktorrent-2.2.1/NEWS
|
||||
</refsection>
|
||||
|
||||
|
||||
<!-- TODO: export, import operations -->
|
||||
<!--######################################################################-->
|
||||
|
||||
<refsection><title>Operation <option>--dump-db</option></title>
|
||||
|
||||
<refsection>
|
||||
<title>Synopsis</title>
|
||||
<cmdsynopsis>
|
||||
<command>nix-store</command>
|
||||
<arg choice='plain'><option>--dump-db</option></arg>
|
||||
</cmdsynopsis>
|
||||
</refsection>
|
||||
|
||||
<refsection><title>Description</title>
|
||||
|
||||
<para>The operation <option>--dump-db</option> writes a dump of the
|
||||
Nix database to standard output. It can be loaded into an empty Nix
|
||||
store using <option>--load-db</option>. This is useful for making
|
||||
backups and when migrating to different database schemas.</para>
|
||||
|
||||
</refsection>
|
||||
|
||||
</refsection>
|
||||
|
||||
|
||||
<!--######################################################################-->
|
||||
|
||||
<refsection><title>Operation <option>--dump-db</option></title>
|
||||
|
||||
<refsection>
|
||||
<title>Synopsis</title>
|
||||
<cmdsynopsis>
|
||||
<command>nix-store</command>
|
||||
<arg choice='plain'><option>--load-db</option></arg>
|
||||
</cmdsynopsis>
|
||||
</refsection>
|
||||
|
||||
<refsection><title>Description</title>
|
||||
|
||||
<para>The operation <option>--load-db</option> reads a dump of the Nix
|
||||
database created by <option>--dump-db</option> from standard input and
|
||||
loads it into the Nix database.</para>
|
||||
|
||||
</refsection>
|
||||
|
||||
</refsection>
|
||||
|
||||
|
||||
</refentry>
|
||||
|
||||
@@ -1,42 +0,0 @@
|
||||
<refentry xmlns="http://docbook.org/ns/docbook"
|
||||
xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||
xmlns:xi="http://www.w3.org/2001/XInclude">
|
||||
|
||||
<refmeta>
|
||||
<refentrytitle>nix-unpack-closure</refentrytitle>
|
||||
<manvolnum>1</manvolnum>
|
||||
<refmiscinfo class="source">Nix</refmiscinfo>
|
||||
<refmiscinfo class="version"><xi:include href="version.txt" parse="text"/></refmiscinfo>
|
||||
</refmeta>
|
||||
|
||||
<refnamediv>
|
||||
<refname>nix-unpack-closure</refname>
|
||||
<refpurpose>unpack the closure of a store path created by <command>nix-pack-closure</command> into the Nix store</refpurpose>
|
||||
</refnamediv>
|
||||
|
||||
<refsynopsisdiv>
|
||||
<cmdsynopsis>
|
||||
<command>nix-unpack-closure</command>
|
||||
</cmdsynopsis>
|
||||
</refsynopsisdiv>
|
||||
|
||||
|
||||
<refsection><title>Description</title>
|
||||
|
||||
<para>The command <command>nix-unpack-closure</command> unpacks the
|
||||
closure of a set of store paths created by
|
||||
<command>nix-pack-closure</command> into the local Nix store. The
|
||||
closure is a single file read from standard input. See the
|
||||
description of <command>nix-pack-closure</command> for details and
|
||||
examples.</para>
|
||||
|
||||
<para>The top-level paths in the closure (i.e., the paths passed to
|
||||
the original <command>nix-pack-closure</command> call that created the
|
||||
closure) are printed on standard output. These paths can be passed,
|
||||
for instance, to <literal>nix-env -i</literal> to install them into a
|
||||
user environment on the target machine.</para>
|
||||
|
||||
</refsection>
|
||||
|
||||
|
||||
</refentry>
|
||||
35
doc/manual/nix-worker.xml
Normal file
35
doc/manual/nix-worker.xml
Normal file
@@ -0,0 +1,35 @@
|
||||
<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">
|
||||
|
||||
<refmeta>
|
||||
<refentrytitle>nix-worker</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>
|
||||
<refpurpose>Nix multi-user support daemon</refpurpose>
|
||||
</refnamediv>
|
||||
|
||||
<refsynopsisdiv>
|
||||
<cmdsynopsis>
|
||||
<command>nix-worker</command>
|
||||
<arg choice="plain"><option>--daemon</option></arg>
|
||||
</cmdsynopsis>
|
||||
</refsynopsisdiv>
|
||||
|
||||
|
||||
<refsection><title>Description</title>
|
||||
|
||||
<para>The Nix daemon is necessary in multi-user Nix installations. It
|
||||
performs build actions and other operations on the Nix store on behalf
|
||||
of unprivileged users.</para>
|
||||
|
||||
|
||||
</refsection>
|
||||
|
||||
</refentry>
|
||||
@@ -13,6 +13,10 @@
|
||||
</group>
|
||||
<replaceable>number</replaceable>
|
||||
</arg>
|
||||
<arg>
|
||||
<arg><option>--cores</option></arg>
|
||||
<replaceable>number</replaceable>
|
||||
</arg>
|
||||
<arg>
|
||||
<arg><option>--max-silent-time</option></arg>
|
||||
<replaceable>number</replaceable>
|
||||
@@ -24,6 +28,7 @@
|
||||
<arg><option>--fallback</option></arg>
|
||||
<arg><option>--readonly-mode</option></arg>
|
||||
<arg><option>--log-type</option> <replaceable>type</replaceable></arg>
|
||||
<arg><option>--show-trace</option></arg>
|
||||
<sbr />
|
||||
|
||||
</nop>
|
||||
|
||||
@@ -98,7 +98,25 @@
|
||||
linkend='conf-build-max-jobs'><literal>build-max-jobs</literal></link>
|
||||
configuration setting, which itself defaults to
|
||||
<literal>1</literal>. A higher value is useful on SMP systems or to
|
||||
exploit I/O latency. </para></listitem>
|
||||
exploit I/O latency.</para></listitem>
|
||||
|
||||
</varlistentry>
|
||||
|
||||
|
||||
<varlistentry xml:id="opt-cores"><term><option>--cores</option></term>
|
||||
|
||||
<listitem><para>Sets the value of the <envar>NIX_BUILD_CORES</envar>
|
||||
environment variable in the invocation of builders. Builders can
|
||||
use this variable at their discretion to control the maximum amount
|
||||
of parallelism. For instance, in Nixpkgs, if the derivation
|
||||
attribute <varname>enableParallelBuilding</varname> is set to
|
||||
<literal>true</literal>, the builder passes the
|
||||
<option>-j<replaceable>N</replaceable></option> flag to GNU Make.
|
||||
It defaults to the value of the <link
|
||||
linkend='conf-build-cores'><literal>build-cores</literal></link>
|
||||
configuration setting, if set, or <literal>1</literal> otherwise.
|
||||
The value <literal>0</literal> means that the builder should use all
|
||||
available CPU cores in the system.</para></listitem>
|
||||
|
||||
</varlistentry>
|
||||
|
||||
@@ -251,14 +269,14 @@
|
||||
|
||||
<programlisting>
|
||||
{ # The system (e.g., `i686-linux') for which to build the packages.
|
||||
system ? __currentSystem
|
||||
system ? builtins.currentSystem
|
||||
<replaceable>...</replaceable>
|
||||
}: <replaceable>...</replaceable></programlisting>
|
||||
|
||||
So if you call this Nix expression (e.g., when you do
|
||||
<literal>nix-env -i <replaceable>pkgname</replaceable></literal>),
|
||||
the function will be called automatically using the value <link
|
||||
linkend='builtin-currentSystem'><literal>__currentSystem</literal></link>
|
||||
linkend='builtin-currentSystem'><literal>builtins.currentSystem</literal></link>
|
||||
for the <literal>system</literal> argument. You can override this
|
||||
using <option>--arg</option>, e.g., <literal>nix-env -i
|
||||
<replaceable>pkgname</replaceable> --arg system
|
||||
@@ -305,6 +323,14 @@
|
||||
</varlistentry>
|
||||
|
||||
|
||||
<varlistentry><term><option>--show-trace</option></term>
|
||||
|
||||
<listitem><para>Causes Nix to print out a stack trace in case of Nix
|
||||
expression evaluation errors.</para></listitem>
|
||||
|
||||
</varlistentry>
|
||||
|
||||
|
||||
</variablelist>
|
||||
|
||||
|
||||
|
||||
@@ -38,7 +38,7 @@ to end-user applications like Mozilla Firefox. (Nix is however not
|
||||
tied to the Nix Package collection; you could write your own Nix
|
||||
expressions based on it, or completely new ones.) You can download
|
||||
the latest version from <link
|
||||
xlink:href='http://nix.cs.uu.nl/dist/nix' />.</para>
|
||||
xlink:href='http://nixos.org/nixpkgs/download.html' />.</para>
|
||||
|
||||
<para>Assuming that you have downloaded and unpacked a release of Nix
|
||||
Packages, you can view the set of available packages in the release:
|
||||
@@ -118,15 +118,15 @@ available somewhere. This is done using the
|
||||
containing a <emphasis>manifest</emphasis> describing what binaries
|
||||
are available. This URL should correspond to the Nix Packages release
|
||||
that you’re using. For instance, if you obtained a release from <link
|
||||
xlink:href='http://nix.cs.uu.nl/dist/nix/nixpkgs-0.6pre1554/' />, then
|
||||
you should do:
|
||||
xlink:href='http://nixos.org/releases/nixpkgs/nixpkgs-0.12pre11712-4lrp7j8x'
|
||||
/>, then you should do:
|
||||
|
||||
<screen>
|
||||
$ nix-pull http://nix.cs.uu.nl/dist/nix/nixpkgs-0.6pre1554/MANIFEST</screen>
|
||||
$ nix-pull http://nixos.org/releases/nixpkgs/nixpkgs-0.12pre11712-4lrp7j8x/MANIFEST</screen>
|
||||
|
||||
If you then issue the installation command, it should start
|
||||
downloading binaries from <systemitem
|
||||
class='fqdomainname'>nix.cs.uu.nl</systemitem>, instead of building
|
||||
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>
|
||||
@@ -331,7 +331,7 @@ default profile, respectively. If the profile doesn’t exist, it will
|
||||
be created automatically. You should be careful about storing a
|
||||
profile in another location than the <filename>profiles</filename>
|
||||
directory, since otherwise it might not be used as a root of the
|
||||
garbage collector (see section <xref linkend='sec-garbage-collection'
|
||||
garbage collector (see <xref linkend='sec-garbage-collection'
|
||||
/>).</para>
|
||||
|
||||
<para>All <command>nix-env</command> operations work on the profile
|
||||
@@ -458,7 +458,7 @@ URL.</para>
|
||||
<command>nix-channel --add</command>, e.g.,
|
||||
|
||||
<screen>
|
||||
$ nix-channel --add http://nix.cs.uu.nl/dist/nix/channels-v3/nixpkgs-unstable</screen>
|
||||
$ nix-channel --add http://nixos.org/releases/nixpkgs/channels/nixpkgs-unstable</screen>
|
||||
|
||||
subscribes you to a channel that always contains that latest version
|
||||
of the Nix Packages collection. (Instead of
|
||||
@@ -496,31 +496,27 @@ available in the subscribed channels.</para>
|
||||
|
||||
<para>Often, when you want to install a specific package (e.g., from
|
||||
the <link
|
||||
xlink:href="http://nix.cs.uu.nl/dist/nix/nixpkgs-unstable-latest/">Nix
|
||||
Packages collection</link> or from our <link
|
||||
xlink:href='http://nix.cs.uu.nl/dist/'>release server</link>),
|
||||
subscribing to a channel is a bit cumbersome. And channels don’t help
|
||||
you at all if you want to install an older version of a package than
|
||||
the one provided by the current contents of the channel, or a package
|
||||
that has been removed from the channel. That’s when
|
||||
<emphasis>one-click installs</emphasis> come in handy: you can just go
|
||||
to the web page that contains the package, click on it, and it will be
|
||||
installed with all the necessary dependencies.</para>
|
||||
xlink:href="http://nixos.org/nixpkgs/">Nix
|
||||
Packages collection</link>), subscribing to a channel is a bit
|
||||
cumbersome. And channels don’t help you at all if you want to install
|
||||
an older version of a package than the one provided by the current
|
||||
contents of the channel, or a package that has been removed from the
|
||||
channel. That’s when <emphasis>one-click installs</emphasis> come in
|
||||
handy: you can just go to the web page that contains the package,
|
||||
click on it, and it will be installed with all the necessary
|
||||
dependencies.</para>
|
||||
|
||||
<para>For instance, you can go to <link
|
||||
xlink:href="http://nix.cs.uu.nl/dist/nix/nixpkgs-unstable-latest/" />
|
||||
— or to any older release of Nix Packages — and click on any link for
|
||||
the individual packages for your platform (say, <link
|
||||
xlink:href='http://nix.cs.uu.nl/dist/nix/nixpkgs-0.10pre6622/pkgs/subversion-1.4.0-i686-linux.nixpkg'><literal>subversion-1.4.0</literal>
|
||||
for <literal>i686-linux</literal></link>). The first time you do
|
||||
this, your browser will ask what to do with
|
||||
<literal>application/nix-package</literal> files. You should open
|
||||
them with <filename>/nix/bin/nix-install-package</filename>. This
|
||||
will open a window that asks you to confirm that you want to install
|
||||
the package. When you answer <literal>Y</literal>, the package and
|
||||
all its dependencies will be installed. This is a binary deployment
|
||||
mechanism — you get packages pre-compiled for the selected platform
|
||||
type.</para>
|
||||
xlink:href="http://hydra.nixos.org/jobset/nixpkgs/trunk/channel/latest"
|
||||
/> and click on any link for the individual packages for your
|
||||
platform. The first time you do this, your browser will ask what to
|
||||
do with <literal>application/nix-package</literal> files. You should
|
||||
open them with <filename>/nix/bin/nix-install-package</filename>.
|
||||
This will open a window that asks you to confirm that you want to
|
||||
install the package. When you answer <literal>Y</literal>, the
|
||||
package and all its dependencies will be installed. This is a binary
|
||||
deployment mechanism — you get packages pre-compiled for the selected
|
||||
platform type.</para>
|
||||
|
||||
<para>You can also install <literal>application/nix-package</literal>
|
||||
files from the command line directly. See <xref
|
||||
@@ -529,4 +525,82 @@ linkend='sec-nix-install-package' /> for details.</para>
|
||||
</section>
|
||||
|
||||
|
||||
<section xml:id="sec-sharing-packages"><title>Sharing packages between machines</title>
|
||||
|
||||
<para>Sometimes you want to copy a package from one machine to
|
||||
another. Or, you want to install some packages and you know that
|
||||
another machine already has some or all of those packages or their
|
||||
dependencies. In that case there are mechanisms to quickly copy
|
||||
packages between machines.</para>
|
||||
|
||||
<para>The command <command
|
||||
linkend="sec-nix-copy-closure">nix-copy-closure</command> copies a Nix
|
||||
store path along with all its dependencies to or from another machine
|
||||
via the SSH protocol. It doesn’t copy store paths that are already
|
||||
present on the target machine. For example, the following command
|
||||
copies Firefox with all its dependencies:
|
||||
|
||||
<screen>
|
||||
$ nix-copy-closure --to alice@itchy.example.org $(type -p firefox)</screen>
|
||||
|
||||
See <xref linkend='sec-nix-copy-closure' /> for details.</para>
|
||||
|
||||
<para>With <command linkend='refsec-nix-store-export'>nix-store
|
||||
--export</command> and <command
|
||||
linkend='refsec-nix-store-import'>nix-store --import</command> you can
|
||||
write the closure of a store path (that is, the path and all its
|
||||
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>
|
||||
|
||||
writes the closure of Firefox to a file. You can then copy this file
|
||||
to another machine and install the closure:
|
||||
|
||||
<screen>
|
||||
$ nix-store --import < firefox.closure</screen>
|
||||
|
||||
Any store paths in the closure that are already present in the target
|
||||
store are ignored. It is also possible to pipe the export into
|
||||
another command, e.g. to copy and install a closure directly to/on
|
||||
another machine:
|
||||
|
||||
<screen>
|
||||
$ nix-store --export $(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
|
||||
efficient in this example because it only copies paths that are not
|
||||
already present in the target Nix store.</para>
|
||||
|
||||
<para>Finally, if you can mount the Nix store of a remote machine in
|
||||
your local filesystem, Nix can copy paths from the remote Nix store to
|
||||
the local Nix store <emphasis>on demand</emphasis>. For instance,
|
||||
suppose that you mount a remote machine containing a Nix store via
|
||||
<command
|
||||
xlink:href="http://fuse.sourceforge.net/sshfs.html">sshfs</command>:
|
||||
|
||||
<screen>
|
||||
$ sshfs alice@itchy.example.org:/ /mnt</screen>
|
||||
|
||||
You should then set the <envar>NIX_OTHER_STORES</envar> environment
|
||||
variable to tell Nix about this remote Nix store:
|
||||
|
||||
<screen>
|
||||
$ export NIX_OTHER_STORES=/mnt/nix</screen>
|
||||
|
||||
Then if you do any Nix operation, e.g.
|
||||
|
||||
<screen>
|
||||
$ 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>
|
||||
|
||||
|
||||
</chapter>
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
<chapter xmlns="http://docbook.org/ns/docbook"
|
||||
xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||
xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||
xml:id="chap-quick-start">
|
||||
|
||||
<title>Quick Start</title>
|
||||
|
||||
@@ -10,9 +11,9 @@ to the following chapters.</para>
|
||||
|
||||
<orderedlist>
|
||||
|
||||
<listitem><para>Download a source tarball or RPM from <link
|
||||
xlink:href='http://nix.cs.uu.nl/'/>. Build source
|
||||
distributions using the regular sequence:
|
||||
<listitem><para>Download a source tarball, RPM or Deb from <link
|
||||
xlink:href='http://nixos.org/'/>. Build source distributions using
|
||||
the regular sequence:
|
||||
|
||||
<screen>
|
||||
$ tar xvfj nix-<replaceable>version</replaceable>.tar.bz2
|
||||
@@ -20,20 +21,28 @@ $ ./configure
|
||||
$ make
|
||||
$ make install <lineannotation>(as root)</lineannotation></screen>
|
||||
|
||||
This will install Nix in <filename>/nix</filename>. You shouldn't
|
||||
change the prefix if at all possible since that will make it
|
||||
impossible to use pre-built binaries from the Nixpkgs channel and
|
||||
other channels. Alternatively, you could grab an RPM if you're on an
|
||||
RPM-based system. You should also add
|
||||
<filename>/nix/etc/profile.d/nix.sh</filename> to your
|
||||
<filename>~/.bashrc</filename> (or some other login
|
||||
This will install the Nix binaries in <filename>/usr/local</filename>
|
||||
and keep the Nix store and other state in <filename>/nix</filename>.
|
||||
You can change the former by specifying
|
||||
<option>--prefix=<replaceable>path</replaceable></option>. The
|
||||
location of the store can be changed using
|
||||
<option>--with-store-dir=<replaceable>path</replaceable></option>.
|
||||
However, you shouldn't change the store location, if at all possible,
|
||||
since that will make it impossible to use pre-built binaries from the
|
||||
Nixpkgs channel and other channels. The location of the state can be
|
||||
changed using
|
||||
<option>--localstatedir=<replaceable>path</replaceable>.</option></para></listitem>
|
||||
|
||||
<listitem><para>You should add
|
||||
<filename><replaceable>prefix</replaceable>/etc/profile.d/nix.sh</filename>
|
||||
to your <filename>~/.bashrc</filename> (or some other login
|
||||
file).</para></listitem>
|
||||
|
||||
<listitem><para>Subscribe to the Nix Packages channel.
|
||||
|
||||
<screen>
|
||||
$ nix-channel --add \
|
||||
http://nix.cs.uu.nl/dist/nix/channels-v3/nixpkgs-unstable</screen>
|
||||
http://nixos.org/releases/nixpkgs/channels/nixpkgs-unstable</screen>
|
||||
|
||||
</para></listitem>
|
||||
|
||||
@@ -51,7 +60,7 @@ available remotely.</para></listitem>
|
||||
in the channel:
|
||||
|
||||
<screen>
|
||||
$ nix-env -qa ’*’ <lineannotation>(mind the quotes!)</lineannotation>
|
||||
$ nix-env -qa \*
|
||||
docbook-xml-4.2
|
||||
firefox-1.0pre-PR-0.10.1
|
||||
hello-2.1.1
|
||||
@@ -99,7 +108,7 @@ numbers).</para></listitem>
|
||||
|
||||
<listitem><para>You can also install specific packages directly from
|
||||
your web browser. For instance, you can go to <link
|
||||
xlink:href="http://nix.cs.uu.nl/dist/nix/nixpkgs-unstable-latest/" />
|
||||
xlink:href="http://hydra.nixos.org/jobset/nixpkgs/trunk/channel/latest" />
|
||||
and click on any link for the individual packages for your platform.
|
||||
Associate <literal>application/nix-package</literal> with the program
|
||||
<filename>/nix/bin/nix-install-package</filename>. A window should
|
||||
|
||||
@@ -6,6 +6,415 @@
|
||||
|
||||
|
||||
|
||||
<!--==================================================================-->
|
||||
|
||||
<section xml:id="ssec-relnotes-1.0"><title>Release 1.0 (TBA)</title>
|
||||
|
||||
<para>This release has the following improvements:</para>
|
||||
|
||||
<itemizedlist>
|
||||
|
||||
<listitem>
|
||||
<para>Nix can now optionally use the Boehm garbage collector.
|
||||
This significantly reduces the Nix evaluator’s memory footprint,
|
||||
especially when evaluating large NixOS system configurations. It
|
||||
can be enabled using the <option>--enable-gc</option> configure
|
||||
option.</para>
|
||||
</listitem>
|
||||
|
||||
</itemizedlist>
|
||||
|
||||
</section>
|
||||
|
||||
|
||||
<!--==================================================================-->
|
||||
|
||||
<section xml:id="ssec-relnotes-0.16"><title>Release 0.16 (August 17, 2010)</title>
|
||||
|
||||
<para>This release has the following improvements:</para>
|
||||
|
||||
<itemizedlist>
|
||||
|
||||
<listitem>
|
||||
<para>The Nix expression evaluator is now much faster in most
|
||||
cases: typically, <link
|
||||
xlink:href="http://www.mail-archive.com/nix-dev@cs.uu.nl/msg04113.html">3
|
||||
to 8 times compared to the old implementation</link>. It also
|
||||
uses less memory. It no longer depends on the ATerm
|
||||
library.</para>
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
<para>
|
||||
Support for configurable parallelism inside builders. Build
|
||||
scripts have always had the ability to perform multiple build
|
||||
actions in parallel (for instance, by running <command>make -j
|
||||
2</command>), but this was not desirable because the number of
|
||||
actions to be performed in parallel was not configurable. Nix
|
||||
now has an option <option>--cores
|
||||
<replaceable>N</replaceable></option> as well as a configuration
|
||||
setting <varname>build-cores =
|
||||
<replaceable>N</replaceable></varname> that causes the
|
||||
environment variable <envar>NIX_BUILD_CORES</envar> to be set to
|
||||
<replaceable>N</replaceable> when the builder is invoked. The
|
||||
builder can use this at its discretion to perform a parallel
|
||||
build, e.g., by calling <command>make -j
|
||||
<replaceable>N</replaceable></command>. In Nixpkgs, this can be
|
||||
enabled on a per-package basis by setting the derivation
|
||||
attribute <varname>enableParallelBuilding</varname> to
|
||||
<literal>true</literal>.
|
||||
</para>
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
<para><command>nix-store -q</command> now supports XML output
|
||||
through the <option>--xml</option> flag.</para>
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
<para>Several bug fixes.</para>
|
||||
</listitem>
|
||||
|
||||
</itemizedlist>
|
||||
|
||||
</section>
|
||||
|
||||
|
||||
<!--==================================================================-->
|
||||
|
||||
<section xml:id="ssec-relnotes-0.15"><title>Release 0.15 (March 17, 2010)</title>
|
||||
|
||||
<para>This is a bug-fix release. Among other things, it fixes
|
||||
building on Mac OS X (Snow Leopard), and improves the contents of
|
||||
<filename>/etc/passwd</filename> and <filename>/etc/group</filename>
|
||||
in <literal>chroot</literal> builds.</para>
|
||||
|
||||
</section>
|
||||
|
||||
|
||||
<!--==================================================================-->
|
||||
|
||||
<section xml:id="ssec-relnotes-0.14"><title>Release 0.14 (February 4, 2010)</title>
|
||||
|
||||
<para>This release has the following improvements:</para>
|
||||
|
||||
<itemizedlist>
|
||||
|
||||
<listitem>
|
||||
<para>The garbage collector now starts deleting garbage much
|
||||
faster than before. It no longer determines liveness of all paths
|
||||
in the store, but does so on demand.</para>
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
<para>Added a new operation, <command>nix-store --query
|
||||
--roots</command>, that shows the garbage collector roots that
|
||||
directly or indirectly point to the given store paths.</para>
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
<para>Removed support for converting Berkeley DB-based Nix
|
||||
databases to the new schema.</para>
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
<para>Removed the <option>--use-atime</option> and
|
||||
<option>--max-atime</option> garbage collector options. They were
|
||||
not very useful in practice.</para>
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
<para>On Windows, Nix now requires Cygwin 1.7.x.</para>
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
<para>A few bug fixes.</para>
|
||||
</listitem>
|
||||
|
||||
</itemizedlist>
|
||||
|
||||
</section>
|
||||
|
||||
|
||||
<!--==================================================================-->
|
||||
|
||||
<section xml:id="ssec-relnotes-0.13"><title>Release 0.13 (November 5,
|
||||
2009)</title>
|
||||
|
||||
<para>This is primarily a bug fix release. It has some new
|
||||
features:</para>
|
||||
|
||||
<itemizedlist>
|
||||
|
||||
<listitem>
|
||||
<para>Syntactic sugar for writing nested attribute sets. Instead of
|
||||
|
||||
<programlisting>
|
||||
{
|
||||
foo = {
|
||||
bar = 123;
|
||||
xyzzy = true;
|
||||
};
|
||||
a = { b = { c = "d"; }; };
|
||||
}
|
||||
</programlisting>
|
||||
|
||||
you can write
|
||||
|
||||
<programlisting>
|
||||
{
|
||||
foo.bar = 123;
|
||||
foo.xyzzy = true;
|
||||
a.b.c = "d";
|
||||
}
|
||||
</programlisting>
|
||||
|
||||
This is useful, for instance, in NixOS configuration files.</para>
|
||||
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
<para>Support for Nix channels generated by Hydra, the Nix-based
|
||||
continuous build system. (Hydra generates NAR archives on the
|
||||
fly, so the size and hash of these archives isn’t known in
|
||||
advance.)</para>
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
<para>Support <literal>i686-linux</literal> builds directly on
|
||||
<literal>x86_64-linux</literal> Nix installations. This is
|
||||
implemented using the <function>personality()</function> syscall,
|
||||
which causes <command>uname</command> to return
|
||||
<literal>i686</literal> in child processes.</para>
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
<para>Various improvements to the <literal>chroot</literal>
|
||||
support. Building in a <literal>chroot</literal> works quite well
|
||||
now.</para>
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
<para>Nix no longer blocks if it tries to build a path and another
|
||||
process is already building the same path. Instead it tries to
|
||||
build another buildable path first. This improves
|
||||
parallelism.</para>
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
<para>Support for large (> 4 GiB) files in NAR archives.</para>
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
<para>Various (performance) improvements to the remote build
|
||||
mechanism.</para>
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
<para>New primops: <varname>builtins.addErrorContext</varname> (to
|
||||
add a string to stack traces — useful for debugging),
|
||||
<varname>builtins.isBool</varname>,
|
||||
<varname>builtins.isString</varname>,
|
||||
<varname>builtins.isInt</varname>,
|
||||
<varname>builtins.intersectAttrs</varname>.</para>
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
<para>OpenSolaris support (Sander van der Burg).</para>
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
<para>Stack traces are no longer displayed unless the
|
||||
<option>--show-trace</option> option is used.</para>
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
<para>The scoping rules for <literal>inherit
|
||||
(<replaceable>e</replaceable>) ...</literal> in recursive
|
||||
attribute sets have changed. The expression
|
||||
<replaceable>e</replaceable> can now refer to the attributes
|
||||
defined in the containing set.</para>
|
||||
</listitem>
|
||||
|
||||
</itemizedlist>
|
||||
|
||||
</section>
|
||||
|
||||
|
||||
<!--==================================================================-->
|
||||
|
||||
<section xml:id="ssec-relnotes-0.12"><title>Release 0.12 (November 20,
|
||||
2008)</title>
|
||||
|
||||
<itemizedlist>
|
||||
|
||||
<listitem>
|
||||
<para>Nix no longer uses Berkeley DB to store Nix store metadata.
|
||||
The principal advantages of the new storage scheme are: it works
|
||||
properly over decent implementations of NFS (allowing Nix stores
|
||||
to be shared between multiple machines); no recovery is needed
|
||||
when a Nix process crashes; no write access is needed for
|
||||
read-only operations; no more running out of Berkeley DB locks on
|
||||
certain operations.</para>
|
||||
|
||||
<para>You still need to compile Nix with Berkeley DB support if
|
||||
you want Nix to automatically convert your old Nix store to the
|
||||
new schema. If you don’t need this, you can build Nix with the
|
||||
<filename>configure</filename> option
|
||||
<option>--disable-old-db-compat</option>.</para>
|
||||
|
||||
<para>After the automatic conversion to the new schema, you can
|
||||
delete the old Berkeley DB files:
|
||||
|
||||
<screen>
|
||||
$ cd /nix/var/nix/db
|
||||
$ rm __db* log.* derivers references referrers reserved validpaths DB_CONFIG</screen>
|
||||
|
||||
The new metadata is stored in the directories
|
||||
<filename>/nix/var/nix/db/info</filename> and
|
||||
<filename>/nix/var/nix/db/referrer</filename>. Though the
|
||||
metadata is stored in human-readable plain-text files, they are
|
||||
not intended to be human-editable, as Nix is rather strict about
|
||||
the format.</para>
|
||||
|
||||
<para>The new storage schema may or may not require less disk
|
||||
space than the Berkeley DB environment, mostly depending on the
|
||||
cluster size of your file system. With 1 KiB clusters (which
|
||||
seems to be the <literal>ext3</literal> default nowadays) it
|
||||
usually takes up much less space.</para>
|
||||
</listitem>
|
||||
|
||||
<listitem><para>There is a new substituter that copies paths
|
||||
directly from other (remote) Nix stores mounted somewhere in the
|
||||
filesystem. For instance, you can speed up an installation by
|
||||
mounting some remote Nix store that already has the packages in
|
||||
question via NFS or <literal>sshfs</literal>. The environment
|
||||
variable <envar>NIX_OTHER_STORES</envar> specifies the locations of
|
||||
the remote Nix directories,
|
||||
e.g. <literal>/mnt/remote-fs/nix</literal>.</para></listitem>
|
||||
|
||||
<listitem><para>New <command>nix-store</command> operations
|
||||
<option>--dump-db</option> and <option>--load-db</option> to dump
|
||||
and reload the Nix database.</para></listitem>
|
||||
|
||||
<listitem><para>The garbage collector has a number of new options to
|
||||
allow only some of the garbage to be deleted. The option
|
||||
<option>--max-freed <replaceable>N</replaceable></option> tells the
|
||||
collector to stop after at least <replaceable>N</replaceable> bytes
|
||||
have been deleted. The option <option>--max-links
|
||||
<replaceable>N</replaceable></option> tells it to stop after the
|
||||
link count on <filename>/nix/store</filename> has dropped below
|
||||
<replaceable>N</replaceable>. This is useful for very large Nix
|
||||
stores on filesystems with a 32000 subdirectories limit (like
|
||||
<literal>ext3</literal>). The option <option>--use-atime</option>
|
||||
causes store paths to be deleted in order of ascending last access
|
||||
time. This allows non-recently used stuff to be deleted. The
|
||||
option <option>--max-atime <replaceable>time</replaceable></option>
|
||||
specifies an upper limit to the last accessed time of paths that may
|
||||
be deleted. For instance,
|
||||
|
||||
<screen>
|
||||
$ nix-store --gc -v --max-atime $(date +%s -d "2 months ago")</screen>
|
||||
|
||||
deletes everything that hasn’t been accessed in two months.</para></listitem>
|
||||
|
||||
<listitem><para><command>nix-env</command> now uses optimistic
|
||||
profile locking when performing an operation like installing or
|
||||
upgrading, instead of setting an exclusive lock on the profile.
|
||||
This allows multiple <command>nix-env -i / -u / -e</command>
|
||||
operations on the same profile in parallel. If a
|
||||
<command>nix-env</command> operation sees at the end that the profile
|
||||
was changed in the meantime by another process, it will just
|
||||
restart. This is generally cheap because the build results are
|
||||
still in the Nix store.</para></listitem>
|
||||
|
||||
<listitem><para>The option <option>--dry-run</option> is now
|
||||
supported by <command>nix-store -r</command> and
|
||||
<command>nix-build</command>.</para></listitem>
|
||||
|
||||
<listitem><para>The information previously shown by
|
||||
<option>--dry-run</option> (i.e., which derivations will be built
|
||||
and which paths will be substituted) is now always shown by
|
||||
<command>nix-env</command>, <command>nix-store -r</command> and
|
||||
<command>nix-build</command>. The total download size of
|
||||
substitutable paths is now also shown. For instance, a build will
|
||||
show something like
|
||||
|
||||
<screen>
|
||||
the following derivations will be built:
|
||||
/nix/store/129sbxnk5n466zg6r1qmq1xjv9zymyy7-activate-configuration.sh.drv
|
||||
/nix/store/7mzy971rdm8l566ch8hgxaf89x7lr7ik-upstart-jobs.drv
|
||||
...
|
||||
the following paths will be downloaded/copied (30.02 MiB):
|
||||
/nix/store/4m8pvgy2dcjgppf5b4cj5l6wyshjhalj-samba-3.2.4
|
||||
/nix/store/7h1kwcj29ip8vk26rhmx6bfjraxp0g4l-libunwind-0.98.6
|
||||
...</screen>
|
||||
|
||||
</para></listitem>
|
||||
|
||||
<listitem><para>Language features:
|
||||
|
||||
<itemizedlist>
|
||||
|
||||
<listitem><para>@-patterns as in Haskell. For instance, in a
|
||||
function definition
|
||||
|
||||
<programlisting>f = args @ {x, y, z}: <replaceable>...</replaceable>;</programlisting>
|
||||
|
||||
<varname>args</varname> refers to the argument as a whole, which
|
||||
is further pattern-matched against the attribute set pattern
|
||||
<literal>{x, y, z}</literal>.</para></listitem>
|
||||
|
||||
<listitem><para>“<literal>...</literal>” (ellipsis) patterns.
|
||||
An attribute set pattern can now say <literal>...</literal> at
|
||||
the end of the attribute name list to specify that the function
|
||||
takes <emphasis>at least</emphasis> the listed attributes, while
|
||||
ignoring additional attributes. For instance,
|
||||
|
||||
<programlisting>{stdenv, fetchurl, fuse, ...}: <replaceable>...</replaceable></programlisting>
|
||||
|
||||
defines a function that accepts any attribute set that includes
|
||||
at least the three listed attributes.</para></listitem>
|
||||
|
||||
<listitem><para>New primops:
|
||||
<varname>builtins.parseDrvName</varname> (split a package name
|
||||
string like <literal>"nix-0.12pre12876"</literal> into its name
|
||||
and version components, e.g. <literal>"nix"</literal> and
|
||||
<literal>"0.12pre12876"</literal>),
|
||||
<varname>builtins.compareVersions</varname> (compare two version
|
||||
strings using the same algorithm that <command>nix-env</command>
|
||||
uses), <varname>builtins.length</varname> (efficiently compute
|
||||
the length of a list), <varname>builtins.mul</varname> (integer
|
||||
multiplication), <varname>builtins.div</varname> (integer
|
||||
division).
|
||||
<!-- <varname>builtins.genericClosure</varname> -->
|
||||
</para></listitem>
|
||||
|
||||
</itemizedlist>
|
||||
|
||||
</para></listitem>
|
||||
|
||||
<listitem><para><command>nix-prefetch-url</command> now supports
|
||||
<literal>mirror://</literal> URLs, provided that the environment
|
||||
variable <envar>NIXPKGS_ALL</envar> points at a Nixpkgs
|
||||
tree.</para></listitem>
|
||||
|
||||
<listitem><para>Removed the commands
|
||||
<command>nix-pack-closure</command> and
|
||||
<command>nix-unpack-closure</command>. You can do almost the same
|
||||
thing but much more efficiently by doing <literal>nix-store --export
|
||||
$(nix-store -qR <replaceable>paths</replaceable>) > closure</literal> and
|
||||
<literal>nix-store --import <
|
||||
closure</literal>.</para></listitem>
|
||||
|
||||
<listitem><para>Lots of bug fixes, including a big performance bug in
|
||||
the handling of <literal>with</literal>-expressions.</para></listitem>
|
||||
|
||||
</itemizedlist>
|
||||
|
||||
</section>
|
||||
|
||||
|
||||
<!--==================================================================-->
|
||||
|
||||
<section xml:id="ssec-relnotes-0.11"><title>Release 0.11 (December 31,
|
||||
|
||||
@@ -5,121 +5,10 @@
|
||||
|
||||
|
||||
<para>This section provides solutions for some common problems. See
|
||||
the <link xlink:href="https://bugs.cs.uu.nl/browse/NIX">Nix
|
||||
the <link xlink:href="http://bugs.strategoxt.org/browse/NIX">Nix
|
||||
bug tracker</link> for a list of currently known issues.</para>
|
||||
|
||||
|
||||
<section><title>Berkeley DB: <quote>Cannot allocate memory</quote></title>
|
||||
|
||||
<para>Symptom: Nix operations (in particular the
|
||||
<command>nix-store</command> operations <option>--gc</option>,
|
||||
<option>--verify</option>, and <option>--clear-substitutes</option> —
|
||||
the latter being called by <command>nix-channel --update</command>)
|
||||
failing:
|
||||
|
||||
<screen>
|
||||
$ nix-store --verify
|
||||
error: Db::del: Cannot allocate memory</screen>
|
||||
|
||||
</para>
|
||||
|
||||
<para>Possible solution: make sure that no Nix processes are running,
|
||||
then do:
|
||||
|
||||
<screen>
|
||||
$ cd /nix/var/nix/db
|
||||
$ rm __db.00*</screen>
|
||||
|
||||
</para>
|
||||
|
||||
</section>
|
||||
|
||||
|
||||
<section><title>Berkeley DB gives weird error messages</title>
|
||||
|
||||
<para>Symptom: you get error messages such as
|
||||
|
||||
<screen>
|
||||
Berkeley DB message: Finding last valid log LSN: file: 1 offset 28
|
||||
Berkeley DB error: file validpaths (meta pgno = 0) has LSN [483][34721].
|
||||
Berkeley DB error: end of log is [1][28]
|
||||
Berkeley DB error: /nix/var/nix/db/validpaths: unexpected file type or format</screen>
|
||||
|
||||
or other weird Berkeley DB errors, and they don’t go away (i.e.,
|
||||
automatic recovery doesn’t work). This may be the case after a system
|
||||
crash.</para>
|
||||
|
||||
<para>Solution: first try to run <command>db_recover</command> and
|
||||
then <link linkend='refsec-nix-store-verify'><command>nix-store
|
||||
--verify</command></link>:
|
||||
|
||||
<screen>
|
||||
$ db_recover -h /nix/var/nix/db
|
||||
$ nix-store --verify</screen>
|
||||
|
||||
(Make sure that you have the right version of
|
||||
<command>db_recover</command>, namely, Berkeley DB 4.4 for Nix 0.10,
|
||||
and 4.5 for Nix 0.11.)</para>
|
||||
|
||||
<para>If that doesn’t work, it’s time to bring out the big guns:
|
||||
|
||||
<screen>
|
||||
$ cd /nix/var/nix
|
||||
$ cp -pr db db-backup <lineannotation>(making a backup just in case)</lineannotation>
|
||||
$ cd db
|
||||
$ rm __db.* log* <lineannotation>(removing the Berkeley DB environment)</lineannotation>
|
||||
$ mkdir tmp
|
||||
$ for i in *; do db_dump $i | (cd tmp && db_load $i); done
|
||||
<lineannotation>(ignore error messages about non-database files like “reserved”)</lineannotation>
|
||||
$ mv tmp/* .
|
||||
$ nix-store --verify</screen>
|
||||
|
||||
</para>
|
||||
|
||||
</section>
|
||||
|
||||
|
||||
<section><title>Berkeley DB out of locks</title>
|
||||
|
||||
<para>It is possible, especially in <command>nix-store
|
||||
--verify</command> or when running the garbage collector, to run out
|
||||
of Berkeley DB locks, like this:
|
||||
|
||||
<screen>
|
||||
$ nix-store --verify
|
||||
checking path existence
|
||||
checking path realisability
|
||||
checking the derivers table
|
||||
checking the references table
|
||||
Berkeley DB error: Lock table is out of available object entries
|
||||
error: Db::get: Cannot allocate memory</screen>
|
||||
|
||||
</para>
|
||||
|
||||
<para>A workaround is to increase the number of locks that Berkeley DB
|
||||
allocates. (The real solution would be for Nix to not use so many
|
||||
locks.) This can be done by putting the following in the file
|
||||
<filename>/nix/var/nix/db/<link
|
||||
xlink:href="http://www.oracle.com/technology/documentation/berkeley-db/db/ref/env/db_config.html">DB_CONFIG</link></filename>:
|
||||
|
||||
<programlisting>
|
||||
set_lk_max_locks 100000
|
||||
set_lk_max_lockers 100000
|
||||
set_lk_max_objects 100000
|
||||
</programlisting>
|
||||
|
||||
(Increase these numbers if necessary.) Then make sure that there are
|
||||
no running Nix processes and delete the Berkeley DB environment:
|
||||
|
||||
<screen>
|
||||
$ rm /nix/var/nix/db/__db.*</screen>
|
||||
|
||||
The Berkeley DB environment is automatically recreated with the new
|
||||
limits when you run any Nix command.</para>
|
||||
|
||||
</section>
|
||||
|
||||
|
||||
<section><title>Collisions in <command>nix-env</command></title>
|
||||
|
||||
<para>Symptom: when installing or upgrading, you get an error message such as
|
||||
@@ -187,7 +76,8 @@ Furthermore, the <literal>st_nlink</literal> field of the
|
||||
<para>This only happens on very large Nix installations (such as build
|
||||
machines).</para>
|
||||
|
||||
<para>Quick solution: run the garbage collector.</para>
|
||||
<para>Quick solution: run the garbage collector. You may want to use
|
||||
the <option>--max-links</option> option.</para>
|
||||
|
||||
<para>Real solution: put the Nix store on a file system that supports
|
||||
more than 32,000 subdirectories per directory, such as ReiserFS.
|
||||
|
||||
@@ -731,9 +731,9 @@ stdenv.mkDerivation {
|
||||
xlink:href='http://www.ietf.org/rfc/rfc2396.txt'>RFC 2396</link>
|
||||
can be written <emphasis>as is</emphasis>, without quotes. For
|
||||
instance, the string
|
||||
<literal>"https://svn.cs.uu.nl:12443/dist/trace/trace-nix-trunk.tar.bz2"</literal>
|
||||
<literal>"http://example.org/foo.tar.bz2"</literal>
|
||||
can also be written as
|
||||
<literal>https://svn.cs.uu.nl:12443/dist/trace/trace-nix-trunk.tar.bz2</literal>.</para>
|
||||
<literal>http://example.org/foo.tar.bz2</literal>.</para>
|
||||
|
||||
</listitem>
|
||||
|
||||
@@ -937,17 +937,84 @@ set.</para>
|
||||
<para>Functions have the following form:
|
||||
|
||||
<programlisting>
|
||||
{<replaceable>params</replaceable>}: <replaceable>body</replaceable></programlisting>
|
||||
<replaceable>pattern</replaceable>: <replaceable>body</replaceable></programlisting>
|
||||
|
||||
This defines a function that must be called with an attribute set
|
||||
containing the attributes listed in <replaceable>params</replaceable>,
|
||||
which is a comma-separated list of attribute names. Optionally, for
|
||||
each parameter a <emphasis>default value</emphasis> may be specified
|
||||
by writing <literal><replaceable>param</replaceable> ?
|
||||
<replaceable>e</replaceable></literal>, where
|
||||
<replaceable>e</replaceable> is an arbitrary expression. If a
|
||||
parameter has a default, the corresponding attribute may be omitted in
|
||||
function calls.</para>
|
||||
The pattern specifies what the argument of the function must look
|
||||
like, and binds variables in the body to (parts of) the
|
||||
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:
|
||||
|
||||
<programlisting>
|
||||
let negate = x: !x;
|
||||
concat = x: y: x + y;
|
||||
in if negate true then concat "foo" "bar" else ""</programlisting>
|
||||
|
||||
Note that <function>concat</function> is a function that takes one
|
||||
argument and returns a function that takes another argument. This
|
||||
allows partial parameterisation (i.e., only filling some of the
|
||||
arguments of a function); e.g.,
|
||||
|
||||
<programlisting>
|
||||
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
|
||||
the values of those attributes to variables in the function body.
|
||||
For example, the function
|
||||
|
||||
<programlisting>
|
||||
{x, y, z}: z + y + x</programlisting>
|
||||
|
||||
can only be called with a set containing exactly the attributes
|
||||
<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>):
|
||||
|
||||
<programlisting>
|
||||
{x, y, z, ....}: z + y + x</programlisting>
|
||||
|
||||
This works on any set that contains at least the three named
|
||||
attributes.</para>
|
||||
|
||||
<para>It is possible to provide <emphasis>default values</emphasis>
|
||||
for attributes, in which case they are allowed to be missing. A
|
||||
default value is specified by writing
|
||||
<literal><replaceable>name</replaceable> ?
|
||||
<replaceable>e</replaceable></literal>, where
|
||||
<replaceable>e</replaceable> is an arbitrary expression. For example,
|
||||
|
||||
<programlisting>
|
||||
{x, y ? "foo", z ? "bar"}: z + y + x</programlisting>
|
||||
|
||||
specifies a function that only requires an attribute named
|
||||
<varname>x</varname>, but optionally accepts <varname>y</varname>
|
||||
and <varname>z</varname>.</para></listitem>
|
||||
|
||||
|
||||
<listitem><para>An <literal>@</literal>-pattern requires that the
|
||||
argument matches with the patterns on the left- and right-hand side
|
||||
of the <literal>@</literal>-sign. For example:
|
||||
|
||||
<programlisting>
|
||||
args@{x, y, z, ...}: z + y + x + args.a</programlisting>
|
||||
|
||||
Here <varname>args</varname> is bound to the entire argument, which
|
||||
is further matches against the pattern <literal>{x, y, z,
|
||||
...}</literal>.</para></listitem>
|
||||
|
||||
|
||||
</itemizedlist>
|
||||
|
||||
<para>Note that functions do not have names. If you want to give them
|
||||
a name, you can bind them to an attribute, e.g.,
|
||||
@@ -958,31 +1025,6 @@ in concat {x = "foo"; y = "bar";}</programlisting>
|
||||
|
||||
</para>
|
||||
|
||||
<para>It is also possible to define a function that takes a single
|
||||
argument and that does not need to be called with an attribute set as
|
||||
argument. The syntax is
|
||||
|
||||
<programlisting>
|
||||
<replaceable>var</replaceable>: <replaceable>body</replaceable></programlisting>
|
||||
|
||||
where <replaceable>var</replaceable> is the name of the argument. It
|
||||
is not possible to define a default. Example:
|
||||
|
||||
<programlisting>
|
||||
let negate = x: !x;
|
||||
concat = x: y: x + y;
|
||||
in if negate true then concat "foo" "bar" else ""</programlisting>
|
||||
|
||||
Note that <function>concat</function> is a function that takes one
|
||||
arguments and returns a function that takes another argument. This
|
||||
allows partial parameterisation (i.e., only filling some of the
|
||||
arguments of a function); e.g.,
|
||||
|
||||
<programlisting>
|
||||
map (concat "foo") ["bar" "bla" "abc"]</programlisting>
|
||||
|
||||
evaluates to <literal>["foobar" "foobla" "fooabc"]</literal>.</para>
|
||||
|
||||
</simplesect>
|
||||
|
||||
|
||||
@@ -1064,7 +1106,7 @@ used in the Nix expression for Subversion.</para>
|
||||
incompatibility might occur.</para>
|
||||
</callout>
|
||||
|
||||
<callout arearefs='ex-subversion-nix-co-2'>
|
||||
<callout arearefs='ex-subversion-nix-co-3'>
|
||||
<para>This assertion says that in order for Subversion to have SSL
|
||||
support (so that it can access <literal>https</literal> URLs), an
|
||||
OpenSSL library must be passed. Additionally, it says that
|
||||
@@ -1390,7 +1432,7 @@ command-line argument. See <xref linkend='sec-standard-environment'
|
||||
inputs.</para></listitem>
|
||||
|
||||
<listitem><para>After the build, Nix sets the last-modified
|
||||
timestamp on all files in the build result to 0 (00:00:00 1/1/1970
|
||||
timestamp on all files in the build result to 1 (00:00:01 1/1/1970
|
||||
UTC), sets the group to the default group, and sets the mode of the
|
||||
file to 0444 or 0555 (i.e., read-only, with execute permission
|
||||
enabled if the file was originally executable). Note that possible
|
||||
@@ -1644,59 +1686,6 @@ impureEnvVars = ["http_proxy" "https_proxy" <replaceable>...</replaceable>];
|
||||
|
||||
<section xml:id='sec-standard-environment'><title>The standard environment</title>
|
||||
|
||||
<para>The standard build environment in the Nix Packages collection
|
||||
provides a basic environment for building Unix packages. It consists
|
||||
of the following packages:
|
||||
|
||||
<itemizedlist>
|
||||
|
||||
<listitem><para>The GNU C Compiler, configured with C and C++
|
||||
support. On Linux, the compiler has been patched to provide greater
|
||||
<quote>purity</quote> assurance. For instance, the compiler doesn't
|
||||
search in locations such as <filename>/usr/include</filename>. In
|
||||
fact, attempts to add such directories through the
|
||||
<option>-I</option> flag are filtered out. Likewise, the linker
|
||||
(from GNU binutils) doesn't search in standard locations such as
|
||||
<filename>/usr/lib</filename>. Programs built on Linux are linked
|
||||
against a GNU C Library that likewise doesn't search in the default
|
||||
system locations.</para></listitem>
|
||||
|
||||
<listitem><para>GNU coreutils (contains a few dozen standard Unix
|
||||
commands).</para></listitem>
|
||||
|
||||
<listitem><para>GNU findutils (contains
|
||||
<command>find</command>).</para></listitem>
|
||||
|
||||
<listitem><para>GNU diffutils (contains <command>diff</command>,
|
||||
<command>cmp</command>).</para></listitem>
|
||||
|
||||
<listitem><para>GNU <command>sed</command>.</para></listitem>
|
||||
|
||||
<listitem><para>GNU <command>grep</command>.</para></listitem>
|
||||
|
||||
<listitem><para>GNU <command>awk</command>.</para></listitem>
|
||||
|
||||
<listitem><para>GNU <command>tar</command>.</para></listitem>
|
||||
|
||||
<listitem><para><command>gzip</command> and
|
||||
<command>bzip2</command>.</para></listitem>
|
||||
|
||||
<listitem><para>GNU Make. It has been patched to provide
|
||||
<quote>nested</quote> output that can be fed into the
|
||||
<command>nix-log2xml</command> command and
|
||||
<command>log2html</command> stylesheet to create a structured,
|
||||
readable output of the build steps performed by
|
||||
Make.</para></listitem>
|
||||
|
||||
<listitem><para>Bash. This is the shell used for all builders in
|
||||
the Nix Packages collection. Not using <command>/bin/sh</command>
|
||||
removes a large source of portability problems.</para></listitem>
|
||||
|
||||
<listitem><para>Patch.</para></listitem>
|
||||
|
||||
</itemizedlist>
|
||||
|
||||
</para>
|
||||
|
||||
<para>The standard environment is used by passing it as an input
|
||||
called <envar>stdenv</envar> to the derivation, and then doing
|
||||
@@ -1765,115 +1754,6 @@ myPostInstall() {
|
||||
|
||||
</para>
|
||||
|
||||
<para>The generic builder has a number of <emphasis>phases</emphasis>,
|
||||
each of which can be override in its entirety by setting the indicated
|
||||
variable. The phases are:
|
||||
|
||||
<itemizedlist>
|
||||
|
||||
<listitem>
|
||||
|
||||
<para><function>unpackPhase</function> unpacks the source files
|
||||
listed in the <envar>src</envar> environment variable to the
|
||||
current directory. It supports <filename>tar</filename> files,
|
||||
optionally compressed with <command>gzip</command> or
|
||||
<command>bzip2</command>; Zip files (but note that the
|
||||
<command>unzip</command> command is not a part of the standard
|
||||
environment; you should add it as a build input yourself); and
|
||||
unpacked source trees (i.e., directories; they are copied
|
||||
verbatim). You can add support for other file types by setting
|
||||
the <varname>findUnpacker</varname> hook. This hook should set
|
||||
the variable <varname>unpackCmd</varname> to contain the command
|
||||
to be executed to unpack the file.</para>
|
||||
|
||||
<para>After unpacking all source files,
|
||||
<function>unpackPhase</function> changes the current directory to
|
||||
the directory created by unpacking the sources. If there are
|
||||
multiple source directories, you should set
|
||||
<varname>sourceRoot</varname> to the name of the intended
|
||||
directory.</para>
|
||||
|
||||
<para>It also calls the hook <varname>postUnpack</varname> after
|
||||
unpacking.</para>
|
||||
|
||||
</listitem>
|
||||
|
||||
<listitem><para><function>patchPhase</function> calls the
|
||||
<command>patch</command> command with the <option>-p1</option>
|
||||
option for each patch file listed in the <envar>patches</envar>
|
||||
variable.</para></listitem>
|
||||
|
||||
<listitem>
|
||||
|
||||
<para><function>configurePhase</function> runs the script called
|
||||
<filename>configure</filename> in the current directory with a
|
||||
<option>--prefix</option> set to the output path. You can add
|
||||
additional flags through the <varname>configureFlags</varname>
|
||||
variable. If <filename>configure</filename> does not exist,
|
||||
nothing happens.</para>
|
||||
|
||||
<para>Before and after running <filename>configure</filename>, the
|
||||
hooks <varname>preConfigure</varname> and
|
||||
<varname>postConfigure</varname> are called, respectively.</para>
|
||||
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
|
||||
<para><function>buildPhase</function> calls
|
||||
<command>make</command>. You can set flags for
|
||||
<command>make</command> through the <varname>makeFlags</varname>
|
||||
variable.</para>
|
||||
|
||||
<para>Before and after running <command>make</command>, the hooks
|
||||
<varname>preBuild</varname> and <varname>postBuild</varname> are
|
||||
called, respectively.</para>
|
||||
|
||||
</listitem>
|
||||
|
||||
<listitem><para><function>checkPhase</function> calls <command>make
|
||||
check</command>, but only if the <varname>doCheck</varname> variable
|
||||
is set to <literal>1</literal>. Additional flags can be set through
|
||||
the <varname>checkFlags</varname> variable.</para></listitem>
|
||||
|
||||
<listitem>
|
||||
|
||||
<para><function>installPhase</function> calls <command>make
|
||||
install</command>. Additional flags can be set through the
|
||||
<varname>installFlags</varname> variable. It also strips any
|
||||
static libraries in the output path of debug information unless
|
||||
<varname>dontStrip</varname> is set to
|
||||
<literal>1</literal>.</para>
|
||||
|
||||
<para>Before and after running <command>make install</command>,
|
||||
the hooks <varname>preInstall</varname> and
|
||||
<varname>postInstall</varname> are called, respectively.</para>
|
||||
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
|
||||
<para><function>distPhase</function> calls <command>make
|
||||
dist</command>, but only if the <varname>doDist</varname> variable
|
||||
is set to <literal>1</literal>. Additional flags can be set
|
||||
through the <varname>distFlags</varname> variable. The resulting
|
||||
tarball is copied to the <filename>/tarballs</filename>
|
||||
subdirectory of the output path.</para>
|
||||
|
||||
<para>Before and after running <command>make dist</command>, the
|
||||
hooks <varname>preDist</varname> and <varname>postDist</varname>
|
||||
are called, respectively.</para>
|
||||
|
||||
</listitem>
|
||||
|
||||
</itemizedlist>
|
||||
|
||||
</para>
|
||||
|
||||
<para>You can change the order in which phases are executed, or add
|
||||
new phases, by setting the <varname>phases</varname> variable. The
|
||||
default is <literal>patchPhase configurePhase buildPhase checkPhase
|
||||
installPhase distPhase</literal>.</para>
|
||||
|
||||
</section>
|
||||
|
||||
|
||||
130
externals/Makefile.am
vendored
130
externals/Makefile.am
vendored
@@ -1,109 +1,67 @@
|
||||
# Berkeley DB
|
||||
|
||||
DB = db-4.5.20
|
||||
|
||||
$(DB).tar.gz:
|
||||
@echo "Nix requires Berkeley DB to build."
|
||||
@echo "Please download version 4.5.20 from"
|
||||
@echo " http://download-east.oracle.com/berkeley-db/db-4.5.20.tar.gz"
|
||||
@echo "and place it in the externals/ directory."
|
||||
false
|
||||
|
||||
$(DB): $(DB).tar.gz
|
||||
gunzip < $(srcdir)/$(DB).tar.gz | tar xvf -
|
||||
(cd $(DB) && $(patch) -p1) < $(srcdir)/bdb-cygwin.patch
|
||||
|
||||
have-db:
|
||||
$(MAKE) $(DB)
|
||||
touch have-db
|
||||
|
||||
if HAVE_BDB
|
||||
build-db:
|
||||
else
|
||||
build-db: have-db
|
||||
(pfx=`pwd` && \
|
||||
cd $(DB)/build_unix && \
|
||||
CC="$(CC)" CXX="$(CXX)" CFLAGS="$(CFLAGS)" CXXFLAGS="$(CXXFLAGS)" \
|
||||
../dist/configure --prefix=$$pfx/inst-bdb \
|
||||
--enable-cxx --disable-shared --disable-cryptography \
|
||||
--disable-replication --disable-verify && \
|
||||
$(MAKE) && \
|
||||
$(MAKE) install_include install_lib)
|
||||
touch build-db
|
||||
endif
|
||||
|
||||
|
||||
# CWI ATerm
|
||||
|
||||
ATERM = aterm-2.4.2-fixes-r2
|
||||
|
||||
$(ATERM).tar.bz2:
|
||||
@echo "Nix requires the CWI ATerm library to build."
|
||||
@echo "Please download version 2.4.2-fixes-r2 from"
|
||||
@echo " http://losser.st-lab.cs.uu.nl/~eelco/dist/aterm-2.4.2-fixes-r2.tar.bz2"
|
||||
@echo "and place it in the externals/ directory."
|
||||
false
|
||||
|
||||
$(ATERM): $(ATERM).tar.bz2
|
||||
bunzip2 < $(srcdir)/$(ATERM).tar.bz2 | tar xvf -
|
||||
|
||||
have-aterm:
|
||||
$(MAKE) $(ATERM)
|
||||
touch have-aterm
|
||||
|
||||
if HAVE_ATERM
|
||||
build-aterm:
|
||||
else
|
||||
build-aterm: have-aterm
|
||||
(pfx=`pwd` && \
|
||||
cd $(ATERM) && \
|
||||
CC="$(CC)" ./configure --prefix=$$pfx/inst-aterm \
|
||||
--disable-shared --enable-static && \
|
||||
$(MAKE) && \
|
||||
$(MAKE) install)
|
||||
touch build-aterm
|
||||
endif
|
||||
|
||||
|
||||
# bzip2
|
||||
|
||||
BZIP2 = bzip2-1.0.4
|
||||
BZIP2 = bzip2-1.0.5
|
||||
|
||||
$(BZIP2).tar.gz:
|
||||
@echo "Nix requires bzip2 to build."
|
||||
@echo "Please download version 1.0.4 from"
|
||||
@echo " http://www.bzip.org/1.0.4/bzip2-1.0.4.tar.gz"
|
||||
@echo "Please download version 1.0.5 from"
|
||||
@echo " http://www.bzip.org/1.0.5/bzip2-1.0.5.tar.gz"
|
||||
@echo "and place it in the externals/ directory."
|
||||
false
|
||||
|
||||
$(BZIP2): $(BZIP2).tar.gz
|
||||
gunzip < $(srcdir)/$(BZIP2).tar.gz | tar xvf -
|
||||
|
||||
have-bzip2:
|
||||
$(MAKE) $(BZIP2)
|
||||
touch have-bzip2
|
||||
|
||||
if HAVE_BZIP2
|
||||
build-bzip2:
|
||||
else
|
||||
build-bzip2: have-bzip2
|
||||
(pfx=`pwd` && \
|
||||
cd $(BZIP2) && \
|
||||
$(MAKE) && \
|
||||
$(MAKE) install PREFIX=$$pfx/inst-bzip2)
|
||||
build-bzip2: $(BZIP2)
|
||||
(cd $(BZIP2) && \
|
||||
$(MAKE) CC="$(CC)" && \
|
||||
$(MAKE) install PREFIX=$(abs_builddir)/inst-bzip2)
|
||||
touch build-bzip2
|
||||
|
||||
install:
|
||||
install-exec-local:: build-bzip2
|
||||
mkdir -p $(DESTDIR)${bzip2_bin}
|
||||
$(INSTALL_PROGRAM) $(bzip2_bin_test)/bzip2 $(bzip2_bin_test)/bunzip2 $(DESTDIR)${bzip2_bin}
|
||||
endif
|
||||
|
||||
|
||||
all: build-db build-aterm build-bzip2
|
||||
# SQLite
|
||||
|
||||
EXTRA_DIST = $(DB).tar.gz $(ATERM).tar.bz2 $(BZIP2).tar.gz \
|
||||
bdb-cygwin.patch
|
||||
SQLITE = sqlite-autoconf-$(SQLITE_VERSION)
|
||||
SQLITE_TAR = sqlite-autoconf-$(SQLITE_VERSION).tar.gz
|
||||
|
||||
ext-clean:
|
||||
$(RM) -f have-db build-db have-aterm build-aterm
|
||||
$(RM) -rf $(DB) $(ATERM) $(BZIP2)
|
||||
$(SQLITE_TAR):
|
||||
@echo "Nix requires the SQLite library to build."
|
||||
@echo "Please download version $(SQLITE_VERSION) from"
|
||||
@echo " http://www.sqlite.org/$(SQLITE_TAR)"
|
||||
@echo "and place it in the externals/ directory."
|
||||
false
|
||||
|
||||
$(SQLITE): $(SQLITE_TAR)
|
||||
gzip -d < $(srcdir)/$(SQLITE_TAR) | tar xvf -
|
||||
|
||||
if HAVE_SQLITE
|
||||
build-sqlite:
|
||||
else
|
||||
build-sqlite: $(SQLITE)
|
||||
(cd $(SQLITE) && \
|
||||
CC="$(CC)" CFLAGS="-DSQLITE_ENABLE_COLUMN_METADATA=1" ./configure --disable-static --prefix=$(pkglibdir)/dummy --libdir=${pkglibdir} $(SUB_CONFIGURE_FLAGS) && \
|
||||
$(MAKE) )
|
||||
touch build-sqlite
|
||||
|
||||
install-exec-local:: build-sqlite
|
||||
cd $(SQLITE) && $(MAKE) install
|
||||
rm -rf "$(DESTDIR)/$(pkglibdir)/dummy"
|
||||
endif
|
||||
|
||||
|
||||
all: build-bzip2 build-sqlite
|
||||
|
||||
EXTRA_DIST = $(BZIP2).tar.gz $(SQLITE_TAR)
|
||||
|
||||
clean:
|
||||
$(RM) -f build-bzip2 build-sqlite
|
||||
$(RM) -rf $(BZIP2) $(SQLITE)
|
||||
$(RM) -rf inst-bzip2
|
||||
|
||||
22
externals/bdb-cygwin.patch
vendored
22
externals/bdb-cygwin.patch
vendored
@@ -1,22 +0,0 @@
|
||||
diff -rc db-4.5.20-orig/os/os_flock.c db-4.5.20/os/os_flock.c
|
||||
*** db-4.5.20-orig/os/os_flock.c 2006-10-13 12:36:12.000000000 +0200
|
||||
--- db-4.5.20/os/os_flock.c 2006-10-13 12:40:11.000000000 +0200
|
||||
***************
|
||||
*** 30,35 ****
|
||||
--- 30,44 ----
|
||||
|
||||
DB_ASSERT(dbenv, F_ISSET(fhp, DB_FH_OPENED) && fhp->fd != -1);
|
||||
|
||||
+ #ifdef __CYGWIN__
|
||||
+ /*
|
||||
+ * Windows file locking interferes with read/write operations, so we
|
||||
+ * map the ranges to an area past the end of the file.
|
||||
+ */
|
||||
+ DB_ASSERT(dbenv, offset < (off_t) 1 << 62);
|
||||
+ offset += (off_t) 1 << 62;
|
||||
+ #endif
|
||||
+
|
||||
fl.l_start = offset;
|
||||
fl.l_len = 1;
|
||||
fl.l_type = acquire ? F_WRLCK : F_UNLCK;
|
||||
Only in db-4.5.20/os: os_flock.c~
|
||||
@@ -76,10 +76,10 @@ The hook `nix-mode-hook' is run when Nix mode is started.
|
||||
("\\<baseNameOf\\>" . font-lock-builtin-face)
|
||||
("\\<toString\\>" . font-lock-builtin-face)
|
||||
("\\<isNull\\>" . font-lock-builtin-face)
|
||||
("\\<\\([a-zA-Z_][a-zA-Z0-9_']*\\)[ \t]*="
|
||||
(1 font-lock-variable-name-face nil nil))
|
||||
("[a-zA-Z][a-zA-Z0-9\\+-\\.]*:[a-zA-Z0-9%/\\?:@&=\\+\\$,_\\.!~\\*'-]+"
|
||||
. font-lock-constant-face)
|
||||
("\\<\\([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)
|
||||
))
|
||||
|
||||
@@ -21,12 +21,14 @@ syn match nixFuncArg "\zs\w\+\ze\s*:"
|
||||
syn region nixStringParam start=+\${+ end=+}+
|
||||
syn region nixMultiLineComment start=+/\*+ skip=+\\"+ end=+\*/+
|
||||
syn match nixEndOfLineComment "#.*$"
|
||||
syn region nixString start=+"+ skip=+\\"+ end=+"+ contains=nixStringParam
|
||||
syn region nixStringIndented start=+''+ skip=+'''\|''${\|"+ end=+''+ contains=nixStringParam
|
||||
syn region nixString start=+"+ skip=+\\"+ end=+"+ contains=nixStringParam
|
||||
|
||||
hi def link nixKeyword Keyword
|
||||
hi def link nixConditional Conditional
|
||||
hi def link nixBrace Special
|
||||
hi def link nixString String
|
||||
hi def link nixStringIndented String
|
||||
hi def link nixBuiltin Special
|
||||
hi def link nixStringParam Macro
|
||||
hi def link nixMultiLineComment Comment
|
||||
|
||||
@@ -29,25 +29,6 @@
|
||||
#gc-keep-derivations = true
|
||||
|
||||
|
||||
### Option `gc-reserved-space'
|
||||
#
|
||||
# This option specifies how much space should be reserved in normal
|
||||
# use so that the garbage collector can run succesfully. Since the
|
||||
# garbage collector must perform Berkeley DB transactions, it needs
|
||||
# some disk space for itself. However, when the disk is full, this
|
||||
# space is not available, so the collector would not be able to run
|
||||
# precisely when it is most needed.
|
||||
#
|
||||
# For this reason, when Nix is run, it allocates a file
|
||||
# /nix/var/nix/db/reserved of the size specified by this option. When
|
||||
# the garbage collector is run, this file is deleted before the
|
||||
# Berkeley DB environment is opened. This should give it enough room
|
||||
# to proceed.
|
||||
#
|
||||
# The default is "1048576" (1 MiB).
|
||||
#gc-reserved-space = 1048576
|
||||
|
||||
|
||||
### Option `env-keep-derivations'
|
||||
#
|
||||
# If `false' (default), derivations are not stored in Nix user
|
||||
@@ -78,6 +59,17 @@
|
||||
#build-max-jobs = 1
|
||||
|
||||
|
||||
### Option `build-cores'
|
||||
#
|
||||
# This option defines the number of CPU cores to utilize in parallel
|
||||
# within a build job, i.e. by passing an appropriate `-jN' flag to GNU
|
||||
# Make. The default is 1, meaning that parallel building within jobs
|
||||
# is disabled. Passing the special value `0' causes Nix to try and
|
||||
# auto-detect the number of available cores on the local host. This
|
||||
# setting can be overridden using the `--cores' command line switch.
|
||||
#build-cores = 1
|
||||
|
||||
|
||||
### Option `build-max-silent-time'
|
||||
#
|
||||
# This option defines the maximum number of seconds that a builder can
|
||||
@@ -164,30 +156,26 @@
|
||||
# (using `mount --bind' on Linux) some directories from the normal
|
||||
# file system hierarchy inside the chroot. These are the Nix store,
|
||||
# the temporary build directory (usually /tmp/nix-<pid>-<number>) and
|
||||
# the directories listed here. The default is "/dev /proc". Files
|
||||
# in /dev (such as /dev/null) are needed by many builds, and some
|
||||
# files in /proc may also be needed occasionally.
|
||||
# the directories listed here. The default is "/dev /dev/pts /proc".
|
||||
# Files in /dev (such as /dev/null) are needed by many builds, and
|
||||
# some files in /proc may also be needed occasionally.
|
||||
#
|
||||
# Example:
|
||||
# build-use-chroot = /dev /proc /bin
|
||||
#build-chroot-dirs = /dev /proc
|
||||
#build-chroot-dirs = /dev /dev/pts /proc
|
||||
|
||||
|
||||
### Option `system'
|
||||
### Option `build-cache-failure'
|
||||
#
|
||||
# This option specifies the canonical Nix system name of the current
|
||||
# installation, such as `i686-linux' or `powerpc-darwin'. Nix can
|
||||
# only build derivations whose `system' attribute equals the value
|
||||
# specified here. In general, it never makes sense to modify this
|
||||
# value from its default, since you can use it to `lie' about the
|
||||
# platform you are building on (e.g., perform a Mac OS build on a
|
||||
# Linux machine; the result would obviously be wrong). It only makes
|
||||
# sense if the Nix binaries can run on multiple platforms, e.g.,
|
||||
# `universal binaries' that run on `powerpc-darwin' and `i686-darwin'.
|
||||
#
|
||||
# It defaults to the canonical Nix system name detected by `configure'
|
||||
# at build time.
|
||||
# If this option is enabled, Nix will do negative caching; that is, it
|
||||
# will remember failed builds, and won't attempt to try to build them
|
||||
# again if you ask for it. Negative caching is disabled by default
|
||||
# because Nix cannot distinguish between permanent build errors (e.g.,
|
||||
# a syntax error in a source file) and transient build errors (e.g., a
|
||||
# full disk), as they both cause the builder to return a non-zero exit
|
||||
# code. You can clear the cache by doing `rm -f
|
||||
# /nix/var/nix/db/failed/*'.
|
||||
#
|
||||
# Example:
|
||||
# system = i686-darwin
|
||||
#system =
|
||||
# build-cache-failure = true
|
||||
#build-cache-failure = false
|
||||
|
||||
18
nix.spec.in
18
nix.spec.in
@@ -13,11 +13,10 @@ Version: @version@
|
||||
Release: 1
|
||||
License: GPL
|
||||
Group: Software Deployment
|
||||
URL: http://nix.cs.uu.nl/
|
||||
URL: http://nixos.org/
|
||||
Source0: %{name}-@version@.tar.bz2
|
||||
BuildRoot: %{_tmppath}/%{name}-%{version}-buildroot
|
||||
%define _prefix /nix
|
||||
Prefix: %{_prefix}
|
||||
Prefix: /usr
|
||||
Requires: /usr/bin/perl
|
||||
Requires: curl
|
||||
|
||||
@@ -47,14 +46,14 @@ if test -n "%{enable_setuid}"; then
|
||||
extraFlags="$extraFlags --with-nix-group=%{nix_group}"
|
||||
fi
|
||||
fi
|
||||
./configure --prefix=%{_prefix} $extraFlags
|
||||
./configure --prefix=%{_prefix} --sysconfdir=/etc $extraFlags
|
||||
make
|
||||
make check
|
||||
|
||||
%install
|
||||
rm -rf $RPM_BUILD_ROOT
|
||||
make DESTDIR=$RPM_BUILD_ROOT install
|
||||
rm $RPM_BUILD_ROOT/%{_prefix}/etc/nix/nix.conf
|
||||
rm $RPM_BUILD_ROOT/etc/nix/nix.conf
|
||||
strip $RPM_BUILD_ROOT/%{_prefix}/bin/* || true
|
||||
|
||||
%clean
|
||||
@@ -76,10 +75,9 @@ fi
|
||||
%{_prefix}/lib
|
||||
%{_prefix}/libexec
|
||||
%{_prefix}/include
|
||||
%{_prefix}/var
|
||||
%{_prefix}/share
|
||||
%{_prefix}/store
|
||||
/etc/profile.d/nix.sh
|
||||
/nix/var
|
||||
/nix/store
|
||||
%config
|
||||
%{_prefix}/etc
|
||||
#%doc
|
||||
#%{_prefix}/share/nix/manual
|
||||
/etc/nix
|
||||
|
||||
177
release.nix
Normal file
177
release.nix
Normal file
@@ -0,0 +1,177 @@
|
||||
{ nixpkgs ? ../nixpkgs
|
||||
, nix ? { outPath = ./.; rev = 1234; }
|
||||
, officialRelease ? false
|
||||
}:
|
||||
|
||||
let
|
||||
|
||||
jobs = rec {
|
||||
|
||||
|
||||
tarball =
|
||||
with import nixpkgs {};
|
||||
|
||||
releaseTools.sourceTarball {
|
||||
name = "nix-tarball";
|
||||
version = builtins.readFile ./version;
|
||||
src = nix;
|
||||
inherit officialRelease;
|
||||
|
||||
buildInputs =
|
||||
[ curl bison24 flex2535 perl libxml2 libxslt w3m bzip2
|
||||
tetex dblatex nukeReferences pkgconfig
|
||||
];
|
||||
|
||||
configureFlags = ''
|
||||
--with-docbook-rng=${docbook5}/xml/rng/docbook
|
||||
--with-docbook-xsl=${docbook5_xsl}/xml/xsl/docbook
|
||||
--with-xml-flags=--nonet
|
||||
'';
|
||||
|
||||
# Include the Bzip2 tarball in the distribution.
|
||||
preConfigure = ''
|
||||
stripHash ${bzip2.src}
|
||||
cp -pv ${bzip2.src} externals/$strippedName
|
||||
|
||||
stripHash ${sqlite.src}
|
||||
cp -pv ${sqlite.src} externals/$strippedName
|
||||
|
||||
# TeX needs a writable font cache.
|
||||
export VARTEXFONTS=$TMPDIR/texfonts
|
||||
'';
|
||||
|
||||
preDist = ''
|
||||
make -C doc/manual install prefix=$out
|
||||
|
||||
make -C doc/manual manual.pdf prefix=$out
|
||||
cp doc/manual/manual.pdf $out/manual.pdf
|
||||
|
||||
# The PDF containes filenames of included graphics (see
|
||||
# http://www.tug.org/pipermail/pdftex/2007-August/007290.html).
|
||||
# This causes a retained dependency on dblatex, which Hydra
|
||||
# doesn't like (the output of the tarball job is distributed
|
||||
# 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
|
||||
'';
|
||||
};
|
||||
|
||||
|
||||
build =
|
||||
{ system ? "i686-linux" }:
|
||||
|
||||
with import nixpkgs { inherit system; };
|
||||
|
||||
releaseTools.nixBuild {
|
||||
name = "nix";
|
||||
src = tarball;
|
||||
|
||||
buildInputs = [ curl perl bzip2 openssl pkgconfig boehmgc ];
|
||||
|
||||
configureFlags = ''
|
||||
--disable-init-state
|
||||
--with-bzip2=${bzip2} --with-sqlite=${sqlite}
|
||||
--enable-gc
|
||||
'';
|
||||
};
|
||||
|
||||
|
||||
coverage =
|
||||
with import nixpkgs { system = "x86_64-linux"; };
|
||||
|
||||
releaseTools.coverageAnalysis {
|
||||
name = "nix-build";
|
||||
src = tarball;
|
||||
|
||||
buildInputs =
|
||||
[ curl perl bzip2 openssl
|
||||
# These are for "make check" only:
|
||||
graphviz libxml2 libxslt
|
||||
];
|
||||
|
||||
configureFlags = ''
|
||||
--disable-init-state --disable-shared
|
||||
--with-bzip2=${bzip2} --with-sqlite=${sqlite}
|
||||
'';
|
||||
|
||||
lcovFilter = [ "*/boost/*" "*-tab.*" ];
|
||||
|
||||
# We call `dot', and even though we just use it to
|
||||
# syntax-check generated dot files, it still requires some
|
||||
# fonts. So provide those.
|
||||
FONTCONFIG_FILE = texFunctions.fontsConf;
|
||||
};
|
||||
|
||||
|
||||
rpm_fedora5i386 = makeRPM_i686 (diskImages: diskImages.fedora5i386) 10;
|
||||
rpm_fedora9i386 = makeRPM_i686 (diskImages: diskImages.fedora9i386) 20;
|
||||
rpm_fedora9x86_64 = makeRPM_x86_64 (diskImages: diskImages.fedora9x86_64) 20;
|
||||
rpm_fedora10i386 = makeRPM_i686 (diskImages: diskImages.fedora10i386) 30;
|
||||
rpm_fedora10x86_64 = makeRPM_x86_64 (diskImages: diskImages.fedora10x86_64) 30;
|
||||
rpm_fedora11i386 = makeRPM_i686 (diskImages: diskImages.fedora11i386) 40;
|
||||
rpm_fedora11x86_64 = makeRPM_x86_64 (diskImages: diskImages.fedora11x86_64) 40;
|
||||
rpm_fedora12i386 = makeRPM_i686 (diskImages: diskImages.fedora12i386) 50;
|
||||
rpm_fedora12x86_64 = makeRPM_x86_64 (diskImages: diskImages.fedora12x86_64) 50;
|
||||
rpm_opensuse103i386 = makeRPM_i686 (diskImages: diskImages.opensuse103i386) 40;
|
||||
rpm_opensuse110i386 = makeRPM_i686 (diskImages: diskImages.opensuse110i386) 50;
|
||||
rpm_opensuse110x86_64 = makeRPM_x86_64 (diskImages: diskImages.opensuse110x86_64) 50;
|
||||
|
||||
|
||||
deb_debian40i386 = makeDeb_i686 (diskImages: diskImages.debian40i386) 40;
|
||||
deb_debian40x86_64 = makeDeb_x86_64 (diskImages: diskImages.debian40x86_64) 40;
|
||||
deb_debian50i386 = makeDeb_i686 (diskImages: diskImages.debian50i386) 50;
|
||||
deb_debian50x86_64 = makeDeb_x86_64 (diskImages: diskImages.debian50x86_64) 50;
|
||||
deb_ubuntu804i386 = makeDeb_i686 (diskImages: diskImages.ubuntu804i386) 20;
|
||||
deb_ubuntu804x86_64 = makeDeb_x86_64 (diskImages: diskImages.ubuntu804x86_64) 20;
|
||||
deb_ubuntu810i386 = makeDeb_i686 (diskImages: diskImages.ubuntu810i386) 30;
|
||||
deb_ubuntu810x86_64 = makeDeb_x86_64 (diskImages: diskImages.ubuntu810x86_64) 30;
|
||||
deb_ubuntu904i386 = makeDeb_i686 (diskImages: diskImages.ubuntu904i386) 40;
|
||||
deb_ubuntu904x86_64 = makeDeb_x86_64 (diskImages: diskImages.ubuntu904x86_64) 40;
|
||||
deb_ubuntu910i386 = makeDeb_i686 (diskImages: diskImages.ubuntu910i386) 50;
|
||||
deb_ubuntu910x86_64 = makeDeb_x86_64 (diskImages: diskImages.ubuntu910x86_64) 50;
|
||||
|
||||
|
||||
};
|
||||
|
||||
|
||||
makeRPM_i686 = makeRPM "i686-linux";
|
||||
makeRPM_x86_64 = makeRPM "x86_64-linux";
|
||||
|
||||
makeRPM =
|
||||
system: diskImageFun: prio:
|
||||
|
||||
with import nixpkgs { inherit system; };
|
||||
|
||||
releaseTools.rpmBuild rec {
|
||||
name = "nix-rpm-${diskImage.name}";
|
||||
src = jobs.tarball;
|
||||
diskImage = diskImageFun vmTools.diskImages;
|
||||
memSize = 1024;
|
||||
meta.schedulingPriority = prio;
|
||||
};
|
||||
|
||||
|
||||
makeDeb_i686 = makeDeb "i686-linux";
|
||||
makeDeb_x86_64 = makeDeb "x86_64-linux";
|
||||
|
||||
makeDeb =
|
||||
system: diskImageFun: prio:
|
||||
|
||||
with import nixpkgs { inherit system; };
|
||||
|
||||
releaseTools.debBuild {
|
||||
name = "nix-deb";
|
||||
src = jobs.tarball;
|
||||
diskImage = diskImageFun vmTools.diskImages;
|
||||
memSize = 1024;
|
||||
meta.schedulingPriority = prio;
|
||||
configureFlags = "--sysconfdir=/etc";
|
||||
debRequires = [ "curl" ];
|
||||
};
|
||||
|
||||
|
||||
in jobs
|
||||
334
scripts/GeneratePatches.pm.in
Executable file
334
scripts/GeneratePatches.pm.in
Executable file
@@ -0,0 +1,334 @@
|
||||
#! @perl@ -w -I@libexecdir@/nix
|
||||
|
||||
use strict;
|
||||
use File::Temp qw(tempdir);
|
||||
|
||||
|
||||
# Some patch generations options.
|
||||
|
||||
# Max size of NAR archives to generate patches for.
|
||||
my $maxNarSize = $ENV{"NIX_MAX_NAR_SIZE"};
|
||||
$maxNarSize = 160 * 1024 * 1024 if !defined $maxNarSize;
|
||||
|
||||
# If patch is bigger than this fraction of full archive, reject.
|
||||
my $maxPatchFraction = $ENV{"NIX_PATCH_FRACTION"};
|
||||
$maxPatchFraction = 0.60 if !defined $maxPatchFraction;
|
||||
|
||||
my $timeLimit = $ENV{"NIX_BSDIFF_TIME_LIMIT"};
|
||||
$timeLimit = 180 if !defined $timeLimit;
|
||||
|
||||
my $hashAlgo = "sha256";
|
||||
|
||||
|
||||
sub findOutputPaths {
|
||||
my $narFiles = shift;
|
||||
|
||||
my %outPaths;
|
||||
|
||||
foreach my $p (keys %{$narFiles}) {
|
||||
|
||||
# Ignore derivations.
|
||||
next if ($p =~ /\.drv$/);
|
||||
|
||||
# Ignore builders (too much ambiguity -- they're all called
|
||||
# `builder.sh').
|
||||
next if ($p =~ /\.sh$/);
|
||||
next if ($p =~ /\.patch$/);
|
||||
|
||||
# Don't bother including tar files etc.
|
||||
next if ($p =~ /\.tar$/ || $p =~ /\.tar\.(gz|bz2|Z|lzma|xz)$/ || $p =~ /\.zip$/ || $p =~ /\.bin$/ || $p =~ /\.tgz$/ || $p =~ /\.rpm$/ || $p =~ /cvs-export$/ || $p =~ /fetchhg$/);
|
||||
|
||||
$outPaths{$p} = 1;
|
||||
}
|
||||
|
||||
return %outPaths;
|
||||
}
|
||||
|
||||
|
||||
sub getNameVersion {
|
||||
my $p = shift;
|
||||
$p =~ /\/[0-9a-z]+((?:-[a-zA-Z][^\/-]*)+)([^\/]*)$/;
|
||||
my $name = $1;
|
||||
my $version = $2;
|
||||
return undef unless defined $name && defined $version;
|
||||
$name =~ s/^-//;
|
||||
$version =~ s/^-//;
|
||||
return ($name, $version);
|
||||
}
|
||||
|
||||
|
||||
# A quick hack to get a measure of the `distance' between two
|
||||
# versions: it's just the position of the first character that differs
|
||||
# (or 999 if they are the same).
|
||||
sub versionDiff {
|
||||
my $s = shift;
|
||||
my $t = shift;
|
||||
my $i;
|
||||
return 999 if $s eq $t;
|
||||
for ($i = 0; $i < length $s; $i++) {
|
||||
return $i if $i >= length $t or
|
||||
substr($s, $i, 1) ne substr($t, $i, 1);
|
||||
}
|
||||
return $i;
|
||||
}
|
||||
|
||||
|
||||
sub getNarBz2 {
|
||||
my $narPath = shift;
|
||||
my $narFiles = shift;
|
||||
my $storePath = shift;
|
||||
|
||||
my $narFileList = $$narFiles{$storePath};
|
||||
die "missing path $storePath" unless defined $narFileList;
|
||||
|
||||
my $narFile = @{$narFileList}[0];
|
||||
die unless defined $narFile;
|
||||
|
||||
$narFile->{url} =~ /\/([^\/]+)$/;
|
||||
die unless defined $1;
|
||||
return "$narPath/$1";
|
||||
}
|
||||
|
||||
|
||||
sub containsPatch {
|
||||
my $patches = shift;
|
||||
my $storePath = shift;
|
||||
my $basePath = shift;
|
||||
my $patchList = $$patches{$storePath};
|
||||
return 0 if !defined $patchList;
|
||||
my $found = 0;
|
||||
foreach my $patch (@{$patchList}) {
|
||||
# !!! baseHash might differ
|
||||
return 1 if $patch->{basePath} eq $basePath;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
sub generatePatches {
|
||||
my ($srcNarFiles, $dstNarFiles, $srcPatches, $dstPatches, $narPath, $patchesPath, $patchesURL, $tmpDir) = @_;
|
||||
|
||||
my %srcOutPaths = findOutputPaths $srcNarFiles;
|
||||
my %dstOutPaths = findOutputPaths $dstNarFiles;
|
||||
|
||||
# For each output path in the destination, see if we need to / can
|
||||
# create a patch.
|
||||
|
||||
print STDERR "creating patches...\n";
|
||||
|
||||
foreach my $p (keys %dstOutPaths) {
|
||||
|
||||
# If exactly the same path already exists in the source, skip it.
|
||||
next if defined $srcOutPaths{$p};
|
||||
|
||||
print " $p\n";
|
||||
|
||||
# If not, then we should find the paths in the source that are
|
||||
# `most' likely to be present on a system that wants to
|
||||
# install this path.
|
||||
|
||||
(my $name, my $version) = getNameVersion $p;
|
||||
next unless defined $name && defined $version;
|
||||
|
||||
my @closest = ();
|
||||
my $closestVersion;
|
||||
my $minDist = -1; # actually, larger means closer
|
||||
|
||||
# Find all source paths with the same name.
|
||||
|
||||
foreach my $q (keys %srcOutPaths) {
|
||||
(my $name2, my $version2) = getNameVersion $q;
|
||||
next unless defined $name2 && defined $version2;
|
||||
|
||||
if ($name eq $name2) {
|
||||
|
||||
my $srcSystem = @{$$dstNarFiles{$p}}[0]->{system};
|
||||
my $dstSystem = @{$$srcNarFiles{$q}}[0]->{system};
|
||||
if (defined $srcSystem && defined $dstSystem && $srcSystem ne $dstSystem) {
|
||||
print " SKIPPING $q due to different systems ($srcSystem vs. $dstSystem)\n";
|
||||
next;
|
||||
}
|
||||
|
||||
# If the sizes differ too much, then skip. This
|
||||
# disambiguates between, e.g., a real component and a
|
||||
# wrapper component (cf. Firefox in Nixpkgs).
|
||||
my $srcSize = @{$$srcNarFiles{$q}}[0]->{size};
|
||||
my $dstSize = @{$$dstNarFiles{$p}}[0]->{size};
|
||||
my $ratio = $srcSize / $dstSize;
|
||||
$ratio = 1 / $ratio if $ratio < 1;
|
||||
# print " SIZE $srcSize $dstSize $ratio $q\n";
|
||||
|
||||
if ($ratio >= 3) {
|
||||
print " SKIPPING $q due to size ratio $ratio ($srcSize vs. $dstSize)\n";
|
||||
next;
|
||||
}
|
||||
|
||||
# If there are multiple matching names, include the
|
||||
# ones with the closest version numbers.
|
||||
my $dist = versionDiff $version, $version2;
|
||||
if ($dist > $minDist) {
|
||||
$minDist = $dist;
|
||||
@closest = ($q);
|
||||
$closestVersion = $version2;
|
||||
} elsif ($dist == $minDist) {
|
||||
push @closest, $q;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (scalar(@closest) == 0) {
|
||||
print " NO BASE: $p\n";
|
||||
next;
|
||||
}
|
||||
|
||||
foreach my $closest (@closest) {
|
||||
|
||||
# Generate a patch between $closest and $p.
|
||||
print STDERR " $p <- $closest\n";
|
||||
|
||||
# If the patch already exists, skip it.
|
||||
if (containsPatch($srcPatches, $p, $closest) ||
|
||||
containsPatch($dstPatches, $p, $closest))
|
||||
{
|
||||
print " skipping, already exists\n";
|
||||
next;
|
||||
}
|
||||
|
||||
my $srcNarBz2 = getNarBz2 $narPath, $srcNarFiles, $closest;
|
||||
my $dstNarBz2 = getNarBz2 $narPath, $dstNarFiles, $p;
|
||||
|
||||
if (! -f $srcNarBz2) {
|
||||
warn "patch source archive $srcNarBz2 is missing\n";
|
||||
next;
|
||||
}
|
||||
|
||||
system("@bunzip2@ < $srcNarBz2 > $tmpDir/A") == 0
|
||||
or die "cannot unpack $srcNarBz2";
|
||||
|
||||
if ((stat "$tmpDir/A")[7] >= $maxNarSize) {
|
||||
print " skipping, source is too large\n";
|
||||
next;
|
||||
}
|
||||
|
||||
system("@bunzip2@ < $dstNarBz2 > $tmpDir/B") == 0
|
||||
or die "cannot unpack $dstNarBz2";
|
||||
|
||||
if ((stat "$tmpDir/B")[7] >= $maxNarSize) {
|
||||
print " skipping, destination is too large\n";
|
||||
next;
|
||||
}
|
||||
|
||||
my $time1 = time();
|
||||
my $res = system("ulimit -t $timeLimit; @libexecdir@/bsdiff $tmpDir/A $tmpDir/B $tmpDir/DIFF");
|
||||
my $time2 = time();
|
||||
if ($res) {
|
||||
warn "binary diff computation aborted after ", $time2 - $time1, " seconds\n";
|
||||
next;
|
||||
}
|
||||
|
||||
my $baseHash = `@bindir@/nix-hash --flat --type $hashAlgo --base32 $tmpDir/A` or die;
|
||||
chomp $baseHash;
|
||||
|
||||
my $narHash = `@bindir@/nix-hash --flat --type $hashAlgo --base32 $tmpDir/B` or die;
|
||||
chomp $narHash;
|
||||
|
||||
my $narDiffHash = `@bindir@/nix-hash --flat --type $hashAlgo --base32 $tmpDir/DIFF` or die;
|
||||
chomp $narDiffHash;
|
||||
|
||||
my $narDiffSize = (stat "$tmpDir/DIFF")[7];
|
||||
my $dstNarBz2Size = (stat $dstNarBz2)[7];
|
||||
|
||||
print " size $narDiffSize; full size $dstNarBz2Size; ", $time2 - $time1, " seconds\n";
|
||||
|
||||
if ($narDiffSize >= $dstNarBz2Size) {
|
||||
print " rejecting; patch bigger than full archive\n";
|
||||
next;
|
||||
}
|
||||
|
||||
if ($narDiffSize / $dstNarBz2Size >= $maxPatchFraction) {
|
||||
print " rejecting; patch too large relative to full archive\n";
|
||||
next;
|
||||
}
|
||||
|
||||
my $finalName = "$narDiffHash.nar-bsdiff";
|
||||
|
||||
if (-e "$patchesPath/$finalName") {
|
||||
print " not copying, already exists\n";
|
||||
}
|
||||
|
||||
else {
|
||||
system("cp '$tmpDir/DIFF' '$patchesPath/$finalName.tmp'") == 0
|
||||
or die "cannot copy diff";
|
||||
rename("$patchesPath/$finalName.tmp", "$patchesPath/$finalName")
|
||||
or die "cannot rename $patchesPath/$finalName.tmp";
|
||||
}
|
||||
|
||||
# Add the patch to the manifest.
|
||||
addPatch $dstPatches, $p,
|
||||
{ url => "$patchesURL/$finalName", hash => "$hashAlgo:$narDiffHash"
|
||||
, size => $narDiffSize, basePath => $closest, baseHash => "$hashAlgo:$baseHash"
|
||||
, narHash => "$hashAlgo:$narHash", patchType => "nar-bsdiff"
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
# Propagate useful patches from $srcPatches to $dstPatches. A patch
|
||||
# is useful if it produces either paths in the $dstNarFiles or paths
|
||||
# that can be used as the base for other useful patches.
|
||||
sub propagatePatches {
|
||||
my ($srcPatches, $dstNarFiles, $dstPatches) = @_;
|
||||
|
||||
print STDERR "propagating patches...\n";
|
||||
|
||||
my $changed;
|
||||
do {
|
||||
# !!! we repeat this to reach the transitive closure; inefficient
|
||||
$changed = 0;
|
||||
|
||||
print STDERR "loop\n";
|
||||
|
||||
my %dstBasePaths;
|
||||
foreach my $q (keys %{$dstPatches}) {
|
||||
foreach my $patch (@{$$dstPatches{$q}}) {
|
||||
$dstBasePaths{$patch->{basePath}} = 1;
|
||||
}
|
||||
}
|
||||
|
||||
foreach my $p (keys %{$srcPatches}) {
|
||||
my $patchList = $$srcPatches{$p};
|
||||
|
||||
my $include = 0;
|
||||
|
||||
# Is path $p included in the destination? If so, include
|
||||
# patches that produce it.
|
||||
$include = 1 if defined $$dstNarFiles{$p};
|
||||
|
||||
# Is path $p a path that serves as a base for paths in the
|
||||
# destination? If so, include patches that produce it.
|
||||
# !!! check baseHash
|
||||
$include = 1 if defined $dstBasePaths{$p};
|
||||
|
||||
if ($include) {
|
||||
foreach my $patch (@{$patchList}) {
|
||||
$changed = 1 if addPatch $dstPatches, $p, $patch;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
} while $changed;
|
||||
}
|
||||
|
||||
|
||||
# Add all new patches in $srcPatches to $dstPatches.
|
||||
sub copyPatches {
|
||||
my ($srcPatches, $dstPatches) = @_;
|
||||
foreach my $p (keys %{$srcPatches}) {
|
||||
addPatch $dstPatches, $p, $_ foreach @{$$srcPatches{$p}};
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return 1;
|
||||
@@ -1,24 +1,27 @@
|
||||
bin_SCRIPTS = nix-collect-garbage \
|
||||
nix-pull nix-push nix-prefetch-url \
|
||||
nix-install-package nix-channel nix-build \
|
||||
nix-pack-closure nix-unpack-closure \
|
||||
nix-copy-closure
|
||||
nix-copy-closure nix-generate-patches
|
||||
|
||||
noinst_SCRIPTS = nix-profile.sh generate-patches.pl \
|
||||
find-runtime-roots.pl build-remote.pl
|
||||
noinst_SCRIPTS = nix-profile.sh GeneratePatches.pm \
|
||||
find-runtime-roots.pl build-remote.pl nix-reduce-build \
|
||||
copy-from-other-stores.pl nix-http-export.cgi
|
||||
|
||||
nix-pull nix-push: readmanifest.pm readconfig.pm download-using-manifests.pl
|
||||
nix-pull nix-push: NixManifest.pm NixConfig.pm download-using-manifests.pl
|
||||
|
||||
install-exec-local: readmanifest.pm download-using-manifests.pl find-runtime-roots.pl
|
||||
install-exec-local: NixManifest.pm GeneratePatches.pm download-using-manifests.pl copy-from-other-stores.pl find-runtime-roots.pl
|
||||
$(INSTALL) -d $(DESTDIR)$(sysconfdir)/profile.d
|
||||
$(INSTALL_PROGRAM) nix-profile.sh $(DESTDIR)$(sysconfdir)/profile.d/nix.sh
|
||||
$(INSTALL) -d $(DESTDIR)$(libexecdir)/nix
|
||||
$(INSTALL_DATA) readmanifest.pm $(DESTDIR)$(libexecdir)/nix
|
||||
$(INSTALL_DATA) readconfig.pm $(DESTDIR)$(libexecdir)/nix
|
||||
$(INSTALL_PROGRAM) download-using-manifests.pl $(DESTDIR)$(libexecdir)/nix
|
||||
$(INSTALL_DATA) NixManifest.pm $(DESTDIR)$(libexecdir)/nix
|
||||
$(INSTALL_DATA) NixConfig.pm $(DESTDIR)$(libexecdir)/nix
|
||||
$(INSTALL_DATA) SSH.pm $(DESTDIR)$(libexecdir)/nix
|
||||
$(INSTALL_DATA) GeneratePatches.pm $(DESTDIR)$(libexecdir)/nix
|
||||
$(INSTALL_PROGRAM) find-runtime-roots.pl $(DESTDIR)$(libexecdir)/nix
|
||||
$(INSTALL_PROGRAM) generate-patches.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) -d $(DESTDIR)$(sysconfdir)/nix
|
||||
|
||||
include ../substitute.mk
|
||||
@@ -27,12 +30,16 @@ EXTRA_DIST = nix-collect-garbage.in \
|
||||
nix-pull.in nix-push.in nix-profile.sh.in \
|
||||
nix-prefetch-url.in nix-install-package.in \
|
||||
nix-channel.in \
|
||||
readmanifest.pm.in \
|
||||
readconfig.pm.in \
|
||||
NixManifest.pm.in \
|
||||
NixConfig.pm.in \
|
||||
SSH.pm \
|
||||
GeneratePatches.pm.in \
|
||||
nix-build.in \
|
||||
download-using-manifests.pl.in \
|
||||
generate-patches.pl.in \
|
||||
nix-pack-closure.in nix-unpack-closure.in \
|
||||
copy-from-other-stores.pl.in \
|
||||
nix-copy-closure.in \
|
||||
find-runtime-roots.pl.in \
|
||||
build-remote.pl.in
|
||||
build-remote.pl.in \
|
||||
nix-reduce-build.in \
|
||||
nix-http-export.cgi.in \
|
||||
nix-generate-patches.in
|
||||
|
||||
@@ -2,10 +2,7 @@ use strict;
|
||||
|
||||
|
||||
sub addPatch {
|
||||
my $patches = shift;
|
||||
my $storePath = shift;
|
||||
my $patch = shift;
|
||||
my $allowConflicts = shift;
|
||||
my ($patches, $storePath, $patch) = @_;
|
||||
|
||||
$$patches{$storePath} = []
|
||||
unless defined $$patches{$storePath};
|
||||
@@ -14,15 +11,9 @@ sub addPatch {
|
||||
|
||||
my $found = 0;
|
||||
foreach my $patch2 (@{$patchList}) {
|
||||
if ($patch2->{url} eq $patch->{url}) {
|
||||
if ($patch2->{hash} eq $patch->{hash}) {
|
||||
$found = 1 if ($patch2->{basePath} eq $patch->{basePath});
|
||||
} else {
|
||||
die "conflicting hashes for URL $patch->{url}, " .
|
||||
"namely $patch2->{hash} and $patch->{hash}"
|
||||
unless $allowConflicts;
|
||||
}
|
||||
}
|
||||
$found = 1 if
|
||||
$patch2->{url} eq $patch->{url} &&
|
||||
$patch2->{basePath} eq $patch->{basePath};
|
||||
}
|
||||
|
||||
push @{$patchList}, $patch if !$found;
|
||||
@@ -32,12 +23,7 @@ sub addPatch {
|
||||
|
||||
|
||||
sub readManifest {
|
||||
my $manifest = shift;
|
||||
my $narFiles = shift;
|
||||
my $localPaths = shift;
|
||||
my $patches = shift;
|
||||
my $allowConflicts = shift;
|
||||
$allowConflicts = 0 unless defined $allowConflicts;
|
||||
my ($manifest, $narFiles, $localPaths, $patches) = @_;
|
||||
|
||||
open MANIFEST, "<$manifest"
|
||||
or die "cannot open `$manifest': $!";
|
||||
@@ -47,18 +33,8 @@ sub readManifest {
|
||||
|
||||
my $manifestVersion = 2;
|
||||
|
||||
my $storePath;
|
||||
my $url;
|
||||
my $hash;
|
||||
my $size;
|
||||
my $basePath;
|
||||
my $baseHash;
|
||||
my $patchType;
|
||||
my $narHash;
|
||||
my $references;
|
||||
my $deriver;
|
||||
my $hashAlgo;
|
||||
my $copyFrom;
|
||||
my ($storePath, $url, $hash, $size, $basePath, $baseHash, $patchType);
|
||||
my ($narHash, $narSize, $references, $deriver, $hashAlgo, $copyFrom, $system);
|
||||
|
||||
while (<MANIFEST>) {
|
||||
chomp;
|
||||
@@ -76,9 +52,11 @@ sub readManifest {
|
||||
undef $hash;
|
||||
undef $size;
|
||||
undef $narHash;
|
||||
undef $narSize;
|
||||
undef $basePath;
|
||||
undef $baseHash;
|
||||
undef $patchType;
|
||||
undef $system;
|
||||
$references = "";
|
||||
$deriver = "";
|
||||
$hashAlgo = "md5";
|
||||
@@ -98,21 +76,15 @@ sub readManifest {
|
||||
|
||||
my $found = 0;
|
||||
foreach my $narFile (@{$narFileList}) {
|
||||
if ($narFile->{url} eq $url) {
|
||||
if ($narFile->{hash} eq $hash) {
|
||||
$found = 1;
|
||||
} else {
|
||||
die "conflicting hashes for URL $url, " .
|
||||
"namely $narFile->{hash} and $hash"
|
||||
unless $allowConflicts;
|
||||
}
|
||||
}
|
||||
$found = 1 if $narFile->{url} eq $url;
|
||||
}
|
||||
if (!$found) {
|
||||
push @{$narFileList},
|
||||
{ url => $url, hash => $hash, size => $size
|
||||
, narHash => $narHash, references => $references
|
||||
, narHash => $narHash, narSize => $narSize
|
||||
, references => $references
|
||||
, deriver => $deriver, hashAlgo => $hashAlgo
|
||||
, system => $system
|
||||
};
|
||||
}
|
||||
|
||||
@@ -122,9 +94,9 @@ sub readManifest {
|
||||
addPatch $patches, $storePath,
|
||||
{ url => $url, hash => $hash, size => $size
|
||||
, basePath => $basePath, baseHash => $baseHash
|
||||
, narHash => $narHash, patchType => $patchType
|
||||
, hashAlgo => $hashAlgo
|
||||
}, $allowConflicts;
|
||||
, narHash => $narHash, narSize => $narSize
|
||||
, patchType => $patchType, hashAlgo => $hashAlgo
|
||||
};
|
||||
}
|
||||
|
||||
elsif ($type eq "localPath") {
|
||||
@@ -154,9 +126,11 @@ sub readManifest {
|
||||
elsif (/^\s*BaseHash:\s*(\S+)\s*$/) { $baseHash = $1; }
|
||||
elsif (/^\s*Type:\s*(\S+)\s*$/) { $patchType = $1; }
|
||||
elsif (/^\s*NarHash:\s*(\S+)\s*$/) { $narHash = $1; }
|
||||
elsif (/^\s*NarSize:\s*(\d+)\s*$/) { $narSize = $1; }
|
||||
elsif (/^\s*References:\s*(.*)\s*$/) { $references = $1; }
|
||||
elsif (/^\s*Deriver:\s*(\S+)\s*$/) { $deriver = $1; }
|
||||
elsif (/^\s*ManifestVersion:\s*(\d+)\s*$/) { $manifestVersion = $1; }
|
||||
elsif (/^\s*System:\s*(\S+)\s*$/) { $system = $1; }
|
||||
|
||||
# Compatibility;
|
||||
elsif (/^\s*NarURL:\s*(\S+)\s*$/) { $url = $1; }
|
||||
@@ -171,12 +145,8 @@ sub readManifest {
|
||||
}
|
||||
|
||||
|
||||
sub writeManifest
|
||||
{
|
||||
my $manifest = shift;
|
||||
my $narFiles = shift;
|
||||
my $patches = shift;
|
||||
my $copySources = shift;
|
||||
sub writeManifest {
|
||||
my ($manifest, $narFiles, $patches, $noCompress) = @_;
|
||||
|
||||
open MANIFEST, ">$manifest.tmp"; # !!! check exclusive
|
||||
|
||||
@@ -190,13 +160,15 @@ sub writeManifest
|
||||
print MANIFEST "{\n";
|
||||
print MANIFEST " StorePath: $storePath\n";
|
||||
print MANIFEST " NarURL: $narFile->{url}\n";
|
||||
print MANIFEST " Hash: $narFile->{hash}\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";
|
||||
print MANIFEST " Size: $narFile->{size}\n";
|
||||
print MANIFEST " NarSize: $narFile->{narSize}\n" if $narFile->{narSize};
|
||||
print MANIFEST " References: $narFile->{references}\n"
|
||||
if defined $narFile->{references} && $narFile->{references} ne "";
|
||||
print MANIFEST " Deriver: $narFile->{deriver}\n"
|
||||
if defined $narFile->{deriver} && $narFile->{deriver} ne "";
|
||||
print MANIFEST " System: $narFile->{system}\n" if defined $narFile->{system};
|
||||
print MANIFEST "}\n";
|
||||
}
|
||||
}
|
||||
@@ -208,8 +180,9 @@ sub writeManifest
|
||||
print MANIFEST " StorePath: $storePath\n";
|
||||
print MANIFEST " NarURL: $patch->{url}\n";
|
||||
print MANIFEST " Hash: $patch->{hash}\n";
|
||||
print MANIFEST " NarHash: $patch->{narHash}\n";
|
||||
print MANIFEST " Size: $patch->{size}\n";
|
||||
print MANIFEST " NarHash: $patch->{narHash}\n";
|
||||
print MANIFEST " NarSize: $patch->{narSize}\n" if $patch->{narSize};
|
||||
print MANIFEST " BasePath: $patch->{basePath}\n";
|
||||
print MANIFEST " BaseHash: $patch->{baseHash}\n";
|
||||
print MANIFEST " Type: $patch->{patchType}\n";
|
||||
@@ -225,11 +198,13 @@ sub writeManifest
|
||||
|
||||
|
||||
# Create a bzipped manifest.
|
||||
system("@bzip2@ < $manifest > $manifest.bz2.tmp") == 0
|
||||
or die "cannot compress manifest";
|
||||
unless (defined $noCompress) {
|
||||
system("@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: $!";
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
52
scripts/SSH.pm
Normal file
52
scripts/SSH.pm
Normal file
@@ -0,0 +1,52 @@
|
||||
use strict;
|
||||
use File::Temp qw(tempdir);
|
||||
|
||||
our @sshOpts = split ' ', ($ENV{"NIX_SSHOPTS"} or "");
|
||||
|
||||
push @sshOpts, "-x";
|
||||
|
||||
my $sshStarted = 0;
|
||||
my $sshHost;
|
||||
|
||||
# Open a master SSH connection to `host', unless there already is a
|
||||
# running master connection (as determined by `-O check').
|
||||
sub openSSHConnection {
|
||||
my ($host) = @_;
|
||||
die if $sshStarted;
|
||||
$sshHost = $host;
|
||||
return 1 if system("ssh $sshHost @sshOpts -O check 2> /dev/null") == 0;
|
||||
|
||||
my $tmpDir = tempdir("nix-ssh.XXXXXX", CLEANUP => 1, TMPDIR => 1)
|
||||
or die "cannot create a temporary directory";
|
||||
|
||||
push @sshOpts, "-S", "$tmpDir/control";
|
||||
|
||||
# Start the master. We can't use the `-f' flag (fork into
|
||||
# background after establishing the connection) because then the
|
||||
# child continues to run if we are killed. So instead make SSH
|
||||
# print "started" when it has established the connection, and wait
|
||||
# until we see that.
|
||||
open SSHPIPE, "ssh $sshHost @sshOpts -M -N -o LocalCommand='echo started' -o PermitLocalCommand=yes |" or die;
|
||||
|
||||
while (<SSHPIPE>) {
|
||||
chomp;
|
||||
if ($_ eq "started") {
|
||||
$sshStarted = 1;
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
# Tell the master SSH client to exit.
|
||||
sub closeSSHConnection {
|
||||
if ($sshStarted) {
|
||||
system("ssh $sshHost @sshOpts -O exit 2> /dev/null") == 0
|
||||
or warn "unable to stop SSH master: $?";
|
||||
}
|
||||
}
|
||||
|
||||
END { my $saved = $?; closeSSHConnection; $? = $saved; }
|
||||
|
||||
return 1;
|
||||
@@ -1,8 +1,11 @@
|
||||
#! @perl@ -w
|
||||
#! @perl@ -w -I@libexecdir@/nix
|
||||
|
||||
use strict;
|
||||
use Fcntl ':flock';
|
||||
use English '-no_match_vars';
|
||||
use IO::Handle;
|
||||
use SSH qw/sshOpts openSSHConnection/;
|
||||
no warnings('once');
|
||||
|
||||
|
||||
# General operation:
|
||||
#
|
||||
@@ -21,188 +24,246 @@ use English '-no_match_vars';
|
||||
# The nice thing about this scheme is that if we die prematurely, the
|
||||
# locks are released automatically.
|
||||
|
||||
my $loadIncreased = 0;
|
||||
|
||||
my $amWilling = shift @ARGV;
|
||||
my $localSystem = shift @ARGV;
|
||||
my $neededSystem = shift @ARGV;
|
||||
my $drvPath = shift @ARGV;
|
||||
|
||||
sub sendReply {
|
||||
my $reply = shift;
|
||||
open OUT, ">&3" or die;
|
||||
print OUT "$reply\n";
|
||||
close OUT;
|
||||
}
|
||||
|
||||
sub decline {
|
||||
sendReply "decline";
|
||||
exit 0;
|
||||
}
|
||||
|
||||
my $currentLoad = $ENV{"NIX_CURRENT_LOAD"};
|
||||
decline unless defined $currentLoad;
|
||||
mkdir $currentLoad, 0777 or die unless -d $currentLoad;
|
||||
|
||||
my $conf = $ENV{"NIX_REMOTE_SYSTEMS"};
|
||||
decline if !defined $conf || ! -e $conf;
|
||||
|
||||
# Decline if the local system can do the build.
|
||||
decline if $amWilling && ($localSystem eq $neededSystem);
|
||||
|
||||
|
||||
# Otherwise find a willing remote machine.
|
||||
my %machines;
|
||||
my %systemTypes;
|
||||
my %sshKeys;
|
||||
my %maxJobs;
|
||||
my %curJobs;
|
||||
|
||||
|
||||
# Read the list of machines.
|
||||
open CONF, "< $conf" or die;
|
||||
|
||||
while (<CONF>) {
|
||||
chomp;
|
||||
s/\#.*$//g;
|
||||
next if /^\s*$/;
|
||||
/^\s*(\S+)\s+(\S+)\s+(\S+)\s+(\d+)\s*$/ or die;
|
||||
$machines{$1} = "";
|
||||
$systemTypes{$1} = $2;
|
||||
$sshKeys{$1} = $3;
|
||||
$maxJobs{$1} = $4;
|
||||
}
|
||||
|
||||
close CONF;
|
||||
|
||||
|
||||
# Acquire the exclusive lock on $currentLoad/main-lock.
|
||||
my $mainLock = "$currentLoad/main-lock";
|
||||
open MAINLOCK, ">>$mainLock" or die;
|
||||
flock(MAINLOCK, LOCK_EX) or die;
|
||||
|
||||
|
||||
# Find a suitable system.
|
||||
my $rightType = 0;
|
||||
my $machine;
|
||||
LOOP: foreach my $cur (keys %machines) {
|
||||
if ($neededSystem eq $systemTypes{$cur}) {
|
||||
$rightType = 1;
|
||||
|
||||
# We have a machine of the right type. Try to get a lock on
|
||||
# one of the machine's lock files.
|
||||
my $slot = 0;
|
||||
while ($slot < $maxJobs{$cur}) {
|
||||
my $slotLock = "$currentLoad/$cur-$slot";
|
||||
open SLOTLOCK, ">>$slotLock" or die;
|
||||
if (flock(SLOTLOCK, LOCK_EX | LOCK_NB)) {
|
||||
$machine = $cur;
|
||||
last LOOP;
|
||||
}
|
||||
close SLOTLOCK;
|
||||
$slot++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
close MAINLOCK;
|
||||
|
||||
|
||||
# Didn't find one?
|
||||
if (!defined $machine) {
|
||||
if ($rightType) {
|
||||
sendReply "postpone";
|
||||
exit 0;
|
||||
} else {
|
||||
decline;
|
||||
}
|
||||
}
|
||||
|
||||
# Yes we did, accept.
|
||||
sendReply "accept";
|
||||
open IN, "<&4" or die;
|
||||
my $x = <IN>;
|
||||
chomp $x;
|
||||
#print "got $x\n";
|
||||
close IN;
|
||||
|
||||
if ($x ne "okay") {
|
||||
exit 0;
|
||||
}
|
||||
|
||||
|
||||
# Do the actual job.
|
||||
print "BUILDING REMOTE: $drvPath on $machine\n";
|
||||
|
||||
# Make sure that we don't get any SSH passphrase or host key popups -
|
||||
# if there is any problem it should fail, not do something
|
||||
# interactive.
|
||||
$ENV{"DISPLAY"} = "";
|
||||
$ENV{"SSH_PASSWORD_FILE="} = "";
|
||||
$ENV{"SSH_ASKPASS="} = "";
|
||||
$ENV{"SSH_ASKPASS"} = "";
|
||||
|
||||
my $sshOpts = "-i $sshKeys{$machine} -x";
|
||||
|
||||
# Hack to support Cygwin: if we login without a password, we don't
|
||||
# have exactly the same right as when we do. This causes the
|
||||
# Microsoft C compiler to fail with certain flags:
|
||||
#
|
||||
# http://connect.microsoft.com/VisualStudio/feedback/ViewFeedback.aspx?FeedbackID=99676
|
||||
#
|
||||
# So as a workaround, we pass a verbatim password. ssh tries to makes
|
||||
# this very hard; the trick is to make it call SSH_ASKPASS to get the
|
||||
# password. (It only calls this command when there is no controlling
|
||||
# terminal, but Nix ensures that is is the case. When doing this
|
||||
# manually, use setsid(1).)
|
||||
if ($sshKeys{$machine} =~ /^password:/) {
|
||||
my $passwordFile = $sshKeys{$machine};
|
||||
$passwordFile =~ s/^password://;
|
||||
$sshOpts = "ssh -x";
|
||||
$ENV{"SSH_PASSWORD_FILE"} = $passwordFile;
|
||||
$ENV{"SSH_ASKPASS"} = "/tmp/writepass";
|
||||
|
||||
open WRITEPASS, ">/tmp/writepass" or die;
|
||||
print WRITEPASS "#! /bin/sh\ncat \"\$SSH_PASSWORD_FILE\"";
|
||||
close WRITEPASS;
|
||||
chmod 0755, "/tmp/writepass" or die;
|
||||
sub sendReply {
|
||||
my $reply = shift;
|
||||
print STDERR "# $reply\n";
|
||||
}
|
||||
|
||||
my $inputs = `cat inputs`; die if ($? != 0);
|
||||
$inputs =~ s/\n/ /g;
|
||||
sub all { $_ || return 0 for @_; 1 }
|
||||
|
||||
my $outputs = `cat outputs`; die if ($? != 0);
|
||||
$outputs =~ s/\n/ /g;
|
||||
|
||||
print "COPYING INPUTS...\n";
|
||||
# Initialisation.
|
||||
my $loadIncreased = 0;
|
||||
|
||||
my ($localSystem, $maxSilentTime, $printBuildTrace) = @ARGV;
|
||||
$maxSilentTime = 0 unless defined $maxSilentTime;
|
||||
|
||||
my $currentLoad = $ENV{"NIX_CURRENT_LOAD"};
|
||||
my $conf = $ENV{"NIX_REMOTE_SYSTEMS"};
|
||||
|
||||
|
||||
sub openSlotLock {
|
||||
my ($machine, $slot) = @_;
|
||||
my $slotLockFn = "$currentLoad/" . (join '+', @{$machine->{systemTypes}}) . "-" . $machine->{hostName} . "-$slot";
|
||||
my $slotLock = new IO::Handle;
|
||||
open $slotLock, ">>$slotLockFn" or die;
|
||||
return $slotLock;
|
||||
}
|
||||
|
||||
|
||||
# Read the list of machines.
|
||||
my @machines;
|
||||
if (defined $conf && -e $conf) {
|
||||
open CONF, "< $conf" or die;
|
||||
while (<CONF>) {
|
||||
chomp;
|
||||
s/\#.*$//g;
|
||||
next if /^\s*$/;
|
||||
my @tokens = split /\s/, $_;
|
||||
push @machines,
|
||||
{ hostName => $tokens[0]
|
||||
, systemTypes => [ split(/,/, $tokens[1]) ]
|
||||
, sshKeys => $tokens[2]
|
||||
, maxJobs => int($tokens[3])
|
||||
, speedFactor => 1.0 * (defined $tokens[4] ? int($tokens[4]) : 1)
|
||||
, features => [ split(/,/, $tokens[5] || "") ]
|
||||
, enabled => 1
|
||||
};
|
||||
}
|
||||
close CONF;
|
||||
}
|
||||
|
||||
|
||||
|
||||
# Wait for the calling process to ask us whether we can build some derivation.
|
||||
my ($drvPath, $hostName, $slotLock);
|
||||
|
||||
REQ: while (1) {
|
||||
$_ = <STDIN> || exit 0;
|
||||
my ($amWilling, $neededSystem);
|
||||
($amWilling, $neededSystem, $drvPath, $requiredFeatures) = split;
|
||||
my @requiredFeatures = split /,/, $requiredFeatures;
|
||||
|
||||
my $canBuildLocally = $amWilling && ($localSystem eq $neededSystem);
|
||||
|
||||
if (!defined $currentLoad) {
|
||||
sendReply "decline";
|
||||
next;
|
||||
}
|
||||
|
||||
# Acquire the exclusive lock on $currentLoad/main-lock.
|
||||
mkdir $currentLoad, 0777 or die unless -d $currentLoad;
|
||||
my $mainLock = "$currentLoad/main-lock";
|
||||
open MAINLOCK, ">>$mainLock" 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
|
||||
# not at their job limit.
|
||||
my $rightType = 0;
|
||||
my @available = ();
|
||||
LOOP: foreach my $cur (@machines) {
|
||||
if ($cur->{enabled}
|
||||
&& (grep { $neededSystem eq $_ } @{$cur->{systemTypes}})
|
||||
&& all(map { my $f = $_; 0 != grep { $f eq $_ } @{$cur->{features}} } @requiredFeatures))
|
||||
{
|
||||
$rightType = 1;
|
||||
|
||||
# We have a machine of the right type. Determine the load on
|
||||
# the machine.
|
||||
my $slot = 0;
|
||||
my $load = 0;
|
||||
my $free;
|
||||
while ($slot < $cur->{maxJobs}) {
|
||||
my $slotLock = openSlotLock($cur, $slot);
|
||||
if (flock($slotLock, LOCK_EX | LOCK_NB)) {
|
||||
$free = $slot unless defined $free;
|
||||
flock($slotLock, LOCK_UN) or die;
|
||||
} else {
|
||||
$load++;
|
||||
}
|
||||
close $slotLock;
|
||||
$slot++;
|
||||
}
|
||||
|
||||
push @available, { machine => $cur, load => $load, free => $free }
|
||||
if $load < $cur->{maxJobs};
|
||||
}
|
||||
}
|
||||
|
||||
if (defined $ENV{NIX_DEBUG_HOOK}) {
|
||||
print STDERR "load on " . $_->{machine}->{hostName} . " = " . $_->{load} . "\n"
|
||||
foreach @available;
|
||||
}
|
||||
|
||||
|
||||
# Didn't find any available machine? Then decline or postpone.
|
||||
if (scalar @available == 0) {
|
||||
# Postpone if we have a machine of the right type, except
|
||||
# if the local system can and wants to do the build.
|
||||
if ($rightType && !$canBuildLocally) {
|
||||
sendReply "postpone";
|
||||
} else {
|
||||
sendReply "decline";
|
||||
}
|
||||
close MAINLOCK;
|
||||
next REQ;
|
||||
}
|
||||
|
||||
|
||||
# Prioritise the available machines as follows:
|
||||
# - First by load divided by speed factor, rounded to the nearest
|
||||
# integer. This causes fast machines to be preferred over slow
|
||||
# machines with similar loads.
|
||||
# - Then by speed factor.
|
||||
# - Finally by load.
|
||||
sub lf { my $x = shift; return int($x->{load} / $x->{machine}->{speedFactor} + 0.4999); }
|
||||
@available = sort
|
||||
{ lf($a) <=> lf($b)
|
||||
|| $b->{machine}->{speedFactor} <=> $a->{machine}->{speedFactor}
|
||||
|| $a->{load} <=> $b->{load}
|
||||
} @available;
|
||||
|
||||
|
||||
# Select the best available machine and lock a free slot.
|
||||
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;
|
||||
|
||||
close MAINLOCK;
|
||||
|
||||
|
||||
# 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";
|
||||
$machine->{enabled} = 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
# Tell Nix we've accepted the build.
|
||||
sendReply "accept";
|
||||
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;
|
||||
|
||||
|
||||
my $maybeSign = "";
|
||||
$maybeSign = "--sign" if -e "/nix/etc/nix/signing-key.sec";
|
||||
|
||||
system("NIX_SSHOPTS=\"$sshOpts\" nix-copy-closure $machine $maybeSign $drvPath $inputs") == 0
|
||||
or die "cannot copy inputs to $machine: $?";
|
||||
|
||||
print "BUILDING...\n";
|
||||
# Register the derivation as a temporary GC root. Note that $PPID is
|
||||
# the PID of the remote SSH process, which, due to the use of a
|
||||
# persistant SSH connection, should be the same across all remote
|
||||
# command invocations for this session.
|
||||
my $rootsDir = "@localstatedir@/nix/gcroots/tmp";
|
||||
system("ssh $hostName @sshOpts 'mkdir -m 1777 -p $rootsDir; ln -sfn $drvPath $rootsDir/\$PPID.drv'");
|
||||
|
||||
system("ssh $sshOpts $machine 'nix-store -rvvK $drvPath'") == 0
|
||||
or die "remote build on $machine failed: $?";
|
||||
sub removeRoots {
|
||||
system("ssh $hostName @sshOpts 'rm -f $rootsDir/\$PPID.drv $rootsDir/\$PPID.out'");
|
||||
}
|
||||
|
||||
print "REMOTE BUILD DONE: $drvPath on $machine\n";
|
||||
|
||||
foreach my $output (split '\n', $outputs) {
|
||||
# Copy the derivation and its dependencies to the build machine.
|
||||
system("NIX_SSHOPTS=\"@sshOpts\" @bindir@/nix-copy-closure $hostName $maybeSign $drvPath @inputs") == 0
|
||||
or die "cannot copy inputs to $hostName: $?";
|
||||
|
||||
|
||||
# Perform the build.
|
||||
my $buildFlags = "--max-silent-time $maxSilentTime --fallback --add-root $rootsDir/\$PPID.out --option verbosity 0";
|
||||
|
||||
# We let the remote side kill its process group when the connection is
|
||||
# closed unexpectedly. This is necessary to ensure that no processes
|
||||
# are left running on the remote system if the local Nix process is
|
||||
# killed. (SSH itself doesn't kill child processes if the connection
|
||||
# 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.)
|
||||
pipe STDIN, DUMMY; # make sure we have a readable STDIN
|
||||
if (system("ssh $hostName @sshOpts '(read; kill -INT -\$\$) <&0 & 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
|
||||
# allow it to distinguish between transient and permanent
|
||||
# failures.
|
||||
my $res = $? >> 8;
|
||||
print STDERR "build of `$drvPath' on `$hostName' failed with exit code $res\n";
|
||||
removeRoots;
|
||||
exit $res;
|
||||
}
|
||||
|
||||
#print "build of `$drvPath' on `$hostName' succeeded\n";
|
||||
|
||||
|
||||
# Copy the output from the build machine.
|
||||
foreach my $output (@outputs) {
|
||||
my $maybeSignRemote = "";
|
||||
$maybeSignRemote = "--sign" if $UID != 0;
|
||||
|
||||
system("ssh $sshOpts $machine 'nix-store --export $maybeSignRemote $output' > dump") == 0
|
||||
or die "cannot copy $output from $machine: $?";
|
||||
|
||||
# This doesn't work yet, since the caller has a lock on the output
|
||||
# path. We should move towards lock-free invocation of build
|
||||
# hooks and substitutes.
|
||||
#system("nix-store --import < dump") == 0
|
||||
# or die "cannot import $output: $?";
|
||||
|
||||
# Hack: skip the first 8 bytes (the nix-store --export next
|
||||
# archive marker). The archive follows.
|
||||
system("(dd bs=1 count=8 of=/dev/null && cat) < dump | nix-store --restore $output") == 0
|
||||
or die "cannot restore $output: $?";
|
||||
system("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: $?";
|
||||
}
|
||||
|
||||
|
||||
# Get rid of the temporary GC roots.
|
||||
removeRoots;
|
||||
|
||||
93
scripts/copy-from-other-stores.pl.in
Normal file
93
scripts/copy-from-other-stores.pl.in
Normal file
@@ -0,0 +1,93 @@
|
||||
#! @perl@ -w
|
||||
|
||||
use strict;
|
||||
use File::Basename;
|
||||
use IO::Handle;
|
||||
|
||||
my $binDir = $ENV{"NIX_BIN_DIR"} || "@bindir@";
|
||||
|
||||
|
||||
STDOUT->autoflush(1);
|
||||
|
||||
my @remoteStoresAll = split ':', ($ENV{"NIX_OTHER_STORES"} or "");
|
||||
|
||||
my @remoteStores;
|
||||
foreach my $dir (@remoteStoresAll) {
|
||||
push @remoteStores, glob($dir);
|
||||
}
|
||||
|
||||
|
||||
$ENV{"NIX_REMOTE"} = "";
|
||||
|
||||
|
||||
sub findStorePath {
|
||||
my $storePath = shift;
|
||||
foreach my $store (@remoteStores) {
|
||||
my $sourcePath = "$store/store/" . basename $storePath;
|
||||
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;
|
||||
}
|
||||
return undef;
|
||||
}
|
||||
|
||||
|
||||
if ($ARGV[0] eq "--query") {
|
||||
|
||||
while (<STDIN>) {
|
||||
my $cmd = $_; chomp $cmd;
|
||||
|
||||
if ($cmd eq "have") {
|
||||
my $storePath = <STDIN>; chomp $storePath;
|
||||
print STDOUT (defined findStorePath($storePath) ? "1\n" : "0\n");
|
||||
}
|
||||
|
||||
elsif ($cmd eq "info") {
|
||||
my $storePath = <STDIN>; chomp $storePath;
|
||||
my ($store, $sourcePath) = findStorePath($storePath);
|
||||
if (!defined $store) {
|
||||
print "0\n";
|
||||
next; # not an error
|
||||
}
|
||||
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";
|
||||
}
|
||||
|
||||
else { die "unknown command `$cmd'"; }
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
elsif ($ARGV[0] eq "--substitute") {
|
||||
die unless scalar @ARGV == 2;
|
||||
my $storePath = $ARGV[1];
|
||||
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
|
||||
or die "cannot copy `$sourcePath' to `$storePath'";
|
||||
}
|
||||
|
||||
|
||||
else { die; }
|
||||
@@ -1,23 +1,20 @@
|
||||
#! @perl@ -w -I@libexecdir@/nix
|
||||
|
||||
use strict;
|
||||
use readmanifest;
|
||||
use NixManifest;
|
||||
use POSIX qw(strftime);
|
||||
use File::Temp qw(tempdir);
|
||||
|
||||
my $manifestDir = "@localstatedir@/nix/manifests";
|
||||
my $binDir = $ENV{"NIX_BIN_DIR"} || "@bindir@";
|
||||
|
||||
STDOUT->autoflush(1);
|
||||
|
||||
my $manifestDir = ($ENV{"NIX_MANIFESTS_DIR"} or "@localstatedir@/nix/manifests");
|
||||
my $logFile = "@localstatedir@/log/nix/downloads";
|
||||
|
||||
open LOGFILE, ">>$logFile" or die "cannot open log file $logFile";
|
||||
|
||||
# Create a temporary directory.
|
||||
my $tmpDir = tempdir("nix-download.XXXXXX", CLEANUP => 1, TMPDIR => 1)
|
||||
or die "cannot create a temporary directory";
|
||||
|
||||
chdir $tmpDir or die "cannot change to `$tmpDir': $!";
|
||||
|
||||
my $tmpNar = "$tmpDir/nar";
|
||||
my $tmpNar2 = "$tmpDir/nar2";
|
||||
# For queries, skip expensive calls to nix-hash etc. We're just
|
||||
# estimating the expected download size.
|
||||
my $fast = 1;
|
||||
|
||||
|
||||
# Load all manifests.
|
||||
@@ -26,56 +23,244 @@ my %localPaths;
|
||||
my %patches;
|
||||
|
||||
for my $manifest (glob "$manifestDir/*.nixmanifest") {
|
||||
# print STDERR "reading $manifest\n";
|
||||
if (readManifest($manifest, \%narFiles, \%localPaths, \%patches) < 3) {
|
||||
my $version = readManifest($manifest, \%narFiles, \%localPaths, \%patches);
|
||||
if ($version < 3) {
|
||||
print STDERR "you have an old-style manifest `$manifest'; please delete it\n";
|
||||
exit 1;
|
||||
}
|
||||
if ($version >= 10) {
|
||||
print STDERR "manifest `$manifest' is too new; please delete it or upgrade Nix\n";
|
||||
exit 1;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
# Parse the arguments.
|
||||
|
||||
if ($ARGV[0] eq "--query-paths") {
|
||||
foreach my $storePath (keys %narFiles) { print "$storePath\n"; }
|
||||
foreach my $storePath (keys %localPaths) { print "$storePath\n"; }
|
||||
exit 0;
|
||||
sub isValidPath {
|
||||
my $p = shift;
|
||||
if ($fast) {
|
||||
return -e $p;
|
||||
} else {
|
||||
return system("$binDir/nix-store --check-validity '$p' 2> /dev/null") == 0;
|
||||
}
|
||||
}
|
||||
|
||||
elsif ($ARGV[0] eq "--query-info") {
|
||||
shift @ARGV;
|
||||
foreach my $storePath (@ARGV) {
|
||||
my $info;
|
||||
if (defined $narFiles{$storePath}) {
|
||||
$info = @{$narFiles{$storePath}}[0];
|
||||
}
|
||||
elsif (defined $localPaths{$storePath}) {
|
||||
$info = @{$localPaths{$storePath}}[0];
|
||||
|
||||
sub parseHash {
|
||||
my $hash = shift;
|
||||
if ($hash =~ /^(.+):(.+)$/) {
|
||||
return ($1, $2);
|
||||
} else {
|
||||
return ("md5", $hash);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
# Compute the most efficient sequence of downloads to produce the
|
||||
# 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
|
||||
# files. The latter edges only occur between "start" and a store
|
||||
# path.
|
||||
my %graph;
|
||||
|
||||
$graph{"start"} = {d => 0, pred => undef, edges => []};
|
||||
|
||||
my @queue = ();
|
||||
my $queueFront = 0;
|
||||
my %done;
|
||||
|
||||
sub addNode {
|
||||
my $graph = shift;
|
||||
my $u = shift;
|
||||
$$graph{$u} = {d => 999999999999, pred => undef, edges => []}
|
||||
unless defined $$graph{$u};
|
||||
}
|
||||
|
||||
sub addEdge {
|
||||
my $graph = shift;
|
||||
my $u = shift;
|
||||
my $v = shift;
|
||||
my $w = shift;
|
||||
my $type = shift;
|
||||
my $info = shift;
|
||||
addNode $graph, $u;
|
||||
push @{$$graph{$u}->{edges}},
|
||||
{weight => $w, start => $u, end => $v, type => $type, info => $info};
|
||||
my $n = scalar @{$$graph{$u}->{edges}};
|
||||
}
|
||||
|
||||
push @queue, $targetPath;
|
||||
|
||||
while ($queueFront < scalar @queue) {
|
||||
my $u = $queue[$queueFront++];
|
||||
next if defined $done{$u};
|
||||
$done{$u} = 1;
|
||||
|
||||
addNode \%graph, $u;
|
||||
|
||||
# If the path already exists, it has distance 0 from the
|
||||
# "start" node.
|
||||
if (isValidPath($u)) {
|
||||
addEdge \%graph, "start", $u, 0, "present", undef;
|
||||
}
|
||||
|
||||
else {
|
||||
next; # not an error
|
||||
}
|
||||
print "$storePath\n";
|
||||
print "$info->{deriver}\n";
|
||||
my @references = split " ", $info->{references};
|
||||
my $count = scalar @references;
|
||||
print "$count\n";
|
||||
foreach my $reference (@references) {
|
||||
print "$reference\n";
|
||||
|
||||
# Add patch edges.
|
||||
my $patchList = $patches{$u};
|
||||
foreach my $patch (@{$patchList}) {
|
||||
if (isValidPath($patch->{basePath})) {
|
||||
# !!! this should be cached
|
||||
my ($baseHashAlgo, $baseHash) = parseHash $patch->{baseHash};
|
||||
my $format = "--base32";
|
||||
$format = "" if $baseHashAlgo eq "md5";
|
||||
my $hash = $fast && $baseHashAlgo eq "sha256"
|
||||
? `$binDir/nix-store -q --hash "$patch->{basePath}"`
|
||||
: `$binDir/nix-hash --type '$baseHashAlgo' $format "$patch->{basePath}"`;
|
||||
chomp $hash;
|
||||
$hash =~ s/.*://;
|
||||
next if $hash ne $baseHash;
|
||||
}
|
||||
push @queue, $patch->{basePath};
|
||||
addEdge \%graph, $patch->{basePath}, $u, $patch->{size}, "patch", $patch;
|
||||
}
|
||||
|
||||
# Add NAR file edges to the start node.
|
||||
my $narFileList = $narFiles{$u};
|
||||
foreach my $narFile (@{$narFileList}) {
|
||||
# !!! how to handle files whose size is not known in advance?
|
||||
# For now, assume some arbitrary size (1 MB).
|
||||
addEdge \%graph, "start", $u, ($narFile->{size} || 1000000), "narfile", $narFile;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
# Run Dijkstra's shortest path algorithm to determine the shortest
|
||||
# sequence of download and/or patch actions that will produce
|
||||
# $targetPath.
|
||||
|
||||
my @todo = keys %graph;
|
||||
|
||||
while (scalar @todo > 0) {
|
||||
|
||||
# Remove the closest element from the todo list.
|
||||
# !!! inefficient, use a priority queue
|
||||
@todo = sort { -($graph{$a}->{d} <=> $graph{$b}->{d}) } @todo;
|
||||
my $u = pop @todo;
|
||||
|
||||
my $u_ = $graph{$u};
|
||||
|
||||
foreach my $edge (@{$u_->{edges}}) {
|
||||
my $v_ = $graph{$edge->{end}};
|
||||
if ($v_->{d} > $u_->{d} + $edge->{weight}) {
|
||||
$v_->{d} = $u_->{d} + $edge->{weight};
|
||||
# Store the edge; to edge->start is actually the
|
||||
# predecessor.
|
||||
$v_->{pred} = $edge;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
# Retrieve the shortest path from "start" to $targetPath.
|
||||
my @path = ();
|
||||
my $cur = $targetPath;
|
||||
return () unless defined $graph{$targetPath}->{pred};
|
||||
while ($cur ne "start") {
|
||||
push @path, $graph{$cur}->{pred};
|
||||
$cur = $graph{$cur}->{pred}->{start};
|
||||
}
|
||||
|
||||
return @path;
|
||||
}
|
||||
|
||||
|
||||
# Parse the arguments.
|
||||
|
||||
if ($ARGV[0] eq "--query") {
|
||||
|
||||
while (<STDIN>) {
|
||||
my $cmd = $_; chomp $cmd;
|
||||
|
||||
if ($cmd eq "have") {
|
||||
my $storePath = <STDIN>; chomp $storePath;
|
||||
print STDOUT ((defined $narFiles{$storePath} or defined $localPaths{$storePath})
|
||||
? "1\n" : "0\n");
|
||||
}
|
||||
|
||||
elsif ($cmd eq "info") {
|
||||
my $storePath = <STDIN>; chomp $storePath;
|
||||
|
||||
my $info;
|
||||
if (defined $narFiles{$storePath}) {
|
||||
$info = @{$narFiles{$storePath}}[0];
|
||||
}
|
||||
elsif (defined $localPaths{$storePath}) {
|
||||
$info = @{$localPaths{$storePath}}[0];
|
||||
}
|
||||
else {
|
||||
print "0\n";
|
||||
next; # not an error
|
||||
}
|
||||
|
||||
print "1\n";
|
||||
print "$info->{deriver}\n";
|
||||
my @references = split " ", $info->{references};
|
||||
print scalar @references, "\n";
|
||||
print "$_\n" foreach @references;
|
||||
|
||||
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";
|
||||
}
|
||||
|
||||
else { die "unknown command `$cmd'"; }
|
||||
}
|
||||
|
||||
exit 0;
|
||||
}
|
||||
|
||||
elsif ($ARGV[0] ne "--substitute") {
|
||||
die "syntax: $0 [--query-paths | --query-info PATHS... | --substitute PATH]\n";
|
||||
die;
|
||||
}
|
||||
|
||||
|
||||
die unless scalar @ARGV == 2;
|
||||
my $targetPath = $ARGV[1];
|
||||
$fast = 0;
|
||||
|
||||
|
||||
# Create a temporary directory.
|
||||
my $tmpDir = tempdir("nix-download.XXXXXX", CLEANUP => 1, TMPDIR => 1)
|
||||
or die "cannot create a temporary directory";
|
||||
|
||||
my $tmpNar = "$tmpDir/nar";
|
||||
my $tmpNar2 = "$tmpDir/nar2";
|
||||
|
||||
|
||||
open LOGFILE, ">>$logFile" or die "cannot open log file $logFile";
|
||||
|
||||
my $date = strftime ("%F %H:%M:%S UTC", gmtime (time));
|
||||
print LOGFILE "$$ get $targetPath $date\n";
|
||||
|
||||
@@ -88,158 +273,16 @@ foreach my $localPath (@{$localPathList}) {
|
||||
my $sourcePath = $localPath->{copyFrom};
|
||||
if (-e $sourcePath) {
|
||||
print "\n*** Step 1/1: copying from $sourcePath\n";
|
||||
system("@bindir@/nix-store --dump $sourcePath | @bindir@/nix-store --restore $targetPath") == 0
|
||||
system("$binDir/nix-store --dump $sourcePath | $binDir/nix-store --restore $targetPath") == 0
|
||||
or die "cannot copy `$sourcePath' to `$targetPath'";
|
||||
exit 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
# 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 files.
|
||||
# The latter edges only occur between "start" and a store path.
|
||||
|
||||
my %graph;
|
||||
|
||||
$graph{"start"} = {d => 0, pred => undef, edges => []};
|
||||
|
||||
my @queue = ();
|
||||
my $queueFront = 0;
|
||||
my %done;
|
||||
|
||||
sub addToQueue {
|
||||
my $v = shift;
|
||||
return if defined $done{$v};
|
||||
$done{$v} = 1;
|
||||
push @queue, $v;
|
||||
}
|
||||
|
||||
sub addNode {
|
||||
my $u = shift;
|
||||
$graph{$u} = {d => 999999999999, pred => undef, edges => []}
|
||||
unless defined $graph{$u};
|
||||
}
|
||||
|
||||
sub addEdge {
|
||||
my $u = shift;
|
||||
my $v = shift;
|
||||
my $w = shift;
|
||||
my $type = shift;
|
||||
my $info = shift;
|
||||
addNode $u;
|
||||
push @{$graph{$u}->{edges}},
|
||||
{weight => $w, start => $u, end => $v, type => $type, info => $info};
|
||||
my $n = scalar @{$graph{$u}->{edges}};
|
||||
}
|
||||
|
||||
addToQueue $targetPath;
|
||||
|
||||
sub isValidPath {
|
||||
my $p = shift;
|
||||
return system("@bindir@/nix-store --check-validity '$p' 2> /dev/null") == 0;
|
||||
}
|
||||
|
||||
sub parseHash {
|
||||
my $hash = shift;
|
||||
if ($hash =~ /^(.+):(.+)$/) {
|
||||
return ($1, $2);
|
||||
} else {
|
||||
return ("md5", $hash);
|
||||
}
|
||||
}
|
||||
|
||||
while ($queueFront < scalar @queue) {
|
||||
my $u = $queue[$queueFront++];
|
||||
# print "$u\n";
|
||||
|
||||
addNode $u;
|
||||
|
||||
# If the path already exists, it has distance 0 from the "start"
|
||||
# node.
|
||||
if (isValidPath($u)) {
|
||||
addEdge "start", $u, 0, "present", undef;
|
||||
}
|
||||
|
||||
else {
|
||||
|
||||
# Add patch edges.
|
||||
my $patchList = $patches{$u};
|
||||
foreach my $patch (@{$patchList}) {
|
||||
if (isValidPath($patch->{basePath})) {
|
||||
# !!! this should be cached
|
||||
my ($baseHashAlgo, $baseHash) = parseHash $patch->{baseHash};
|
||||
my $format = "--base32";
|
||||
$format = "" if $baseHashAlgo eq "md5";
|
||||
my $hash = `@bindir@/nix-hash --type '$baseHashAlgo' $format "$patch->{basePath}"`;
|
||||
chomp $hash;
|
||||
# print " MY HASH is $hash\n";
|
||||
if ($hash ne $baseHash) {
|
||||
print LOGFILE "$$ rejecting $patch->{basePath}\n";
|
||||
next;
|
||||
}
|
||||
}
|
||||
# print " PATCH from $patch->{basePath}\n";
|
||||
addToQueue $patch->{basePath};
|
||||
addEdge $patch->{basePath}, $u, $patch->{size}, "patch", $patch;
|
||||
}
|
||||
|
||||
# Add NAR file edges to the start node.
|
||||
my $narFileList = $narFiles{$u};
|
||||
foreach my $narFile (@{$narFileList}) {
|
||||
# print " NAR from $narFile->{url}\n";
|
||||
addEdge "start", $u, $narFile->{size}, "narfile", $narFile;
|
||||
if ($u eq $targetPath) {
|
||||
print LOGFILE "$$ full-download-would-be $narFile->{size}\n";
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
# Run Dijkstra's shortest path algorithm to determine the shortest
|
||||
# sequence of download and/or patch actions that will produce
|
||||
# $targetPath.
|
||||
|
||||
sub byDistance { # sort by distance, reversed
|
||||
return -($graph{$a}->{d} <=> $graph{$b}->{d});
|
||||
}
|
||||
|
||||
my @todo = keys %graph;
|
||||
|
||||
while (scalar @todo > 0) {
|
||||
|
||||
# Remove the closest element from the todo list.
|
||||
@todo = sort byDistance @todo;
|
||||
my $u = pop @todo;
|
||||
|
||||
my $u_ = $graph{$u};
|
||||
|
||||
# print "IN $u $u_->{d}\n";
|
||||
|
||||
foreach my $edge (@{$u_->{edges}}) {
|
||||
my $v_ = $graph{$edge->{end}};
|
||||
if ($v_->{d} > $u_->{d} + $edge->{weight}) {
|
||||
$v_->{d} = $u_->{d} + $edge->{weight};
|
||||
# Store the edge; to edge->start is actually the
|
||||
# predecessor.
|
||||
$v_->{pred} = $edge;
|
||||
# print " RELAX $edge->{end} $v_->{d}\n";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
# Retrieve the shortest path from "start" to $targetPath.
|
||||
my @path = ();
|
||||
my $cur = $targetPath;
|
||||
die "don't know how to produce $targetPath\n"
|
||||
unless defined $graph{$targetPath}->{pred};
|
||||
while ($cur ne "start") {
|
||||
push @path, $graph{$cur}->{pred};
|
||||
$cur = $graph{$cur}->{pred}->{start};
|
||||
}
|
||||
# Compute the shortest path.
|
||||
my @path = computeSmallestDownload $targetPath;
|
||||
die "don't know how to produce $targetPath\n" if scalar @path == 0;
|
||||
|
||||
|
||||
# Traverse the shortest path, perform the actions described by the
|
||||
@@ -247,20 +290,18 @@ while ($cur ne "start") {
|
||||
my $curStep = 1;
|
||||
my $maxStep = scalar @path;
|
||||
|
||||
sub downloadFile {
|
||||
my $url = shift;
|
||||
my ($hashAlgo, $hash) = parseHash(shift);
|
||||
sub downloadFile {
|
||||
my $url = shift;
|
||||
$ENV{"PRINT_PATH"} = 1;
|
||||
$ENV{"QUIET"} = 1;
|
||||
$ENV{"NIX_HASH_ALGO"} = $hashAlgo;
|
||||
my ($hash2, $path) = `@bindir@/nix-prefetch-url '$url' '$hash'`;
|
||||
my ($hash, $path) = `$binDir/nix-prefetch-url '$url'`;
|
||||
die "download of `$url' failed" unless $? == 0;
|
||||
chomp $hash2;
|
||||
chomp $path;
|
||||
die "hash mismatch, expected $hash, got $hash2" if $hash ne $hash2;
|
||||
return $path;
|
||||
}
|
||||
|
||||
my $finalNarHash;
|
||||
|
||||
while (scalar @path > 0) {
|
||||
my $edge = pop @path;
|
||||
my $u = $edge->{start};
|
||||
@@ -277,7 +318,7 @@ while (scalar @path > 0) {
|
||||
# as a base to one or more patches. So turn the base path
|
||||
# into a NAR archive, to which we can apply the patch.
|
||||
print " packing base path...\n";
|
||||
system("@bindir@/nix-store --dump $v > $tmpNar") == 0
|
||||
system("$binDir/nix-store --dump $v > $tmpNar") == 0
|
||||
or die "cannot dump `$v'";
|
||||
}
|
||||
}
|
||||
@@ -290,7 +331,7 @@ while (scalar @path > 0) {
|
||||
|
||||
# Download the patch.
|
||||
print " downloading patch...\n";
|
||||
my $patchPath = downloadFile "$patch->{url}", "$patch->{hash}";
|
||||
my $patchPath = downloadFile "$patch->{url}";
|
||||
|
||||
# Apply the patch to the NAR archive produced in step 1 (for
|
||||
# the already present path) or a later step (for patch sequences).
|
||||
@@ -305,20 +346,23 @@ while (scalar @path > 0) {
|
||||
# This was the last patch. Unpack the final NAR archive
|
||||
# into the target path.
|
||||
print " unpacking patched archive...\n";
|
||||
system("@bindir@/nix-store --restore $v < $tmpNar2") == 0
|
||||
system("$binDir/nix-store --restore $v < $tmpNar2") == 0
|
||||
or die "cannot unpack $tmpNar2 into `$v'";
|
||||
}
|
||||
|
||||
$finalNarHash = $patch->{narHash};
|
||||
}
|
||||
|
||||
elsif ($edge->{type} eq "narfile") {
|
||||
my $narFile = $edge->{info};
|
||||
print "downloading `$narFile->{url}' into `$v'\n";
|
||||
|
||||
print LOGFILE "$$ narfile $narFile->{url} $narFile->{size} $v\n";
|
||||
my $size = $narFile->{size} || -1;
|
||||
print LOGFILE "$$ narfile $narFile->{url} $size $v\n";
|
||||
|
||||
# Download the archive.
|
||||
print " downloading archive...\n";
|
||||
my $narFilePath = downloadFile "$narFile->{url}", "$narFile->{hash}";
|
||||
my $narFilePath = downloadFile "$narFile->{url}";
|
||||
|
||||
if ($curStep < $maxStep) {
|
||||
# The archive will be used a base to a patch.
|
||||
@@ -327,14 +371,39 @@ while (scalar @path > 0) {
|
||||
} else {
|
||||
# Unpack the archive into the target path.
|
||||
print " unpacking archive...\n";
|
||||
system("@bunzip2@ < '$narFilePath' | @bindir@/nix-store --restore '$v'") == 0
|
||||
system("@bunzip2@ < '$narFilePath' | $binDir/nix-store --restore '$v'") == 0
|
||||
or die "cannot unpack `$narFilePath' into `$v'";
|
||||
}
|
||||
|
||||
$finalNarHash = $narFile->{narHash};
|
||||
}
|
||||
|
||||
$curStep++;
|
||||
}
|
||||
|
||||
|
||||
# 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 $extraFlag =
|
||||
($hashAlgo eq "sha256" && length($hash) != 64)
|
||||
? "--base32" : "";
|
||||
|
||||
my $hash2 = `@bindir@/nix-hash --type $hashAlgo $extraFlag $targetPath`
|
||||
or die "cannot compute hash of path `$targetPath'";
|
||||
chomp $hash2;
|
||||
|
||||
die "hash mismatch in downloaded path $targetPath; expected $hash, got $hash2"
|
||||
if $hash ne $hash2;
|
||||
} else {
|
||||
die "cannot check integrity of the downloaded path since its hash is not known";
|
||||
}
|
||||
|
||||
|
||||
print LOGFILE "$$ success\n";
|
||||
close LOGFILE;
|
||||
|
||||
@@ -1,408 +0,0 @@
|
||||
#! @perl@ -w -I@libexecdir@/nix
|
||||
|
||||
use strict;
|
||||
use File::Temp qw(tempdir);
|
||||
use readmanifest;
|
||||
|
||||
|
||||
# Some patch generations options.
|
||||
|
||||
# Max size of NAR archives to generate patches for.
|
||||
my $maxNarSize = $ENV{"NIX_MAX_NAR_SIZE"};
|
||||
$maxNarSize = 100 * 1024 * 1024 if !defined $maxNarSize;
|
||||
|
||||
# If patch is bigger than this fraction of full archive, reject.
|
||||
my $maxPatchFraction = $ENV{"NIX_PATCH_FRACTION"};
|
||||
$maxPatchFraction = 0.60 if !defined $maxPatchFraction;
|
||||
|
||||
|
||||
die unless scalar @ARGV == 5;
|
||||
|
||||
my $hashAlgo = "sha256";
|
||||
|
||||
my $cacheDir = $ARGV[0];
|
||||
my $patchesDir = $ARGV[1];
|
||||
my $patchesURL = $ARGV[2];
|
||||
my $srcDir = $ARGV[3];
|
||||
my $dstDir = $ARGV[4];
|
||||
|
||||
my $tmpDir = tempdir("nix-generate-patches.XXXXXX", CLEANUP => 1, TMPDIR => 1)
|
||||
or die "cannot create a temporary directory";
|
||||
|
||||
print "TEMP = $tmpDir\n";
|
||||
|
||||
#END { rmdir $tmpDir; }
|
||||
|
||||
my %srcNarFiles;
|
||||
my %srcLocalPaths;
|
||||
my %srcPatches;
|
||||
|
||||
my %dstNarFiles;
|
||||
my %dstLocalPaths;
|
||||
my %dstPatches;
|
||||
|
||||
readManifest "$srcDir/MANIFEST",
|
||||
\%srcNarFiles, \%srcLocalPaths, \%srcPatches;
|
||||
|
||||
readManifest "$dstDir/MANIFEST",
|
||||
\%dstNarFiles, \%dstLocalPaths, \%dstPatches;
|
||||
|
||||
|
||||
sub findOutputPaths {
|
||||
my $narFiles = shift;
|
||||
|
||||
my %outPaths;
|
||||
|
||||
foreach my $p (keys %{$narFiles}) {
|
||||
|
||||
# Ignore store expressions.
|
||||
next if ($p =~ /\.store$/);
|
||||
next if ($p =~ /\.drv$/);
|
||||
|
||||
# Ignore builders (too much ambiguity -- they're all called
|
||||
# `builder.sh').
|
||||
next if ($p =~ /\.sh$/);
|
||||
next if ($p =~ /\.patch$/);
|
||||
|
||||
# Don't bother including tar files etc.
|
||||
next if ($p =~ /\.tar\.(gz|bz2)$/ || $p =~ /\.zip$/ || $p =~ /\.bin$/);
|
||||
|
||||
$outPaths{$p} = 1;
|
||||
}
|
||||
|
||||
return %outPaths;
|
||||
}
|
||||
|
||||
print "finding src output paths...\n";
|
||||
my %srcOutPaths = findOutputPaths \%srcNarFiles;
|
||||
|
||||
print "finding dst output paths...\n";
|
||||
my %dstOutPaths = findOutputPaths \%dstNarFiles;
|
||||
|
||||
|
||||
sub getNameVersion {
|
||||
my $p = shift;
|
||||
$p =~ /\/[0-9a-z]+((?:-[a-zA-Z][^\/-]*)+)([^\/]*)$/;
|
||||
my $name = $1;
|
||||
my $version = $2;
|
||||
$name =~ s/^-//;
|
||||
$version =~ s/^-//;
|
||||
return ($name, $version);
|
||||
}
|
||||
|
||||
|
||||
# A quick hack to get a measure of the `distance' between two
|
||||
# versions: it's just the position of the first character that differs
|
||||
# (or 999 if they are the same).
|
||||
sub versionDiff {
|
||||
my $s = shift;
|
||||
my $t = shift;
|
||||
my $i;
|
||||
return 999 if $s eq $t;
|
||||
for ($i = 0; $i < length $s; $i++) {
|
||||
return $i if $i >= length $t or
|
||||
substr($s, $i, 1) ne substr($t, $i, 1);
|
||||
}
|
||||
return $i;
|
||||
}
|
||||
|
||||
|
||||
sub getNarBz2 {
|
||||
my $narFiles = shift;
|
||||
my $storePath = shift;
|
||||
|
||||
my $narFileList = $$narFiles{$storePath};
|
||||
die "missing store expression $storePath" unless defined $narFileList;
|
||||
|
||||
my $narFile = @{$narFileList}[0];
|
||||
die unless defined $narFile;
|
||||
|
||||
$narFile->{url} =~ /\/([^\/]+)$/;
|
||||
die unless defined $1;
|
||||
return "$cacheDir/$1";
|
||||
}
|
||||
|
||||
|
||||
sub containsPatch {
|
||||
my $patches = shift;
|
||||
my $storePath = shift;
|
||||
my $basePath = shift;
|
||||
my $patchList = $$patches{$storePath};
|
||||
return 0 if !defined $patchList;
|
||||
my $found = 0;
|
||||
foreach my $patch (@{$patchList}) {
|
||||
# !!! baseHash might differ
|
||||
return 1 if $patch->{basePath} eq $basePath;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
# Compute the "weighted" number of uses of a path in the build graph.
|
||||
sub computeUses {
|
||||
my $narFiles = shift;
|
||||
my $path = shift;
|
||||
|
||||
# Find the deriver of $path.
|
||||
return 1 unless defined $$narFiles{$path};
|
||||
my $deriver = @{$$narFiles{$path}}[0]->{deriver};
|
||||
return 1 unless defined $deriver && $deriver ne "";
|
||||
|
||||
# print " DERIVER $deriver\n";
|
||||
|
||||
# Optimisation: build the referrers graph from the references
|
||||
# graph.
|
||||
my %referrers;
|
||||
foreach my $q (keys %{$narFiles}) {
|
||||
my @refs = split " ", @{$$narFiles{$q}}[0]->{references};
|
||||
foreach my $r (@refs) {
|
||||
$referrers{$r} = [] unless defined $referrers{$r};
|
||||
push @{$referrers{$r}}, $q;
|
||||
}
|
||||
}
|
||||
|
||||
# Determine the shortest path from $deriver to all other reachable
|
||||
# paths in the `referrers' graph.
|
||||
|
||||
my %dist;
|
||||
$dist{$deriver} = 0;
|
||||
|
||||
my @queue = ($deriver);
|
||||
my $pos = 0;
|
||||
|
||||
while ($pos < scalar @queue) {
|
||||
my $p = $queue[$pos];
|
||||
$pos++;
|
||||
|
||||
foreach my $q (@{$referrers{$p}}) {
|
||||
if (!defined $dist{$q}) {
|
||||
$dist{$q} = $dist{$p} + 1;
|
||||
# print " $q $dist{$q}\n";
|
||||
push @queue, $q;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
my $wuse = 1.0;
|
||||
foreach my $user (keys %dist) {
|
||||
next if $user eq $deriver;
|
||||
# print " $user $dist{$user}\n";
|
||||
$wuse += 1.0 / 2.0**$dist{$user};
|
||||
}
|
||||
|
||||
# print " XXX $path $wuse\n";
|
||||
|
||||
return $wuse;
|
||||
}
|
||||
|
||||
|
||||
# For each output path in the destination, see if we need to / can
|
||||
# create a patch.
|
||||
|
||||
print "creating patches...\n";
|
||||
|
||||
foreach my $p (keys %dstOutPaths) {
|
||||
|
||||
# If exactly the same path already exists in the source, skip it.
|
||||
next if defined $srcOutPaths{$p};
|
||||
|
||||
print " $p\n";
|
||||
|
||||
# If not, then we should find the paths in the source that are
|
||||
# `most' likely to be present on a system that wants to install
|
||||
# this path.
|
||||
|
||||
(my $name, my $version) = getNameVersion $p;
|
||||
|
||||
my @closest = ();
|
||||
my $closestVersion;
|
||||
my $minDist = -1; # actually, larger means closer
|
||||
|
||||
# Find all source paths with the same name.
|
||||
|
||||
foreach my $q (keys %srcOutPaths) {
|
||||
(my $name2, my $version2) = getNameVersion $q;
|
||||
if ($name eq $name2) {
|
||||
|
||||
# If the sizes differ too much, then skip. This
|
||||
# disambiguates between, e.g., a real component and a
|
||||
# wrapper component (cf. Firefox in Nixpkgs).
|
||||
my $srcSize = @{$srcNarFiles{$q}}[0]->{size};
|
||||
my $dstSize = @{$dstNarFiles{$p}}[0]->{size};
|
||||
my $ratio = $srcSize / $dstSize;
|
||||
$ratio = 1 / $ratio if $ratio < 1;
|
||||
# print " SIZE $srcSize $dstSize $ratio $q\n";
|
||||
|
||||
if ($ratio >= 3) {
|
||||
print " SKIPPING $q due to size ratio $ratio ($srcSize $dstSize)\n";
|
||||
next;
|
||||
}
|
||||
|
||||
# If the numbers of weighted uses differ too much, then
|
||||
# skip. This disambiguates between, e.g., the bootstrap
|
||||
# GCC and the final GCC in Nixpkgs.
|
||||
my $srcUses = computeUses \%srcNarFiles, $q;
|
||||
my $dstUses = computeUses \%dstNarFiles, $p;
|
||||
$ratio = $srcUses / $dstUses;
|
||||
$ratio = 1 / $ratio if $ratio < 1;
|
||||
print " USE $srcUses $dstUses $ratio $q\n";
|
||||
|
||||
# if ($ratio >= 2) {
|
||||
# print " SKIPPING $q due to use ratio $ratio ($srcUses $dstUses)\n";
|
||||
# next;
|
||||
# }
|
||||
|
||||
# If there are multiple matching names, include the ones
|
||||
# with the closest version numbers.
|
||||
my $dist = versionDiff $version, $version2;
|
||||
if ($dist > $minDist) {
|
||||
$minDist = $dist;
|
||||
@closest = ($q);
|
||||
$closestVersion = $version2;
|
||||
} elsif ($dist == $minDist) {
|
||||
push @closest, $q;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (scalar(@closest) == 0) {
|
||||
print " NO BASE: $p\n";
|
||||
next;
|
||||
}
|
||||
|
||||
foreach my $closest (@closest) {
|
||||
|
||||
# Generate a patch between $closest and $p.
|
||||
print " $p <- $closest\n";
|
||||
|
||||
# If the patch already exists, skip it.
|
||||
if (containsPatch(\%srcPatches, $p, $closest) ||
|
||||
containsPatch(\%dstPatches, $p, $closest))
|
||||
{
|
||||
print " skipping, already exists\n";
|
||||
next;
|
||||
}
|
||||
|
||||
# next;
|
||||
|
||||
my $srcNarBz2 = getNarBz2 \%srcNarFiles, $closest;
|
||||
my $dstNarBz2 = getNarBz2 \%dstNarFiles, $p;
|
||||
|
||||
system("@bunzip2@ < $srcNarBz2 > $tmpDir/A") == 0
|
||||
or die "cannot unpack $srcNarBz2";
|
||||
|
||||
if ((stat "$tmpDir/A")[7] >= $maxNarSize) {
|
||||
print " skipping, source is too large\n";
|
||||
next;
|
||||
}
|
||||
|
||||
system("@bunzip2@ < $dstNarBz2 > $tmpDir/B") == 0
|
||||
or die "cannot unpack $dstNarBz2";
|
||||
|
||||
if ((stat "$tmpDir/B")[7] >= $maxNarSize) {
|
||||
print " skipping, destination is too large\n";
|
||||
next;
|
||||
}
|
||||
|
||||
system("@libexecdir@/bsdiff $tmpDir/A $tmpDir/B $tmpDir/DIFF") == 0
|
||||
or die "cannot compute binary diff";
|
||||
|
||||
my $baseHash = `@bindir@/nix-hash --flat --type $hashAlgo --base32 $tmpDir/A` or die;
|
||||
chomp $baseHash;
|
||||
|
||||
my $narHash = `@bindir@/nix-hash --flat --type $hashAlgo --base32 $tmpDir/B` or die;
|
||||
chomp $narHash;
|
||||
|
||||
my $narDiffHash = `@bindir@/nix-hash --flat --type $hashAlgo --base32 $tmpDir/DIFF` or die;
|
||||
chomp $narDiffHash;
|
||||
|
||||
my $narDiffSize = (stat "$tmpDir/DIFF")[7];
|
||||
my $dstNarBz2Size = (stat $dstNarBz2)[7];
|
||||
|
||||
print " size $narDiffSize; full size $dstNarBz2Size\n";
|
||||
|
||||
if ($narDiffSize >= $dstNarBz2Size) {
|
||||
print " rejecting; patch bigger than full archive\n";
|
||||
next;
|
||||
}
|
||||
|
||||
if ($narDiffSize / $dstNarBz2Size >= $maxPatchFraction) {
|
||||
print " rejecting; patch too large relative to full archive\n";
|
||||
next;
|
||||
}
|
||||
|
||||
my $finalName =
|
||||
"$narDiffHash.nar-bsdiff";
|
||||
|
||||
if (-e "$patchesDir/$finalName") {
|
||||
print " not copying, already exists\n";
|
||||
}
|
||||
|
||||
else {
|
||||
|
||||
system("cp '$tmpDir/DIFF' '$patchesDir/$finalName.tmp'") == 0
|
||||
or die "cannot copy diff";
|
||||
|
||||
rename("$patchesDir/$finalName.tmp", "$patchesDir/$finalName")
|
||||
or die "cannot rename $patchesDir/$finalName.tmp";
|
||||
|
||||
}
|
||||
|
||||
# Add the patch to the manifest.
|
||||
addPatch \%dstPatches, $p,
|
||||
{ url => "$patchesURL/$finalName", hash => "$hashAlgo:$narDiffHash"
|
||||
, size => $narDiffSize, basePath => $closest, baseHash => "$hashAlgo:$baseHash"
|
||||
, narHash => "$hashAlgo:$narHash", patchType => "nar-bsdiff"
|
||||
}, 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
# Add in any potentially useful patches in the source (namely, those
|
||||
# patches that produce either paths in the destination or paths that
|
||||
# can be used as the base for other useful patches).
|
||||
|
||||
print "propagating patches...\n";
|
||||
|
||||
my $changed;
|
||||
do {
|
||||
# !!! we repeat this to reach the transitive closure; inefficient
|
||||
$changed = 0;
|
||||
|
||||
print "loop\n";
|
||||
|
||||
my %dstBasePaths;
|
||||
foreach my $q (keys %dstPatches) {
|
||||
foreach my $patch (@{$dstPatches{$q}}) {
|
||||
$dstBasePaths{$patch->{basePath}} = 1;
|
||||
}
|
||||
}
|
||||
|
||||
foreach my $p (keys %srcPatches) {
|
||||
my $patchList = $srcPatches{$p};
|
||||
|
||||
my $include = 0;
|
||||
|
||||
# Is path $p included in the destination? If so, include
|
||||
# patches that produce it.
|
||||
$include = 1 if defined $dstNarFiles{$p};
|
||||
|
||||
# Is path $p a path that serves as a base for paths in the
|
||||
# destination? If so, include patches that produce it.
|
||||
# !!! check baseHash
|
||||
$include = 1 if defined $dstBasePaths{$p};
|
||||
|
||||
if ($include) {
|
||||
foreach my $patch (@{$patchList}) {
|
||||
$changed = 1 if addPatch \%dstPatches, $p, $patch;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
} while $changed;
|
||||
|
||||
|
||||
# Rewrite the manifest of the destination (with the new patches).
|
||||
writeManifest "$dstDir/MANIFEST",
|
||||
\%dstNarFiles, \%dstPatches;
|
||||
@@ -2,6 +2,8 @@
|
||||
|
||||
use strict;
|
||||
|
||||
my $binDir = $ENV{"NIX_BIN_DIR"} || "@bindir@";
|
||||
|
||||
|
||||
my $addDrvLink = 0;
|
||||
my $addOutLink = 1;
|
||||
@@ -9,6 +11,9 @@ my $addOutLink = 1;
|
||||
my $outLink;
|
||||
my $drvLink;
|
||||
|
||||
my $dryRun = 0;
|
||||
my $verbose = 0;
|
||||
|
||||
my @instArgs = ();
|
||||
my @buildArgs = ();
|
||||
my @exprs = ();
|
||||
@@ -73,7 +78,7 @@ EOF
|
||||
|
||||
elsif ($arg eq "--attr" or $arg eq "-A") {
|
||||
$n++;
|
||||
die "$0: `--attr' requires an argument\n" unless $n < scalar @ARGV;
|
||||
die "$0: `$arg' requires an argument\n" unless $n < scalar @ARGV;
|
||||
push @instArgs, ("--attr", $ARGV[$n]);
|
||||
}
|
||||
|
||||
@@ -83,12 +88,46 @@ EOF
|
||||
$n += 2;
|
||||
}
|
||||
|
||||
elsif ($arg eq "--max-jobs" or $arg eq "-j" or $arg eq "--max-silent-time") {
|
||||
elsif ($arg eq "--log-type") {
|
||||
$n++;
|
||||
die "$0: `$arg' requires an argument\n" unless $n < scalar @ARGV;
|
||||
push @instArgs, ($arg, $ARGV[$n]);
|
||||
push @buildArgs, ($arg, $ARGV[$n]);
|
||||
}
|
||||
|
||||
elsif ($arg eq "--option") {
|
||||
die "$0: `$arg' requires two arguments\n" unless $n + 2 < scalar @ARGV;
|
||||
push @instArgs, ($arg, $ARGV[$n + 1], $ARGV[$n + 2]);
|
||||
push @buildArgs, ($arg, $ARGV[$n + 1], $ARGV[$n + 2]);
|
||||
$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") {
|
||||
$n++;
|
||||
die "$0: `$arg' requires an argument\n" unless $n < scalar @ARGV;
|
||||
push @buildArgs, ($arg, $ARGV[$n]);
|
||||
}
|
||||
|
||||
elsif ($arg eq "--dry-run") {
|
||||
push @buildArgs, "--dry-run";
|
||||
$dryRun = 1;
|
||||
}
|
||||
|
||||
elsif ($arg eq "--show-trace") {
|
||||
push @instArgs, $arg;
|
||||
}
|
||||
|
||||
elsif ($arg eq "--verbose" or substr($arg, 0, 2) eq "-v") {
|
||||
push @buildArgs, $arg;
|
||||
push @instArgs, $arg;
|
||||
$verbose = 1;
|
||||
}
|
||||
|
||||
elsif ($arg eq "--quiet") {
|
||||
push @buildArgs, $arg;
|
||||
push @instArgs, $arg;
|
||||
}
|
||||
|
||||
elsif (substr($arg, 0, 1) eq "-") {
|
||||
push @buildArgs, $arg;
|
||||
}
|
||||
@@ -117,21 +156,29 @@ foreach my $expr (@exprs) {
|
||||
# Instantiate.
|
||||
my @drvPaths;
|
||||
# !!! would prefer the perl 5.8.0 pipe open feature here.
|
||||
my $pid = open(DRVPATHS, "-|") || exec "@bindir@/nix-instantiate", "--add-root", $drvLink, "--indirect", @instArgs, $expr;
|
||||
my $pid = open(DRVPATHS, "-|") || exec "$binDir/nix-instantiate", "--add-root", $drvLink, "--indirect", @instArgs, $expr;
|
||||
while (<DRVPATHS>) {chomp; push @drvPaths, $_;}
|
||||
close DRVPATHS or exit 1;
|
||||
if (!close DRVPATHS) {
|
||||
die "nix-instantiate killed by signal " . ($? & 127) . "\n" if ($? & 127);
|
||||
exit 1;
|
||||
}
|
||||
|
||||
foreach my $drvPath (@drvPaths) {
|
||||
my $target = readlink $drvPath or die "cannot read symlink `$drvPath'";
|
||||
print STDERR "store derivation is $target\n";
|
||||
print STDERR "derivation is $target\n" if $verbose;
|
||||
}
|
||||
|
||||
# Build.
|
||||
my @outPaths;
|
||||
$pid = open(OUTPATHS, "-|") || exec "@bindir@/nix-store", "--add-root", $outLink, "--indirect", "-rv",
|
||||
$pid = open(OUTPATHS, "-|") || exec "$binDir/nix-store", "--add-root", $outLink, "--indirect", "-r",
|
||||
@buildArgs, @drvPaths;
|
||||
while (<OUTPATHS>) {chomp; push @outPaths, $_;}
|
||||
close OUTPATHS or exit 1;
|
||||
if (!close OUTPATHS) {
|
||||
die "nix-store killed by signal " . ($? & 127) . "\n" if ($? & 127);
|
||||
exit 1;
|
||||
}
|
||||
|
||||
next if $dryRun;
|
||||
|
||||
foreach my $outPath (@outPaths) {
|
||||
my $target = readlink $outPath or die "cannot read symlink `$outPath'";
|
||||
|
||||
@@ -78,6 +78,9 @@ sub removeChannel {
|
||||
sub update {
|
||||
readChannels;
|
||||
|
||||
# Create the manifests directory if it doesn't exist.
|
||||
mkdir "$stateDir/manifests", 0755 unless -e "$stateDir/manifests";
|
||||
|
||||
# Do we have write permission to the manifests directory? If not,
|
||||
# then just skip pulling the manifest and just download the Nix
|
||||
# expressions. If the user is a non-privileged user in a
|
||||
@@ -85,11 +88,6 @@ sub update {
|
||||
# source.
|
||||
if (-W "$stateDir/manifests") {
|
||||
|
||||
# Remove all the old manifests.
|
||||
for my $manifest (glob "$stateDir/manifests/*.nixmanifest") {
|
||||
unlink $manifest or die "cannot remove `$manifest': $!";
|
||||
}
|
||||
|
||||
# Pull cache manifests.
|
||||
foreach my $url (@channels) {
|
||||
#print "pulling cache manifest from `$url'\n";
|
||||
@@ -125,15 +123,13 @@ sub update {
|
||||
|
||||
my $rootFile = "$rootsDir/per-user/$userName/channels";
|
||||
|
||||
# Instantiate the Nix expression.
|
||||
# Build the Nix expression.
|
||||
print "unpacking channel Nix expressions...\n";
|
||||
my $storeExpr = `@bindir@/nix-instantiate --add-root '$rootFile'.tmp @datadir@/nix/corepkgs/channels/unpack.nix --argstr system @system@ --arg inputs '$inputs'`
|
||||
or die "cannot instantiate Nix expression";
|
||||
chomp $storeExpr;
|
||||
|
||||
# Build the resulting derivation.
|
||||
my $outPath = `@bindir@/nix-store --add-root '$rootFile' -r '$storeExpr'`
|
||||
or die "cannot realise store expression";
|
||||
my $outPath = `\\
|
||||
@bindir@/nix-build --out-link '$rootFile' --drv-link '$rootFile'.tmp \\
|
||||
@datadir@/nix/corepkgs/channels/unpack.nix \\
|
||||
--argstr system @system@ --arg inputs '$inputs'`
|
||||
or die "cannot unpack the channels";
|
||||
chomp $outPath;
|
||||
|
||||
unlink "$rootFile.tmp";
|
||||
|
||||
@@ -4,6 +4,8 @@ use strict;
|
||||
|
||||
my $profilesDir = "@localstatedir@/nix/profiles";
|
||||
|
||||
my $binDir = $ENV{"NIX_BIN_DIR"} || "@bindir@";
|
||||
|
||||
|
||||
# Process the command line arguments.
|
||||
my @args = ();
|
||||
@@ -34,7 +36,7 @@ sub removeOldGenerations {
|
||||
$name = $dir . "/" . $name;
|
||||
if (-l $name && (readlink($name) =~ /link/)) {
|
||||
print STDERR "removing old generations of profile $name\n";
|
||||
system("@bindir@/nix-env", "-p", $name, "--delete-generations", "old");
|
||||
system("$binDir/nix-env", "-p", $name, "--delete-generations", "old");
|
||||
}
|
||||
elsif (! -l $name && -d $name) {
|
||||
removeOldGenerations $name;
|
||||
@@ -48,4 +50,4 @@ removeOldGenerations $profilesDir if $removeOld;
|
||||
|
||||
|
||||
# Run the actual garbage collector.
|
||||
exec "@bindir@/nix-store", "--gc", @args;
|
||||
exec "$binDir/nix-store", "--gc", @args;
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
#! @perl@ -w
|
||||
#! @perl@ -w -I@libexecdir@/nix
|
||||
|
||||
my $binDir = $ENV{"NIX_BIN_DIR"};
|
||||
$binDir = "@bindir@" unless defined $binDir;
|
||||
use SSH;
|
||||
|
||||
my $binDir = $ENV{"NIX_BIN_DIR"} || "@bindir@";
|
||||
|
||||
|
||||
if (scalar @ARGV < 1) {
|
||||
@@ -15,12 +16,11 @@ EOF
|
||||
|
||||
# Get the target host.
|
||||
my $sshHost;
|
||||
my @sshOpts = split ' ', ($ENV{"NIX_SSHOPTS"} or "");
|
||||
|
||||
my $sign = 0;
|
||||
|
||||
my $compressor = "cat";
|
||||
my $decompressor = "cat";
|
||||
my $compressor = "";
|
||||
my $decompressor = "";
|
||||
|
||||
my $toMode = 1;
|
||||
|
||||
@@ -35,8 +35,8 @@ while (@ARGV) {
|
||||
$sign = 1;
|
||||
}
|
||||
elsif ($arg eq "--gzip") {
|
||||
$compressor = "gzip";
|
||||
$decompressor = "gunzip";
|
||||
$compressor = "| gzip";
|
||||
$decompressor = "gunzip |";
|
||||
}
|
||||
elsif ($arg eq "--from") {
|
||||
$toMode = 0;
|
||||
@@ -53,40 +53,30 @@ while (@ARGV) {
|
||||
}
|
||||
|
||||
|
||||
openSSHConnection $sshHost or die "$0: unable to start SSH\n";
|
||||
|
||||
|
||||
if ($toMode) { # Copy TO the remote machine.
|
||||
|
||||
my @allStorePaths;
|
||||
my %storePathsSeen;
|
||||
|
||||
foreach my $storePath (@storePaths) {
|
||||
# $arg might be a symlink to the store, so resolve it.
|
||||
my $storePath2 = (`$binDir/nix-store --query --resolve '$storePath'`
|
||||
or die "cannot resolve `$storePath'");
|
||||
chomp $storePath2;
|
||||
|
||||
# Get the closure of this path.
|
||||
my $pid = open(READ,
|
||||
"$binDir/nix-store --query --requisites '$storePath2'|") or die;
|
||||
# Get the closure of this path.
|
||||
my $pid = open(READ, "set -f; $binDir/nix-store --query --requisites @storePaths|") or die;
|
||||
|
||||
while (<READ>) {
|
||||
chomp;
|
||||
die "bad: $_" unless /^\//;
|
||||
if (!defined $storePathsSeen{$_}) {
|
||||
push @allStorePaths, $_;
|
||||
$storePathsSeen{$_} = 1;
|
||||
}
|
||||
}
|
||||
|
||||
close READ or die "nix-store failed: $?";
|
||||
while (<READ>) {
|
||||
chomp;
|
||||
die "bad: $_" unless /^\//;
|
||||
push @allStorePaths, $_;
|
||||
}
|
||||
|
||||
close READ or die "nix-store failed: $?";
|
||||
|
||||
|
||||
# Ask the remote host which paths are invalid.
|
||||
open(READ, "ssh @sshOpts $sshHost nix-store --check-validity --print-invalid @allStorePaths|");
|
||||
open(READ, "set -f; ssh $sshHost @sshOpts nix-store --check-validity --print-invalid @allStorePaths|");
|
||||
my @missing = ();
|
||||
while (<READ>) {
|
||||
chomp;
|
||||
print STDERR "target machine needs $_\n";
|
||||
push @missing, $_;
|
||||
}
|
||||
close READ or die;
|
||||
@@ -94,9 +84,11 @@ if ($toMode) { # Copy TO the remote machine.
|
||||
|
||||
# Export the store paths and import them on the remote machine.
|
||||
if (scalar @missing > 0) {
|
||||
print STDERR "copying these missing paths:\n";
|
||||
print STDERR " $_\n" foreach @missing;
|
||||
my $extraOpts = "";
|
||||
$extraOpts .= "--sign" if $sign == 1;
|
||||
system("nix-store --export $extraOpts @missing | $compressor | ssh @sshOpts $sshHost '$decompressor | nix-store --import'") == 0
|
||||
system("set -f; nix-store --export $extraOpts @missing $compressor | ssh $sshHost @sshOpts '$decompressor nix-store --import'") == 0
|
||||
or die "copying store paths to remote machine `$sshHost' failed: $?";
|
||||
}
|
||||
|
||||
@@ -109,29 +101,24 @@ else { # Copy FROM the remote machine.
|
||||
# machine. Paths are assumed to be store paths; there is no
|
||||
# resolution (following of symlinks).
|
||||
my $pid = open(READ,
|
||||
"ssh @sshOpts $sshHost nix-store --query --requisites @storePaths|") or die;
|
||||
"set -f; ssh @sshOpts $sshHost nix-store --query --requisites @storePaths|") or die;
|
||||
|
||||
my @allStorePaths;
|
||||
my %storePathsSeen;
|
||||
|
||||
while (<READ>) {
|
||||
chomp;
|
||||
die "bad: $_" unless /^\//;
|
||||
if (!defined $storePathsSeen{$_}) {
|
||||
push @allStorePaths, $_;
|
||||
$storePathsSeen{$_} = 1;
|
||||
}
|
||||
push @allStorePaths, $_;
|
||||
}
|
||||
|
||||
close READ or die "nix-store on remote machine `$sshHost' failed: $?";
|
||||
|
||||
|
||||
# What paths are already valid locally?
|
||||
open(READ, "@bindir@/nix-store --check-validity --print-invalid @allStorePaths|");
|
||||
open(READ, "set -f; @bindir@/nix-store --check-validity --print-invalid @allStorePaths|");
|
||||
my @missing = ();
|
||||
while (<READ>) {
|
||||
chomp;
|
||||
print STDERR "local machine needs $_\n";
|
||||
push @missing, $_;
|
||||
}
|
||||
close READ or die;
|
||||
@@ -139,10 +126,12 @@ else { # Copy FROM the remote machine.
|
||||
|
||||
# Export the store paths on the remote machine and import them on locally.
|
||||
if (scalar @missing > 0) {
|
||||
print STDERR "copying these missing paths:\n";
|
||||
print STDERR " $_\n" foreach @missing;
|
||||
my $extraOpts = "";
|
||||
$extraOpts .= "--sign" if $sign == 1;
|
||||
system("ssh @sshOpts $sshHost 'nix-store --export $extraOpts @missing | $compressor' | $decompressor | @bindir@/nix-store --import") == 0
|
||||
or die "copying store paths to remote machine `$sshHost' failed: $?";
|
||||
system("set -f; ssh $sshHost @sshOpts 'nix-store --export $extraOpts @missing $compressor' | $decompressor @bindir@/nix-store --import") == 0
|
||||
or die "copying store paths from remote machine `$sshHost' failed: $?";
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
42
scripts/nix-generate-patches.in
Normal file
42
scripts/nix-generate-patches.in
Normal file
@@ -0,0 +1,42 @@
|
||||
#! @perl@ -w -I@libexecdir@/nix
|
||||
|
||||
use strict;
|
||||
use File::Temp qw(tempdir);
|
||||
use NixManifest;
|
||||
use GeneratePatches;
|
||||
|
||||
if (scalar @ARGV != 5) {
|
||||
print STDERR <<EOF;
|
||||
Usage: nix-generate-patches NAR-DIR PATCH-DIR PATCH-URI OLD-MANIFEST NEW-MANIFEST
|
||||
|
||||
This command generates binary patches between NAR files listed in
|
||||
OLD-MANIFEST and NEW-MANIFEST. The patches are written to the
|
||||
directory PATCH-DIR, and the prefix PATCH-URI is used to generate URIs
|
||||
for the patches. The patches are added to NEW-MANIFEST. All NARs are
|
||||
required to exist in NAR-DIR. Patches are generated between
|
||||
succeeding versions of packages with the same name.
|
||||
EOF
|
||||
exit 1;
|
||||
}
|
||||
|
||||
my $narPath = $ARGV[0];
|
||||
my $patchesPath = $ARGV[1];
|
||||
my $patchesURL = $ARGV[2];
|
||||
my $srcManifest = $ARGV[3];
|
||||
my $dstManifest = $ARGV[4];
|
||||
|
||||
my (%srcNarFiles, %srcLocalPaths, %srcPatches);
|
||||
readManifest $srcManifest, \%srcNarFiles, \%srcLocalPaths, \%srcPatches;
|
||||
|
||||
my (%dstNarFiles, %dstLocalPaths, %dstPatches);
|
||||
readManifest $dstManifest, \%dstNarFiles, \%dstLocalPaths, \%dstPatches;
|
||||
|
||||
my $tmpDir = tempdir("nix-generate-patches.XXXXXX", CLEANUP => 1, TMPDIR => 1)
|
||||
or die "cannot create a temporary directory";
|
||||
|
||||
generatePatches \%srcNarFiles, \%dstNarFiles, \%srcPatches, \%dstPatches,
|
||||
$narPath, $patchesPath, $patchesURL, $tmpDir;
|
||||
|
||||
propagatePatches \%srcPatches, \%dstNarFiles, \%dstPatches;
|
||||
|
||||
writeManifest $dstManifest, \%dstNarFiles, \%dstPatches;
|
||||
51
scripts/nix-http-export.cgi.in
Executable file
51
scripts/nix-http-export.cgi.in
Executable file
@@ -0,0 +1,51 @@
|
||||
#! /bin/sh
|
||||
|
||||
export HOME=/tmp
|
||||
export NIX_REMOTE=daemon
|
||||
|
||||
TMP_DIR="${TMP_DIR:-/tmp/nix-export}"
|
||||
|
||||
@coreutils@/mkdir -p "$TMP_DIR" || true
|
||||
@coreutils@/chmod a+r "$TMP_DIR"
|
||||
|
||||
needed_path="?$QUERY_STRING"
|
||||
needed_path="${needed_path#*[?&]needed_path=}"
|
||||
needed_path="${needed_path%%&*}"
|
||||
#needed_path="$(echo $needed_path | ./unhttp)"
|
||||
needed_path="${needed_path//%2B/+}"
|
||||
needed_path="${needed_path//%3D/=}"
|
||||
|
||||
echo needed_path: "$needed_path" >&2
|
||||
|
||||
NIX_STORE="${NIX_STORE_DIR:-/nix/store}"
|
||||
|
||||
echo NIX_STORE: "${NIX_STORE}" >&2
|
||||
|
||||
full_path="${NIX_STORE}"/"$needed_path"
|
||||
|
||||
if [ "$needed_path" != "${needed_path%.drv}" ]; then
|
||||
echo "Status: 403 You should create the derivation file yourself"
|
||||
echo "Content-Type: text/plain"
|
||||
echo
|
||||
echo "Refusing to disclose derivation contents"
|
||||
exit
|
||||
fi
|
||||
|
||||
if @bindir@/nix-store --check-validity "$full_path"; then
|
||||
if ! [ -e nix-export/"$needed_path".nar.gz ]; then
|
||||
@bindir@/nix-store --export "$full_path" | @gzip@ > "$TMP_DIR"/"$needed_path".nar.gz
|
||||
@coreutils@/ln -fs "$TMP_DIR"/"$needed_path".nar.gz nix-export/"$needed_path".nar.gz
|
||||
fi;
|
||||
echo "Status: 301 Moved"
|
||||
echo "Location: nix-export/"$needed_path".nar.gz"
|
||||
echo
|
||||
else
|
||||
echo "Status: 404 No such path found"
|
||||
echo "Content-Type: text/plain"
|
||||
echo
|
||||
echo "Path not found:"
|
||||
echo "$needed_path"
|
||||
echo "checked:"
|
||||
echo "$full_path"
|
||||
fi
|
||||
|
||||
@@ -3,6 +3,8 @@
|
||||
use strict;
|
||||
use File::Temp qw(tempdir);
|
||||
|
||||
my $binDir = $ENV{"NIX_BIN_DIR"} || "@bindir@";
|
||||
|
||||
|
||||
sub usageError {
|
||||
print STDERR <<EOF;
|
||||
@@ -59,7 +61,7 @@ if ($interactive && !defined $ENV{"NIX_HAVE_TERMINAL"}) {
|
||||
$ENV{"NIX_HAVE_TERMINAL"} = "1";
|
||||
$ENV{"LD_LIBRARY_PATH"} = "";
|
||||
foreach my $term ("xterm", "konsole", "gnome-terminal", "xterm") {
|
||||
exec($term, "-e", "@bindir@/nix-install-package", @ARGV);
|
||||
exec($term, "-e", "$binDir/nix-install-package", @ARGV);
|
||||
}
|
||||
die "cannot execute `xterm'";
|
||||
}
|
||||
@@ -121,13 +123,18 @@ if ($interactive) {
|
||||
}
|
||||
|
||||
|
||||
# Store the manifest in the temporary directory so that we don't
|
||||
# pollute /nix/var/nix/manifests.
|
||||
$ENV{NIX_MANIFESTS_DIR} = $tmpDir;
|
||||
|
||||
|
||||
print "\nPulling manifests...\n";
|
||||
system("@bindir@/nix-pull", $manifestURL) == 0
|
||||
system("$binDir/nix-pull", $manifestURL) == 0
|
||||
or barf "nix-pull failed: $?";
|
||||
|
||||
|
||||
print "\nInstalling package...\n";
|
||||
system("@bindir@/nix-env", "--install", $outPath, "--force-name", $drvName, @extraNixEnvArgs) == 0
|
||||
system("$binDir/nix-env", "--install", $outPath, "--force-name", $drvName, @extraNixEnvArgs) == 0
|
||||
or barf "nix-env failed: $?";
|
||||
|
||||
|
||||
|
||||
@@ -1,75 +0,0 @@
|
||||
#! @perl@ -w
|
||||
|
||||
# This tool computes the closure of a path (using "nix-store --query
|
||||
# --requisites") and puts the contents of each path in the closure in
|
||||
# a big NAR archive that can be installed on another Nix installation
|
||||
# using "nix-unpack-closure".
|
||||
|
||||
# TODO: make this program "streamy", i.e., don't use a temporary
|
||||
# directory.
|
||||
|
||||
use strict;
|
||||
use File::Temp qw(tempdir);
|
||||
|
||||
my $binDir = $ENV{"NIX_BIN_DIR"};
|
||||
$binDir = "@bindir@" unless defined $binDir;
|
||||
|
||||
my $tmpDir = tempdir("nix-pack-closure.XXXXXX", CLEANUP => 1, TMPDIR => 1)
|
||||
or die "cannot create a temporary directory";
|
||||
|
||||
mkdir "$tmpDir/contents", 0755 or die;
|
||||
mkdir "$tmpDir/references", 0755 or die;
|
||||
mkdir "$tmpDir/derivers", 0755 or die;
|
||||
|
||||
open TOPLEVEL, ">$tmpDir/top-level" or die;
|
||||
|
||||
|
||||
my %storePaths;
|
||||
|
||||
|
||||
while (@ARGV) {
|
||||
my $storePath = shift @ARGV;
|
||||
|
||||
# $storePath might be a symlink to the store, so resolve it.
|
||||
$storePath = (`$binDir/nix-store --query --resolve '$storePath'`
|
||||
or die "cannot resolve `$storePath'");
|
||||
chomp $storePath;
|
||||
print TOPLEVEL $storePath, "\n";
|
||||
|
||||
# Get the closure of this path.
|
||||
my $pid = open(READ,
|
||||
"$binDir/nix-store --query --requisites '$storePath'|") or die;
|
||||
|
||||
while (<READ>) {
|
||||
chomp;
|
||||
die "bad: $_" unless /^\//;
|
||||
$storePaths{$_} = "";
|
||||
}
|
||||
|
||||
close READ or die "nix-store failed: $?";
|
||||
}
|
||||
|
||||
|
||||
close TOPLEVEL or die;
|
||||
|
||||
|
||||
foreach my $storePath (sort(keys %storePaths)) {
|
||||
print STDERR "packing `$storePath'...\n";
|
||||
|
||||
$storePath =~ /\/([^\/]+)$/;
|
||||
my $name = $1;
|
||||
|
||||
system("$binDir/nix-store --dump '$storePath' > $tmpDir/contents/$name") == 0
|
||||
or die "nix-store --dump failed on `$storePath': $?";
|
||||
|
||||
system("$binDir/nix-store --query --references '$storePath' > $tmpDir/references/$name") == 0
|
||||
or die "nix-store --query --references failed on `$storePath': $?";
|
||||
|
||||
system("$binDir/nix-store --query --deriver '$storePath' > $tmpDir/derivers/$name") == 0
|
||||
or die "nix-store --query --deriver failed on `$storePath': $?";
|
||||
}
|
||||
|
||||
|
||||
# Write a NAR archive of everything to standard output.
|
||||
system("nix-store --dump '$tmpDir'") == 0
|
||||
or die "nix-store --dump failed";
|
||||
@@ -21,7 +21,10 @@ if test -z "$url"; then
|
||||
exit 1
|
||||
fi
|
||||
|
||||
name=$(basename "$url")
|
||||
# Handle escaped characters in the URI. `+', `=' and `?' are the only
|
||||
# characters that are valid in Nix store path names but have a special
|
||||
# meaning in URIs.
|
||||
name=$(basename "$url" | @sed@ -e 's/%2b/+/g' -e 's/%3d/=/g' -e 's/%3f/\?/g')
|
||||
if test -z "$name"; then echo "invalid url"; exit 1; fi
|
||||
|
||||
|
||||
@@ -37,6 +40,7 @@ fi
|
||||
|
||||
|
||||
mkTempDir() {
|
||||
if test -n "$tmpPath"; then return; fi
|
||||
local i=0
|
||||
while true; do
|
||||
if test -z "$TMPDIR"; then TMPDIR=/tmp; fi
|
||||
@@ -59,11 +63,32 @@ removeTempDir() {
|
||||
|
||||
|
||||
doDownload() {
|
||||
@curl@ $cacheFlags --fail -# --location --max-redirs 20 --disable-epsv \
|
||||
@curl@ $cacheFlags --fail --location --max-redirs 20 --disable-epsv \
|
||||
--cookie-jar $tmpPath/cookies "$url" -o $tmpFile
|
||||
}
|
||||
|
||||
|
||||
# Hack to support the mirror:// scheme from Nixpkgs.
|
||||
if test "${url:0:9}" = "mirror://"; then
|
||||
if test -z "$NIXPKGS_ALL"; then
|
||||
echo "Resolving mirror:// URLs requires Nixpkgs. Please point \$NIXPKGS_ALL at a Nixpkgs tree." >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
mkTempDir
|
||||
nix-build "$NIXPKGS_ALL" -A resolveMirrorURLs --argstr url "$url" -o $tmpPath/urls > /dev/null
|
||||
|
||||
expanded=($(cat $tmpPath/urls))
|
||||
if test "${#expanded[*]}" = 0; then
|
||||
echo "$0: cannot resolve $url." >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "$url expands to ${expanded[*]} (using ${expanded[0]})" >&2
|
||||
url="${expanded[0]}"
|
||||
fi
|
||||
|
||||
|
||||
# If we don't know the hash or a file with that hash doesn't exist,
|
||||
# download the file and add it to the store.
|
||||
if test -z "$finalPath"; then
|
||||
@@ -78,7 +103,7 @@ if test -z "$finalPath"; then
|
||||
# garbage-collected independently.
|
||||
if test -n "$NIX_DOWNLOAD_CACHE"; then
|
||||
echo -n "$url" > $tmpPath/url
|
||||
urlHash=$(nix-hash --type sha256 --base32 --flat $tmpPath/url)
|
||||
urlHash=$(@bindir@/nix-hash --type sha256 --base32 --flat $tmpPath/url)
|
||||
echo "$url" > "$NIX_DOWNLOAD_CACHE/$urlHash.url"
|
||||
cachedHashFN="$NIX_DOWNLOAD_CACHE/$urlHash.$hashType"
|
||||
cachedTimestampFN="$NIX_DOWNLOAD_CACHE/$urlHash.stamp"
|
||||
|
||||
@@ -7,5 +7,12 @@ if test -n "$HOME"; then
|
||||
@coreutils@/ln -s "$_NIX_DEF_LINK" "$NIX_LINK"
|
||||
fi
|
||||
|
||||
export PATH=$NIX_LINK/bin:@prefix@/bin:$PATH
|
||||
export PATH=$NIX_LINK/bin:$PATH
|
||||
fi
|
||||
|
||||
# This is a quick hack to make fontconfig-based packages in Nixpkgs
|
||||
# work out of the box on non-NixOS systems. Of course, we should
|
||||
# really fix fontconfig...
|
||||
if test -z "$FONTCONFIG_FILE" -a -e /etc/fonts/fonts.conf; then
|
||||
export FONTCONFIG_FILE=/etc/fonts/fonts.conf
|
||||
fi
|
||||
|
||||
@@ -2,28 +2,28 @@
|
||||
|
||||
use strict;
|
||||
use File::Temp qw(tempdir);
|
||||
use readmanifest;
|
||||
use NixManifest;
|
||||
|
||||
my $tmpDir = tempdir("nix-pull.XXXXXX", CLEANUP => 1, TMPDIR => 1)
|
||||
or die "cannot create a temporary directory";
|
||||
|
||||
my $binDir = $ENV{"NIX_BIN_DIR"};
|
||||
$binDir = "@bindir@" unless defined $binDir;
|
||||
|
||||
my $libexecDir = $ENV{"NIX_LIBEXEC_DIR"};
|
||||
$libexecDir = "@libexecdir@" unless defined $libexecDir;
|
||||
|
||||
my $stateDir = $ENV{"NIX_STATE_DIR"};
|
||||
$stateDir = "@localstatedir@/nix" unless defined $stateDir;
|
||||
|
||||
my $storeDir = $ENV{"NIX_STORE_DIR"};
|
||||
$storeDir = "@storedir@" unless defined $storeDir;
|
||||
my $binDir = $ENV{"NIX_BIN_DIR"} || "@bindir@";
|
||||
my $libexecDir = ($ENV{"NIX_LIBEXEC_DIR"} or "@libexecdir@");
|
||||
my $storeDir = ($ENV{"NIX_STORE_DIR"} or "@storedir@");
|
||||
my $stateDir = ($ENV{"NIX_STATE_DIR"} or "@localstatedir@/nix");
|
||||
my $manifestDir = ($ENV{"NIX_MANIFESTS_DIR"} or "$stateDir/manifests");
|
||||
|
||||
|
||||
# Prevent access problems in shared-stored installations.
|
||||
umask 0022;
|
||||
|
||||
|
||||
# Create the manifests directory if it doesn't exist.
|
||||
if (! -e $manifestDir) {
|
||||
mkdir $manifestDir, 0755 or die "cannot create directory `$manifestDir'";
|
||||
}
|
||||
|
||||
|
||||
# Process the URLs specified on the command line.
|
||||
my %narFiles;
|
||||
my %localPaths;
|
||||
@@ -51,7 +51,7 @@ sub processURL {
|
||||
|
||||
# First see if a bzipped manifest is available.
|
||||
if (system("@curl@ --fail --silent --head '$url'.bz2 > /dev/null") == 0) {
|
||||
print "obtaining list of Nix archives at `$url.bz2'...\n";
|
||||
print "fetching list of Nix archives at `$url.bz2'...\n";
|
||||
my $bzipped = downloadFile "$url.bz2";
|
||||
|
||||
$manifest = "$tmpDir/MANIFEST";
|
||||
@@ -69,10 +69,11 @@ sub processURL {
|
||||
print "obtaining list of Nix archives at `$url'...\n";
|
||||
$manifest = downloadFile $url;
|
||||
}
|
||||
|
||||
my $version = readManifest($manifest, \%narFiles, \%localPaths, \%patches);
|
||||
|
||||
if (readManifest($manifest, \%narFiles, \%localPaths, \%patches) < 3) {
|
||||
die "`$url' is not manifest or it is too old (i.e., for Nix <= 0.7)\n";
|
||||
}
|
||||
die "`$url' is not a manifest or it is too old (i.e., for Nix <= 0.7)\n" if $version < 3;
|
||||
die "manifest `$url' is too new\n" if $version >= 5;
|
||||
|
||||
if ($skipWrongStore) {
|
||||
foreach my $path (keys %narFiles) {
|
||||
@@ -91,11 +92,31 @@ sub processURL {
|
||||
my $hash = `$binDir/nix-hash --flat '$manifest'`
|
||||
or die "cannot hash `$manifest'";
|
||||
chomp $hash;
|
||||
|
||||
my $urlFile = "$manifestDir/$baseName-$hash.url";
|
||||
open URL, ">$urlFile" or die "cannot create `$urlFile'";
|
||||
print URL "$url";
|
||||
close URL;
|
||||
|
||||
my $finalPath = "$stateDir/manifests/$baseName-$hash.nixmanifest";
|
||||
|
||||
system("@coreutils@/ln", "-sfn", "$manifest", "$finalPath") == 0
|
||||
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 $url eq $url2;
|
||||
my $base = $urlFile2; $base =~ s/.url$//;
|
||||
unlink "${base}.url";
|
||||
unlink "${base}.nixmanifest";
|
||||
}
|
||||
}
|
||||
|
||||
while (@ARGV) {
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
use strict;
|
||||
use File::Temp qw(tempdir);
|
||||
use readmanifest;
|
||||
use NixManifest;
|
||||
|
||||
my $hashAlgo = "sha256";
|
||||
|
||||
@@ -16,8 +16,7 @@ my $curl = "@curl@ --fail --silent";
|
||||
my $extraCurlFlags = ${ENV{'CURL_FLAGS'}};
|
||||
$curl = "$curl $extraCurlFlags" if defined $extraCurlFlags;
|
||||
|
||||
my $binDir = $ENV{"NIX_BIN_DIR"};
|
||||
$binDir = "@bindir@" unless defined $binDir;
|
||||
my $binDir = $ENV{"NIX_BIN_DIR"} || "@bindir@";
|
||||
|
||||
my $dataDir = $ENV{"NIX_DATA_DIR"};
|
||||
$dataDir = "@datadir@" unless defined $dataDir;
|
||||
@@ -107,7 +106,7 @@ foreach my $storePath (@storePaths) {
|
||||
# Construct a Nix expression that creates a Nix archive.
|
||||
my $nixexpr =
|
||||
"((import $dataDir/nix/corepkgs/nar/nar.nix) " .
|
||||
"{storePath = builtins.toPath \"$storePath\"; system = \"@system@\"; hashAlgo = \"$hashAlgo\";}) ";
|
||||
"{storePath = builtins.storePath \"$storePath\"; system = \"@system@\"; hashAlgo = \"$hashAlgo\";}) ";
|
||||
|
||||
print NIX $nixexpr;
|
||||
}
|
||||
@@ -129,7 +128,7 @@ while (<READ>) {
|
||||
close READ or die "nix-instantiate failed: $?";
|
||||
|
||||
|
||||
# Realise the store expressions.
|
||||
# Build the derivations.
|
||||
print STDERR "creating archives...\n";
|
||||
|
||||
my @narPaths;
|
||||
@@ -141,12 +140,7 @@ while (scalar @tmp > 0) {
|
||||
my @tmp2 = @tmp[0..$n - 1];
|
||||
@tmp = @tmp[$n..scalar @tmp - 1];
|
||||
|
||||
# Note: we disable build hooks because of the impure path
|
||||
# reference (see above). Even if that is fixed, using a hook
|
||||
# probably wouldn't make that much sense; pumping lots of data
|
||||
# around just to compress them won't gain that much.
|
||||
$ENV{"NIX_BUILD_HOOK"} = "";
|
||||
my $pid = open(READ, "$binDir/nix-store --no-build-hook --realise @tmp2|")
|
||||
my $pid = open(READ, "$binDir/nix-store --realise @tmp2|")
|
||||
or die "cannot run nix-store";
|
||||
while (<READ>) {
|
||||
chomp;
|
||||
@@ -178,12 +172,6 @@ for (my $n = 0; $n < scalar @storePaths; $n++) {
|
||||
$narbz2Hash =~ /^[0-9a-z]+$/ or die "invalid hash";
|
||||
close HASH;
|
||||
|
||||
open HASH, "$narDir/nar-hash" or die "cannot open nar-hash";
|
||||
my $narHash = <HASH>;
|
||||
chomp $narHash;
|
||||
$narHash =~ /^[0-9a-z]+$/ or die "invalid hash";
|
||||
close HASH;
|
||||
|
||||
my $narName = "$narbz2Hash.nar.bz2";
|
||||
|
||||
my $narFile = "$narDir/$narName";
|
||||
@@ -201,6 +189,14 @@ for (my $n = 0; $n < scalar @storePaths; $n++) {
|
||||
chomp $deriver;
|
||||
$deriver = "" if $deriver eq "unknown-deriver";
|
||||
|
||||
my $narHash = `$binDir/nix-store --query --hash '$storePath'`;
|
||||
die "cannot query hash for `$storePath'" if $? != 0;
|
||||
chomp $narHash;
|
||||
|
||||
my $narSize = `$binDir/nix-store --query --size '$storePath'`;
|
||||
die "cannot query size for `$storePath'" if $? != 0;
|
||||
chomp $narSize;
|
||||
|
||||
my $url;
|
||||
if ($localCopy) {
|
||||
$url = "$targetArchivesUrl/$narName";
|
||||
@@ -211,7 +207,8 @@ for (my $n = 0; $n < scalar @storePaths; $n++) {
|
||||
{ url => $url
|
||||
, hash => "$hashAlgo:$narbz2Hash"
|
||||
, size => $narbz2Size
|
||||
, narHash => "$hashAlgo:$narHash"
|
||||
, narHash => "$narHash"
|
||||
, narSize => $narSize
|
||||
, references => $references
|
||||
, deriver => $deriver
|
||||
}
|
||||
@@ -224,13 +221,14 @@ writeManifest $manifest, \%narFiles, \%patches;
|
||||
sub copyFile {
|
||||
my $src = shift;
|
||||
my $dst = shift;
|
||||
system("@coreutils@/cp", $src, "$dst.tmp") == 0 or die "cannot copy file";
|
||||
rename("$dst.tmp", "$dst") or die "cannot rename file";
|
||||
my $tmp = "$dst.tmp.$$";
|
||||
system("@coreutils@/cp", $src, $tmp) == 0 or die "cannot copy file";
|
||||
rename($tmp, $dst) or die "cannot rename file: $!";
|
||||
}
|
||||
|
||||
|
||||
# Upload the archives.
|
||||
print STDERR "uploading archives...\n";
|
||||
# Upload/copy the archives.
|
||||
print STDERR "uploading/copying archives...\n";
|
||||
|
||||
sub archiveExists {
|
||||
my $name = shift;
|
||||
@@ -244,9 +242,12 @@ foreach my $narArchive (@narArchives) {
|
||||
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, "$localArchivesDir/$basename";
|
||||
copyFile $narArchive, $dst;
|
||||
}
|
||||
}
|
||||
else {
|
||||
|
||||
171
scripts/nix-reduce-build.in
Normal file
171
scripts/nix-reduce-build.in
Normal file
@@ -0,0 +1,171 @@
|
||||
#! @shell@
|
||||
|
||||
WORKING_DIRECTORY=$(mktemp -d "${TMPDIR:-/tmp}"/nix-reduce-build-XXXXXX);
|
||||
cd "$WORKING_DIRECTORY";
|
||||
|
||||
if test -z "$1" || test "a--help" = "a$1" ; then
|
||||
echo 'nix-reduce-build (paths or Nix expressions) -- (package sources)' >&2
|
||||
echo As in: >&2
|
||||
echo nix-reduce-build /etc/nixos/nixos -- ssh://user@somewhere.nowhere.example.org >&2
|
||||
echo nix-reduce-build /etc/nixos/nixos -- \\
|
||||
echo " " \''http://somewhere.nowhere.example.org/nix/nix-http-export.cgi?needed_path='\' >&2
|
||||
echo " store path name will be added into the end of the URL" >&2
|
||||
echo nix-reduce-build /etc/nixos/nixos -- file://home/user/nar/ >&2
|
||||
echo " that should be a directory where gzipped 'nix-store --export' ">&2
|
||||
echo " files are located (they should have .nar.gz extension)" >&2
|
||||
echo " Or all together: " >&2
|
||||
echo -e nix-reduce-build /expr.nix /e2.nix -- \\\\\\\n\
|
||||
" ssh://a@b.example.com http://n.example.com/get-nar?q= file://nar/" >&2
|
||||
echo " Also supports best-effort local builds of failing expression set:" >&2
|
||||
echo "nix-reduce-build /e.nix -- nix-daemon:// nix-self://" >&2
|
||||
echo " nix-daemon:// builds using daemon"
|
||||
echo " nix-self:// builds directly using nix-store from current installation" >&2
|
||||
echo " nix-daemon-fixed:// and nix-self-fixed:// do the same, but only for" >&2;
|
||||
echo "derivations with specified output hash (sha256, sha1 or md5)." >&2
|
||||
echo " nix-daemon-substitute:// and nix-self-substitute:// try to substitute" >&2;
|
||||
echo "maximum amount of paths" >&2;
|
||||
echo " nix-daemon-build:// and nix-self-build:// try to build (not substitute)" >&2;
|
||||
echo "maximum amount of paths" >&2;
|
||||
echo " If no package sources are specified, required paths are listed." >&2;
|
||||
exit;
|
||||
fi;
|
||||
|
||||
while ! test "$1" = "--" || test "$1" = "" ; do
|
||||
echo "$1" >> initial; >&2
|
||||
shift;
|
||||
done
|
||||
shift;
|
||||
echo Will work on $(cat initial | wc -l) targets. >&2
|
||||
|
||||
while read ; do
|
||||
case "$REPLY" in
|
||||
${NIX_STORE_DIR:-/nix/store}/*)
|
||||
echo "$REPLY" >> paths; >&2
|
||||
;;
|
||||
*)
|
||||
(
|
||||
IFS=: ;
|
||||
nix-instantiate $REPLY >> paths;
|
||||
);
|
||||
;;
|
||||
esac;
|
||||
done < initial;
|
||||
echo Proceeding $(cat paths | wc -l) paths. >&2
|
||||
|
||||
while read; do
|
||||
case "$REPLY" in
|
||||
*.drv)
|
||||
echo "$REPLY" >> derivers; >&2
|
||||
;;
|
||||
*)
|
||||
nix-store --query --deriver "$REPLY" >>derivers;
|
||||
;;
|
||||
esac;
|
||||
done < paths;
|
||||
echo Found $(cat derivers | wc -l) derivers. >&2
|
||||
|
||||
cat derivers | xargs nix-store --query -R > derivers-closure;
|
||||
echo Proceeding at most $(cat derivers-closure | wc -l) derivers. >&2
|
||||
|
||||
cat derivers-closure | egrep '[.]drv$' | xargs nix-store --query --outputs > wanted-paths;
|
||||
cat derivers-closure | egrep -v '[.]drv$' >> wanted-paths;
|
||||
echo Prepared $(cat wanted-paths | wc -l) paths to get. >&2
|
||||
|
||||
cat wanted-paths | xargs nix-store --check-validity --print-invalid > needed-paths;
|
||||
echo We need $(cat needed-paths | wc -l) paths. >&2
|
||||
|
||||
egrep '[.]drv$' derivers-closure > critical-derivers;
|
||||
|
||||
if test -z "$1" ; then
|
||||
cat needed-paths;
|
||||
fi;
|
||||
|
||||
refresh_critical_derivers() {
|
||||
echo "Finding needed derivers..." >&2;
|
||||
cat critical-derivers | while read; do
|
||||
if ! (nix-store --query --outputs "$REPLY" | xargs nix-store --check-validity &> /dev/null;); then
|
||||
echo "$REPLY";
|
||||
fi;
|
||||
done > new-critical-derivers;
|
||||
mv new-critical-derivers critical-derivers;
|
||||
echo The needed paths are realized by $(cat critical-derivers | wc -l) derivers. >&2
|
||||
}
|
||||
|
||||
build_here() {
|
||||
cat critical-derivers | while read; do
|
||||
echo "Realising $REPLY using nix-daemon" >&2
|
||||
@bindir@/nix-store -r "${REPLY}"
|
||||
done;
|
||||
}
|
||||
|
||||
try_to_substitute(){
|
||||
cat needed-paths | while read ; do
|
||||
echo "Building $REPLY using nix-daemon" >&2
|
||||
@bindir@/nix-store -r "${NIX_STORE_DIR:-/nix/store}/${REPLY##*/}"
|
||||
done;
|
||||
}
|
||||
|
||||
for i in "$@"; do
|
||||
sshHost="${i#ssh://}";
|
||||
httpHost="${i#http://}";
|
||||
httpsHost="${i#https://}";
|
||||
filePath="${i#file:/}";
|
||||
if [ "$i" != "$sshHost" ]; then
|
||||
cat needed-paths | while read; do
|
||||
echo "Getting $REPLY and its closure over ssh" >&2
|
||||
nix-copy-closure --from "$sshHost" --gzip "$REPLY" </dev/null || true;
|
||||
done;
|
||||
elif [ "$i" != "$httpHost" ] || [ "$i" != "$httpsHost" ]; then
|
||||
cat needed-paths | while read; do
|
||||
echo "Getting $REPLY over http/https" >&2
|
||||
curl ${BAD_CERTIFICATE:+-k} -L "$i${REPLY##*/}" | gunzip | nix-store --import;
|
||||
done;
|
||||
elif [ "$i" != "$filePath" ] ; then
|
||||
cat needed-paths | while read; do
|
||||
echo "Installing $REPLY from file" >&2
|
||||
gunzip < "$filePath/${REPLY##*/}".nar.gz | nix-store --import;
|
||||
done;
|
||||
elif [ "$i" = "nix-daemon://" ] ; then
|
||||
NIX_REMOTE=daemon try_to_substitute;
|
||||
refresh_critical_derivers;
|
||||
NIX_REMOTE=daemon build_here;
|
||||
elif [ "$i" = "nix-self://" ] ; then
|
||||
NIX_REMOTE= try_to_substitute;
|
||||
refresh_critical_derivers;
|
||||
NIX_REMOTE= build_here;
|
||||
elif [ "$i" = "nix-daemon-fixed://" ] ; then
|
||||
refresh_critical_derivers;
|
||||
|
||||
cat critical-derivers | while read; do
|
||||
if egrep '"(md5|sha1|sha256)"' "$REPLY" &>/dev/null; then
|
||||
echo "Realising $REPLY using nix-daemon" >&2
|
||||
NIX_REMOTE=daemon @bindir@/nix-store -r "${REPLY}"
|
||||
fi;
|
||||
done;
|
||||
elif [ "$i" = "nix-self-fixed://" ] ; then
|
||||
refresh_critical_derivers;
|
||||
|
||||
cat critical-derivers | while read; do
|
||||
if egrep '"(md5|sha1|sha256)"' "$REPLY" &>/dev/null; then
|
||||
echo "Realising $REPLY using direct Nix build" >&2
|
||||
NIX_REMOTE= @bindir@/nix-store -r "${REPLY}"
|
||||
fi;
|
||||
done;
|
||||
elif [ "$i" = "nix-daemon-substitute://" ] ; then
|
||||
NIX_REMOTE=daemon try_to_substitute;
|
||||
elif [ "$i" = "nix-self-substitute://" ] ; then
|
||||
NIX_REMOTE= try_to_substitute;
|
||||
elif [ "$i" = "nix-daemon-build://" ] ; then
|
||||
refresh_critical_derivers;
|
||||
NIX_REMOTE=daemon build_here;
|
||||
elif [ "$i" = "nix-self-build://" ] ; then
|
||||
refresh_critical_derivers;
|
||||
NIX_REMOTE= build_here;
|
||||
fi;
|
||||
mv needed-paths wanted-paths;
|
||||
cat wanted-paths | xargs nix-store --check-validity --print-invalid > needed-paths;
|
||||
echo We still need $(cat needed-paths | wc -l) paths. >&2
|
||||
done;
|
||||
|
||||
cd /
|
||||
rm -r "$WORKING_DIRECTORY"
|
||||
@@ -1,88 +0,0 @@
|
||||
#! @perl@ -w
|
||||
|
||||
# This tool unpacks the closures created by "nix-pack-closure" and
|
||||
# adds them to the Nix store.
|
||||
|
||||
# TODO: make this program "streamy", i.e., don't use a temporary
|
||||
# directory.
|
||||
|
||||
use strict;
|
||||
use File::Temp qw(tempdir);
|
||||
|
||||
my $binDir = $ENV{"NIX_BIN_DIR"};
|
||||
$binDir = "@bindir@" unless defined $binDir;
|
||||
|
||||
my $tmpDir = tempdir("nix-unpack-closure.XXXXXX", CLEANUP => 1, TMPDIR => 1)
|
||||
or die "cannot create a temporary directory";
|
||||
|
||||
|
||||
# Unpack the NAR archive on standard input.
|
||||
system("nix-store --restore '$tmpDir/unpacked'") == 0
|
||||
or die "nix-store --restore failed";
|
||||
|
||||
|
||||
open VALID, ">$tmpDir/validity" or die;
|
||||
|
||||
|
||||
# For each path in the closure that is not yet valid, add it to the
|
||||
# store. TODO: use proper locking. Or even better, let nix-store do
|
||||
# this.
|
||||
opendir(DIR, "$tmpDir/unpacked/contents") or die "cannot open directory: $!";
|
||||
|
||||
foreach my $name (sort(readdir DIR)) {
|
||||
next if $name eq "." or $name eq "..";
|
||||
|
||||
my $storePath = "@storedir@/$name"; # !!!
|
||||
|
||||
# !!! this really isn't a good validity check!
|
||||
system "$binDir/nix-store --check-validity '$storePath' 2> /dev/null";
|
||||
if ($? != 0) {
|
||||
print STDERR "unpacking `$storePath'...\n";
|
||||
|
||||
# !!! race
|
||||
system("@coreutils@/rm -rf '$storePath'") == 0
|
||||
or die "cannot remove `$storePath': $?";
|
||||
|
||||
system("$binDir/nix-store --restore '$storePath' < '$tmpDir/unpacked/contents/$name'") == 0
|
||||
or die "nix-store --dump failed on `$storePath': $?";
|
||||
|
||||
print VALID "$storePath\n";
|
||||
|
||||
open DRV, "<$tmpDir/unpacked/derivers/$name" or die;
|
||||
my $deriver = <DRV>;
|
||||
chomp $deriver;
|
||||
$deriver = "" if $deriver eq "unknown-deriver";
|
||||
close DRV;
|
||||
|
||||
my @refs;
|
||||
open REFS, "<$tmpDir/unpacked/references/$name" or die;
|
||||
while (<REFS>) {
|
||||
chomp;
|
||||
push @refs, $_;
|
||||
}
|
||||
close REFS;
|
||||
|
||||
print VALID "$deriver\n";
|
||||
|
||||
print VALID (scalar @refs), "\n";
|
||||
foreach my $ref (@refs) {
|
||||
print VALID "$ref\n";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
closedir(DIR) or die;
|
||||
|
||||
|
||||
# Register the invalid paths as valid.
|
||||
system("nix-store --register-validity <'$tmpDir/validity'") == 0
|
||||
or die "nix-store --register-validity failed";
|
||||
|
||||
|
||||
# Show the top-level paths so that something useful can be done with
|
||||
# them, e.g., passing them to `nix-env -i'.
|
||||
if (-e "$tmpDir/unpacked/top-level") {
|
||||
open TOPLEVEL, "<$tmpDir/unpacked/top-level" or die;
|
||||
while (<TOPLEVEL>) { print "$_"; }
|
||||
close TOPLEVEL;
|
||||
}
|
||||
@@ -1,5 +1,3 @@
|
||||
SUBDIRS = bin2c boost libutil libstore libmain nix-store nix-hash \
|
||||
libexpr nix-instantiate nix-env nix-worker nix-setuid-helper \
|
||||
nix-log2xml bsdiff-4.3
|
||||
|
||||
EXTRA_DIST = aterm-helper.pl
|
||||
|
||||
@@ -1,178 +0,0 @@
|
||||
#! /usr/bin/perl -w
|
||||
|
||||
# This program generates C/C++ code for efficiently manipulating
|
||||
# ATerms. It generates functions to build and match ATerms according
|
||||
# to a set of constructor definitions defined in a file read from
|
||||
# standard input. A constructor is defined by a line with the
|
||||
# following format:
|
||||
#
|
||||
# SYM | ARGS | TYPE | FUN?
|
||||
#
|
||||
# where SYM is the name of the constructor, ARGS is a
|
||||
# whitespace-separated list of argument types, TYPE is the type of the
|
||||
# resulting ATerm (which should be `ATerm' or a type synonym for
|
||||
# `ATerm'), and the optional FUN is used to construct the names of the
|
||||
# build and match functions (it defaults to SYM; overriding it is
|
||||
# useful if there are overloaded constructors, e.g., with different
|
||||
# arities). Note that SYM may be empty.
|
||||
#
|
||||
# A line of the form
|
||||
#
|
||||
# VAR = EXPR
|
||||
#
|
||||
# causes a ATerm variable to be generated that is initialised to the
|
||||
# value EXPR.
|
||||
#
|
||||
# Finally, a line of the form
|
||||
#
|
||||
# init NAME
|
||||
#
|
||||
# causes the initialisation function to be called `NAME'. This
|
||||
# function must be called before any of the build/match functions or
|
||||
# the generated variables are used.
|
||||
|
||||
die if scalar @ARGV != 2;
|
||||
|
||||
my $syms = "";
|
||||
my $init = "";
|
||||
my $initFun = "init";
|
||||
|
||||
open HEADER, ">$ARGV[0]";
|
||||
open IMPL, ">$ARGV[1]";
|
||||
|
||||
print HEADER "#include <aterm2.h>\n";
|
||||
print HEADER "#ifdef __cplusplus\n";
|
||||
print HEADER "namespace nix {\n";
|
||||
print HEADER "#endif\n\n\n";
|
||||
print IMPL "namespace nix {\n";
|
||||
|
||||
while (<STDIN>) {
|
||||
s/\#.*//;
|
||||
next if (/^\s*$/);
|
||||
|
||||
if (/^\s*(\w*)\s*\|([^\|]*)\|\s*(\w+)\s*\|\s*(\w+)?/) {
|
||||
my $const = $1;
|
||||
my @types = split ' ', $2;
|
||||
my $result = $3;
|
||||
my $funname = $4;
|
||||
$funname = $const unless defined $funname;
|
||||
|
||||
my $formals = "";
|
||||
my $formals2 = "";
|
||||
my $args = "";
|
||||
my $unpack = "";
|
||||
my $n = 1;
|
||||
foreach my $type (@types) {
|
||||
my $realType = $type;
|
||||
$args .= ", ";
|
||||
if ($type eq "string") {
|
||||
# $args .= "(ATerm) ATmakeAppl0(ATmakeAFun((char *) e$n, 0, ATtrue))";
|
||||
# $type = "const char *";
|
||||
$type = "ATerm";
|
||||
$args .= "e$n";
|
||||
# !!! in the matcher, we should check that the
|
||||
# argument is a string (i.e., a nullary application).
|
||||
} elsif ($type eq "int") {
|
||||
$args .= "(ATerm) ATmakeInt(e$n)";
|
||||
} elsif ($type eq "ATermList" || $type eq "ATermBlob") {
|
||||
$args .= "(ATerm) e$n";
|
||||
} else {
|
||||
$args .= "e$n";
|
||||
}
|
||||
$formals .= ", " if $formals ne "";
|
||||
$formals .= "$type e$n";
|
||||
$formals2 .= ", ";
|
||||
$formals2 .= "$type & e$n";
|
||||
my $m = $n - 1;
|
||||
# !!! more checks here
|
||||
if ($type eq "int") {
|
||||
$unpack .= " e$n = ATgetInt((ATermInt) ATgetArgument(e, $m));\n";
|
||||
} elsif ($type eq "ATermList") {
|
||||
$unpack .= " e$n = (ATermList) ATgetArgument(e, $m);\n";
|
||||
} elsif ($type eq "ATermBlob") {
|
||||
$unpack .= " e$n = (ATermBlob) ATgetArgument(e, $m);\n";
|
||||
} elsif ($realType eq "string") {
|
||||
$unpack .= " e$n = ATgetArgument(e, $m);\n";
|
||||
$unpack .= " if (ATgetType(e$n) != AT_APPL) return false;\n";
|
||||
} else {
|
||||
$unpack .= " e$n = ATgetArgument(e, $m);\n";
|
||||
}
|
||||
$n++;
|
||||
}
|
||||
|
||||
my $arity = scalar @types;
|
||||
|
||||
print HEADER "extern AFun sym$funname;\n\n";
|
||||
|
||||
print IMPL "AFun sym$funname = 0;\n";
|
||||
|
||||
if ($arity == 0) {
|
||||
print HEADER "extern ATerm const$funname;\n\n";
|
||||
print IMPL "ATerm const$funname = 0;\n";
|
||||
}
|
||||
|
||||
print HEADER "static inline $result make$funname($formals) __attribute__ ((pure, nothrow));\n";
|
||||
print HEADER "static inline $result make$funname($formals) {\n";
|
||||
if ($arity == 0) {
|
||||
print HEADER " return const$funname;\n";
|
||||
}
|
||||
elsif ($arity <= 6) {
|
||||
print HEADER " return (ATerm) ATmakeAppl$arity(sym$funname$args);\n";
|
||||
} else {
|
||||
$args =~ s/^,//;
|
||||
print HEADER " ATerm array[$arity] = {$args};\n";
|
||||
print HEADER " return (ATerm) ATmakeApplArray(sym$funname, array);\n";
|
||||
}
|
||||
print HEADER "}\n\n";
|
||||
|
||||
print HEADER "#ifdef __cplusplus\n";
|
||||
print HEADER "static inline bool match$funname(ATerm e$formals2) {\n";
|
||||
print HEADER " if (ATgetType(e) != AT_APPL || (AFun) ATgetAFun(e) != sym$funname) return false;\n";
|
||||
print HEADER "$unpack";
|
||||
print HEADER " return true;\n";
|
||||
print HEADER "}\n";
|
||||
print HEADER "#endif\n\n\n";
|
||||
|
||||
$init .= " sym$funname = ATmakeAFun(\"$const\", $arity, ATfalse);\n";
|
||||
$init .= " ATprotectAFun(sym$funname);\n";
|
||||
if ($arity == 0) {
|
||||
$init .= " const$funname = (ATerm) ATmakeAppl0(sym$funname);\n";
|
||||
$init .= " ATprotect(&const$funname);\n";
|
||||
}
|
||||
}
|
||||
|
||||
elsif (/^\s*(\w+)\s*=\s*(.*)$/) {
|
||||
my $name = $1;
|
||||
my $value = $2;
|
||||
print HEADER "extern ATerm $name;\n";
|
||||
print IMPL "ATerm $name = 0;\n";
|
||||
$init .= " $name = $value;\n";
|
||||
}
|
||||
|
||||
elsif (/^\s*init\s+(\w+)\s*$/) {
|
||||
$initFun = $1;
|
||||
}
|
||||
|
||||
else {
|
||||
die "bad line: `$_'";
|
||||
}
|
||||
}
|
||||
|
||||
print HEADER "void $initFun();\n\n";
|
||||
|
||||
print HEADER "static inline const char * aterm2String(ATerm t) {\n";
|
||||
print HEADER " return (const char *) ATgetName(ATgetAFun(t));\n";
|
||||
print HEADER "}\n\n";
|
||||
|
||||
print IMPL "\n";
|
||||
print IMPL "void $initFun() {\n";
|
||||
print IMPL "$init";
|
||||
print IMPL "}\n";
|
||||
|
||||
print HEADER "#ifdef __cplusplus\n";
|
||||
print HEADER "}\n";
|
||||
print HEADER "#endif\n\n\n";
|
||||
print IMPL "}\n";
|
||||
|
||||
close HEADER;
|
||||
close IMPL;
|
||||
@@ -1,3 +1,6 @@
|
||||
noinst_PROGRAMS = bin2c
|
||||
|
||||
bin2c_SOURCES = bin2c.c
|
||||
|
||||
bin2c$(EXEEXT): bin2c.c
|
||||
$(CC_FOR_BUILD) $(CFLAGS_FOR_BUILD) -o bin2c bin2c.c
|
||||
|
||||
@@ -14,10 +14,10 @@ int main(int argc, char * * argv)
|
||||
{
|
||||
int c;
|
||||
if (argc != 2) abort();
|
||||
print("static unsigned char %s[] = {", argv[1]);
|
||||
print("static unsigned char %s[] = { ", argv[1]);
|
||||
while ((c = getchar()) != EOF) {
|
||||
print("0x%02x, ", (unsigned char) c);
|
||||
}
|
||||
print("};\n");
|
||||
print("0 };\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
SUBDIRS = format
|
||||
|
||||
pkginclude_HEADERS = assert.hpp checked_delete.hpp format.hpp \
|
||||
nobase_pkginclude_HEADERS = assert.hpp checked_delete.hpp format.hpp \
|
||||
shared_ptr.hpp weak_ptr.hpp throw_exception.hpp \
|
||||
enable_shared_from_this.hpp \
|
||||
detail/shared_count.hpp detail/workaround.hpp
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
EXTRA_DIST = compat-include
|
||||
|
||||
libexec_PROGRAMS = bsdiff bspatch
|
||||
|
||||
bsdiff_SOURCES = bsdiff.c
|
||||
@@ -8,4 +10,4 @@ bspatch_SOURCES = bspatch.c
|
||||
|
||||
bspatch_LDADD = ${bzip2_lib}
|
||||
|
||||
AM_CFLAGS = -O3 ${bzip2_include}
|
||||
AM_CFLAGS = -O3 ${bzip2_include} ${bsddiff_compat_include}
|
||||
|
||||
@@ -277,6 +277,7 @@ int main(int argc,char *argv[])
|
||||
for(scsc=scan+=len;scan<newsize;scan++) {
|
||||
len=search(I,old,oldsize,new+scan,newsize-scan,
|
||||
0,oldsize,&pos);
|
||||
if (len > 64 * 1024) break;
|
||||
|
||||
for(;scsc<scan+len;scsc++)
|
||||
if((scsc+lastoffset<oldsize) &&
|
||||
|
||||
12
src/bsdiff-4.3/compat-include/err.h
Normal file
12
src/bsdiff-4.3/compat-include/err.h
Normal file
@@ -0,0 +1,12 @@
|
||||
/* Simulate BSD's <err.h> functionality. */
|
||||
|
||||
#ifndef COMPAT_ERR_H_INCLUDED
|
||||
#define COMPAT_ERR_H_INCLUDED 1
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#define err(rc,...) do { fprintf(stderr,__VA_ARGS__); exit(rc); } while(0)
|
||||
#define errx(rc,...) do { fprintf(stderr,__VA_ARGS__); exit(rc); } while(0)
|
||||
|
||||
#endif
|
||||
@@ -2,25 +2,25 @@ pkglib_LTLIBRARIES = libexpr.la
|
||||
|
||||
libexpr_la_SOURCES = \
|
||||
nixexpr.cc eval.cc primops.cc lexer-tab.cc parser-tab.cc \
|
||||
get-drvs.cc attr-path.cc expr-to-xml.cc common-opts.cc
|
||||
get-drvs.cc attr-path.cc value-to-xml.cc common-opts.cc \
|
||||
names.cc
|
||||
|
||||
pkginclude_HEADERS = \
|
||||
nixexpr.hh eval.hh parser.hh lexer-tab.hh parser-tab.hh \
|
||||
get-drvs.hh attr-path.hh expr-to-xml.hh common-opts.hh
|
||||
get-drvs.hh attr-path.hh value-to-xml.hh common-opts.hh \
|
||||
names.hh symbol-table.hh
|
||||
|
||||
libexpr_la_LIBADD = ../libutil/libutil.la ../libstore/libstore.la \
|
||||
../boost/format/libformat.la
|
||||
../boost/format/libformat.la @boehmgc_lib@
|
||||
|
||||
BUILT_SOURCES = nixexpr-ast.cc nixexpr-ast.hh \
|
||||
BUILT_SOURCES = \
|
||||
parser-tab.hh lexer-tab.hh parser-tab.cc lexer-tab.cc
|
||||
|
||||
EXTRA_DIST = lexer.l parser.y nixexpr-ast.def nixexpr-ast.cc
|
||||
EXTRA_DIST = lexer.l parser.y
|
||||
|
||||
AM_CXXFLAGS = \
|
||||
-I$(srcdir)/.. ${bdb_include} ${aterm_include} \
|
||||
-I$(srcdir)/.. \
|
||||
-I$(srcdir)/../libutil -I$(srcdir)/../libstore
|
||||
AM_CFLAGS = \
|
||||
${aterm_include}
|
||||
|
||||
|
||||
# Parser generation.
|
||||
@@ -32,15 +32,6 @@ lexer-tab.cc lexer-tab.hh: lexer.l
|
||||
$(flex) --outfile lexer-tab.cc --header-file=lexer-tab.hh $(srcdir)/lexer.l
|
||||
|
||||
|
||||
# ATerm helper function generation.
|
||||
|
||||
nixexpr-ast.cc nixexpr-ast.hh: ../aterm-helper.pl nixexpr-ast.def
|
||||
$(perl) $(srcdir)/../aterm-helper.pl nixexpr-ast.hh nixexpr-ast.cc < $(srcdir)/nixexpr-ast.def
|
||||
|
||||
|
||||
CLEANFILES =
|
||||
|
||||
|
||||
# SDF stuff (not built by default).
|
||||
nix.tbl: nix.sdf
|
||||
sdf2table -m Nix -s -i nix.sdf -o nix.tbl
|
||||
|
||||
@@ -1,23 +1,13 @@
|
||||
#include "attr-path.hh"
|
||||
#include "nixexpr-ast.hh"
|
||||
#include "util.hh"
|
||||
|
||||
|
||||
namespace nix {
|
||||
|
||||
|
||||
bool isAttrs(EvalState & state, Expr e, ATermMap & attrs)
|
||||
{
|
||||
e = evalExpr(state, e);
|
||||
ATermList dummy;
|
||||
if (!matchAttrs(e, dummy)) return false;
|
||||
queryAllAttrs(e, attrs, false);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
Expr findAlongAttrPath(EvalState & state, const string & attrPath,
|
||||
const ATermMap & autoArgs, Expr e)
|
||||
// !!! Shouldn't we return a pointer to a Value?
|
||||
void findAlongAttrPath(EvalState & state, const string & attrPath,
|
||||
Bindings & autoArgs, Expr * e, Value & v)
|
||||
{
|
||||
Strings tokens = tokenizeString(attrPath, ".");
|
||||
|
||||
@@ -25,8 +15,10 @@ Expr findAlongAttrPath(EvalState & state, const string & attrPath,
|
||||
Error(format("attribute selection path `%1%' does not match expression") % attrPath);
|
||||
|
||||
string curPath;
|
||||
|
||||
state.mkThunk_(v, e);
|
||||
|
||||
for (Strings::iterator i = tokens.begin(); i != tokens.end(); ++i) {
|
||||
foreach (Strings::iterator, i, tokens) {
|
||||
|
||||
if (!curPath.empty()) curPath += ".";
|
||||
curPath += *i;
|
||||
@@ -38,7 +30,10 @@ Expr findAlongAttrPath(EvalState & state, const string & attrPath,
|
||||
if (string2Int(attr, attrIndex)) apType = apIndex;
|
||||
|
||||
/* Evaluate the expression. */
|
||||
e = evalExpr(state, autoCallFunction(evalExpr(state, e), autoArgs));
|
||||
Value vTmp;
|
||||
state.autoCallFunction(autoArgs, v, vTmp);
|
||||
v = vTmp;
|
||||
state.forceValue(v);
|
||||
|
||||
/* It should evaluate to either an attribute set or an
|
||||
expression, according to what is specified in the
|
||||
@@ -46,36 +41,31 @@ Expr findAlongAttrPath(EvalState & state, const string & attrPath,
|
||||
|
||||
if (apType == apAttr) {
|
||||
|
||||
ATermMap attrs;
|
||||
|
||||
if (!isAttrs(state, e, attrs))
|
||||
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(e));
|
||||
|
||||
e = attrs.get(toATerm(attr));
|
||||
if (!e)
|
||||
throw Error(format("attribute `%1%' in selection path `%2%' not found") % attr % curPath);
|
||||
% curPath % showType(v));
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
else if (apType == apIndex) {
|
||||
|
||||
ATermList es;
|
||||
if (!matchList(e, es))
|
||||
if (v.type != tList)
|
||||
throw TypeError(
|
||||
format("the expression selected by the selection path `%1%' should be a list but is %2%")
|
||||
% curPath % showType(e));
|
||||
% curPath % showType(v));
|
||||
|
||||
e = ATelementAt(es, attrIndex);
|
||||
if (!e)
|
||||
throw Error(format("list index %1% in selection path `%2%' not found") % attrIndex % curPath);
|
||||
|
||||
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];
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return e;
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -1,17 +1,17 @@
|
||||
#ifndef __ATTR_PATH_H
|
||||
#define __ATTR_PATH_H
|
||||
|
||||
#include "eval.hh"
|
||||
|
||||
#include <string>
|
||||
#include <map>
|
||||
|
||||
#include "eval.hh"
|
||||
|
||||
|
||||
namespace nix {
|
||||
|
||||
|
||||
Expr findAlongAttrPath(EvalState & state, const string & attrPath,
|
||||
const ATermMap & autoArgs, Expr e);
|
||||
void findAlongAttrPath(EvalState & state, const string & attrPath,
|
||||
Bindings & autoArgs, Expr * e, Value & v);
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -9,7 +9,7 @@ namespace nix {
|
||||
|
||||
bool parseOptionArg(const string & arg, Strings::iterator & i,
|
||||
const Strings::iterator & argsEnd, EvalState & state,
|
||||
ATermMap & autoArgs)
|
||||
Bindings & autoArgs)
|
||||
{
|
||||
if (arg != "--arg" && arg != "--argstr") return false;
|
||||
|
||||
@@ -19,12 +19,18 @@ bool parseOptionArg(const string & arg, Strings::iterator & i,
|
||||
string name = *i++;
|
||||
if (i == argsEnd) throw error;
|
||||
string value = *i++;
|
||||
|
||||
Expr e = arg == "--arg"
|
||||
? parseExprFromString(state, value, absPath("."))
|
||||
: makeStr(value);
|
||||
autoArgs.set(toATerm(name), e);
|
||||
|
||||
|
||||
/* !!! check for duplicates! */
|
||||
Value * v = state.allocValue();
|
||||
autoArgs.push_back(Attr(state.symbols.create(name), v));
|
||||
|
||||
if (arg == "--arg")
|
||||
state.mkThunk_(*v, parseExprFromString(state, value, absPath(".")));
|
||||
else
|
||||
mkString(*v, value);
|
||||
|
||||
autoArgs.sort(); // !!! inefficient
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -9,7 +9,7 @@ namespace nix {
|
||||
/* Some common option parsing between nix-env and nix-instantiate. */
|
||||
bool parseOptionArg(const string & arg, Strings::iterator & i,
|
||||
const Strings::iterator & argsEnd, EvalState & state,
|
||||
ATermMap & autoArgs);
|
||||
Bindings & autoArgs);
|
||||
|
||||
}
|
||||
|
||||
|
||||
1723
src/libexpr/eval.cc
1723
src/libexpr/eval.cc
File diff suppressed because it is too large
Load Diff
@@ -1,19 +1,193 @@
|
||||
#ifndef __EVAL_H
|
||||
#define __EVAL_H
|
||||
|
||||
#include "nixexpr.hh"
|
||||
#include "symbol-table.hh"
|
||||
#include "hash.hh"
|
||||
|
||||
#include <map>
|
||||
|
||||
#include "aterm.hh"
|
||||
#include "nixexpr.hh"
|
||||
#if HAVE_BOEHMGC
|
||||
#include <gc/gc_allocator.h>
|
||||
#endif
|
||||
|
||||
|
||||
namespace nix {
|
||||
|
||||
|
||||
class Hash;
|
||||
|
||||
class EvalState;
|
||||
struct Env;
|
||||
struct Value;
|
||||
struct Attr;
|
||||
|
||||
|
||||
/* Attribute sets are represented as a vector of attributes, sorted by
|
||||
symbol (i.e. pointer to the attribute name in the symbol table). */
|
||||
#if HAVE_BOEHMGC
|
||||
typedef std::vector<Attr, gc_allocator<Attr> > BindingsBase;
|
||||
#else
|
||||
typedef std::vector<Attr> BindingsBase;
|
||||
#endif
|
||||
|
||||
|
||||
class Bindings : public BindingsBase
|
||||
{
|
||||
public:
|
||||
iterator find(const Symbol & name);
|
||||
void sort();
|
||||
};
|
||||
|
||||
|
||||
typedef enum {
|
||||
tInt = 1,
|
||||
tBool,
|
||||
tString,
|
||||
tPath,
|
||||
tNull,
|
||||
tAttrs,
|
||||
tList,
|
||||
tThunk,
|
||||
tApp,
|
||||
tLambda,
|
||||
tBlackhole,
|
||||
tPrimOp,
|
||||
tPrimOpApp,
|
||||
} ValueType;
|
||||
|
||||
|
||||
typedef void (* PrimOpFun) (EvalState & state, Value * * args, Value & v);
|
||||
|
||||
|
||||
struct PrimOp
|
||||
{
|
||||
PrimOpFun fun;
|
||||
unsigned int arity;
|
||||
Symbol name;
|
||||
PrimOp(PrimOpFun fun, unsigned int arity, Symbol name)
|
||||
: fun(fun), arity(arity), name(name) { }
|
||||
};
|
||||
|
||||
|
||||
struct Value
|
||||
{
|
||||
ValueType type;
|
||||
union
|
||||
{
|
||||
int 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
|
||||
|
||||
"--with-freetype2-library=" + freetype + "/lib"
|
||||
|
||||
where `freetype' is a derivation (or a source to be copied
|
||||
to the store). If we just concatenated the strings without
|
||||
keeping track of the referenced store paths, then if the
|
||||
string is used as a derivation attribute, the derivation
|
||||
will not have the correct dependencies in its inputDrvs and
|
||||
inputSrcs.
|
||||
|
||||
The semantics of the context is as follows: when a string
|
||||
with context C is used as a derivation attribute, then the
|
||||
derivations in C will be added to the inputDrvs of the
|
||||
derivation, and the other store paths in C will be added to
|
||||
the inputSrcs of the derivations.
|
||||
|
||||
For canonicity, the store paths should be in sorted order. */
|
||||
struct {
|
||||
const char * s;
|
||||
const char * * context; // must be in sorted order
|
||||
} string;
|
||||
|
||||
const char * path;
|
||||
Bindings * attrs;
|
||||
struct {
|
||||
unsigned int length;
|
||||
Value * * elems;
|
||||
} list;
|
||||
struct {
|
||||
Env * env;
|
||||
Expr * expr;
|
||||
} thunk;
|
||||
struct {
|
||||
Value * left, * right;
|
||||
} app;
|
||||
struct {
|
||||
Env * env;
|
||||
ExprLambda * fun;
|
||||
} lambda;
|
||||
PrimOp * primOp;
|
||||
struct {
|
||||
Value * left, * right;
|
||||
} primOpApp;
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
struct Env
|
||||
{
|
||||
Env * up;
|
||||
unsigned int prevWith; // nr of levels up to next `with' environment
|
||||
Value * values[0];
|
||||
};
|
||||
|
||||
|
||||
struct Attr
|
||||
{
|
||||
Symbol name;
|
||||
Value * value;
|
||||
Pos * pos;
|
||||
Attr(Symbol name, Value * value, Pos * pos = &noPos)
|
||||
: name(name), value(value), pos(pos) { };
|
||||
Attr() : pos(&noPos) { };
|
||||
bool operator < (const Attr & a) const
|
||||
{
|
||||
return name < a.name;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/* After overwriting an app node, be sure to clear pointers in the
|
||||
Value to ensure that the target isn't kept alive unnecessarily. */
|
||||
static inline void clearValue(Value & v)
|
||||
{
|
||||
v.app.right = 0;
|
||||
}
|
||||
|
||||
|
||||
static inline void mkInt(Value & v, int n)
|
||||
{
|
||||
clearValue(v);
|
||||
v.type = tInt;
|
||||
v.integer = n;
|
||||
}
|
||||
|
||||
|
||||
static inline void mkBool(Value & v, bool b)
|
||||
{
|
||||
clearValue(v);
|
||||
v.type = tBool;
|
||||
v.boolean = b;
|
||||
}
|
||||
|
||||
|
||||
static inline void mkApp(Value & v, Value & left, Value & right)
|
||||
{
|
||||
v.type = tApp;
|
||||
v.app.left = &left;
|
||||
v.app.right = &right;
|
||||
}
|
||||
|
||||
|
||||
void mkString(Value & v, const char * s);
|
||||
void mkString(Value & v, const string & s, const PathSet & context = PathSet());
|
||||
void mkPath(Value & v, const char * s);
|
||||
|
||||
void copyContext(const Value & v, PathSet & context);
|
||||
|
||||
|
||||
typedef std::map<Path, PathSet> DrvRoots;
|
||||
typedef std::map<Path, Hash> DrvHashes;
|
||||
|
||||
/* Cache for calls to addToStore(); maps source paths to the store
|
||||
@@ -22,73 +196,160 @@ typedef std::map<Path, Path> SrcToStore;
|
||||
|
||||
struct EvalState;
|
||||
|
||||
/* Note: using a ATermVector is safe here, since when we call a primop
|
||||
we also have an ATermList on the stack. */
|
||||
typedef Expr (* PrimOp) (EvalState &, const ATermVector & args);
|
||||
|
||||
std::ostream & operator << (std::ostream & str, const Value & v);
|
||||
|
||||
|
||||
struct EvalState
|
||||
class EvalState
|
||||
{
|
||||
ATermMap normalForms;
|
||||
ATermMap primOps;
|
||||
DrvRoots drvRoots;
|
||||
public:
|
||||
DrvHashes drvHashes; /* normalised derivation hashes */
|
||||
|
||||
SymbolTable symbols;
|
||||
|
||||
const Symbol sWith, sOutPath, sDrvPath, sType, sMeta, sName,
|
||||
sSystem, sOverrides;
|
||||
|
||||
private:
|
||||
SrcToStore srcToStore;
|
||||
|
||||
unsigned int nrEvaluated;
|
||||
unsigned int nrCached;
|
||||
bool allowUnsafeEquality;
|
||||
|
||||
std::map<Path, Expr *> parseTrees;
|
||||
|
||||
public:
|
||||
|
||||
EvalState();
|
||||
~EvalState();
|
||||
|
||||
/* Evaluate an expression read from the given file to normal
|
||||
form. */
|
||||
void evalFile(const Path & path, Value & v);
|
||||
|
||||
/* Evaluate an expression to normal form, storing the result in
|
||||
value `v'. */
|
||||
void eval(Expr * e, Value & v);
|
||||
void eval(Env & env, Expr * e, Value & v);
|
||||
|
||||
/* Evaluation the expression, then verify that it has the expected
|
||||
type. */
|
||||
bool evalBool(Env & env, Expr * e);
|
||||
void evalAttrs(Env & env, Expr * e, Value & v);
|
||||
|
||||
/* If `v' is a thunk, enter it and overwrite `v' with the result
|
||||
of the evaluation of the thunk. If `v' is a delayed function
|
||||
application, call the function and overwrite `v' with the
|
||||
result. Otherwise, this is a no-op. */
|
||||
void forceValue(Value & v);
|
||||
|
||||
/* Force a value, then recursively force list elements and
|
||||
attributes. */
|
||||
void strictForceValue(Value & v);
|
||||
|
||||
/* Force `v', and then verify that it has the expected type. */
|
||||
int forceInt(Value & v);
|
||||
bool forceBool(Value & v);
|
||||
void forceAttrs(Value & v);
|
||||
void forceList(Value & v);
|
||||
void forceFunction(Value & v); // either lambda or primop
|
||||
string forceString(Value & v);
|
||||
string forceString(Value & v, PathSet & context);
|
||||
string forceStringNoCtx(Value & v);
|
||||
|
||||
/* Return true iff the value `v' denotes a derivation (i.e. a
|
||||
set with attribute `type = "derivation"'). */
|
||||
bool isDerivation(Value & v);
|
||||
|
||||
/* 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 */
|
||||
string coerceToString(Value & v, PathSet & context,
|
||||
bool coerceMore = false, bool copyToStore = true);
|
||||
|
||||
/* Path coercion. Converts strings, paths and derivations to a
|
||||
path. The result is guaranteed to be a canonicalised, absolute
|
||||
path. Nothing is copied to the store. */
|
||||
Path coerceToPath(Value & v, PathSet & context);
|
||||
|
||||
private:
|
||||
|
||||
/* 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:
|
||||
|
||||
void createBaseEnv();
|
||||
|
||||
void addConstant(const string & name, Value & v);
|
||||
|
||||
void addPrimOps();
|
||||
void addPrimOp(const string & name,
|
||||
unsigned int arity, PrimOp primOp);
|
||||
unsigned int arity, PrimOpFun primOp);
|
||||
|
||||
Value * lookupVar(Env * env, const VarRef & var);
|
||||
|
||||
friend class ExprVar;
|
||||
friend class ExprAttrs;
|
||||
friend class ExprLet;
|
||||
|
||||
public:
|
||||
|
||||
/* Do a deep equality test between two values. That is, list
|
||||
elements and attributes are compared recursively. */
|
||||
bool eqValues(Value & v1, Value & v2);
|
||||
|
||||
void callFunction(Value & fun, Value & arg, Value & v);
|
||||
|
||||
/* 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);
|
||||
|
||||
Value * allocAttr(Value & vAttrs, const Symbol & name);
|
||||
|
||||
void mkList(Value & v, unsigned int length);
|
||||
void mkAttrs(Value & v, unsigned int expected);
|
||||
void mkThunk_(Value & v, Expr * expr);
|
||||
|
||||
Value * maybeThunk(Env & env, Expr * expr);
|
||||
|
||||
/* Print statistics. */
|
||||
void printStats();
|
||||
|
||||
private:
|
||||
|
||||
unsigned long nrEnvs;
|
||||
unsigned long nrValuesInEnvs;
|
||||
unsigned long nrValues;
|
||||
unsigned long nrListElems;
|
||||
unsigned long nrEvaluated;
|
||||
unsigned long nrAttrsets;
|
||||
unsigned long nrOpUpdates;
|
||||
unsigned long nrOpUpdateValuesCopied;
|
||||
unsigned int recursionDepth;
|
||||
unsigned int maxRecursionDepth;
|
||||
char * deepestStack; /* for measuring stack usage */
|
||||
|
||||
friend class RecursionCounter;
|
||||
friend class ExprOpUpdate;
|
||||
};
|
||||
|
||||
|
||||
/* Evaluate an expression to normal form. */
|
||||
Expr evalExpr(EvalState & state, Expr e);
|
||||
/* Return a string representing the type of the value `v'. */
|
||||
string showType(const Value & v);
|
||||
|
||||
/* Evaluate an expression read from the given file to normal form. */
|
||||
Expr evalFile(EvalState & state, const Path & path);
|
||||
|
||||
/* Evaluate an expression, and recursively evaluate list elements and
|
||||
attributes. If `canonicalise' is true, we remove things like
|
||||
position information and make sure that attribute sets are in
|
||||
sorded order. */
|
||||
Expr strictEvalExpr(EvalState & state, Expr e);
|
||||
|
||||
/* Specific results. */
|
||||
string evalString(EvalState & state, Expr e, PathSet & context);
|
||||
string evalStringNoCtx(EvalState & state, Expr e);
|
||||
int evalInt(EvalState & state, Expr e);
|
||||
bool evalBool(EvalState & state, Expr e);
|
||||
ATermList evalList(EvalState & state, Expr e);
|
||||
|
||||
/* Flatten nested lists into a single list (or expand a singleton into
|
||||
a list). */
|
||||
ATermList flattenList(EvalState & state, Expr e);
|
||||
|
||||
/* String coercion. Converts strings, paths and derivations to a
|
||||
string. If `coerceMore' is set, also converts nulls, integers,
|
||||
booleans and lists to a string. */
|
||||
string coerceToString(EvalState & state, Expr e, PathSet & context,
|
||||
bool coerceMore = false, bool copyToStore = true);
|
||||
|
||||
/* Path coercion. Converts strings, paths and derivations to a path.
|
||||
The result is guaranteed to be an canonicalised, absolute path.
|
||||
Nothing is copied to the store. */
|
||||
Path coerceToPath(EvalState & state, Expr e, PathSet & context);
|
||||
|
||||
/* Automatically call a function for which each argument has a default
|
||||
value or has a binding in the `args' map. Note: result is a call,
|
||||
not a normal form; it should be evaluated by calling evalExpr(). */
|
||||
Expr autoCallFunction(Expr e, const ATermMap & args);
|
||||
|
||||
/* Print statistics. */
|
||||
void printEvalStats(EvalState & state);
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -1,142 +0,0 @@
|
||||
#include "expr-to-xml.hh"
|
||||
#include "xml-writer.hh"
|
||||
#include "nixexpr-ast.hh"
|
||||
#include "aterm.hh"
|
||||
#include "util.hh"
|
||||
|
||||
|
||||
namespace nix {
|
||||
|
||||
|
||||
static XMLAttrs singletonAttrs(const string & name, const string & value)
|
||||
{
|
||||
XMLAttrs attrs;
|
||||
attrs[name] = value;
|
||||
return attrs;
|
||||
}
|
||||
|
||||
|
||||
/* set<Expr> is safe because all the expressions are also reachable
|
||||
from the stack, therefore can't be garbage-collected. */
|
||||
typedef set<Expr> ExprSet;
|
||||
|
||||
|
||||
static void printTermAsXML(Expr e, XMLWriter & doc, PathSet & context,
|
||||
ExprSet & drvsSeen);
|
||||
|
||||
|
||||
static void showAttrs(const ATermMap & attrs, XMLWriter & doc,
|
||||
PathSet & context, ExprSet & drvsSeen)
|
||||
{
|
||||
StringSet names;
|
||||
for (ATermMap::const_iterator i = attrs.begin(); i != attrs.end(); ++i)
|
||||
names.insert(aterm2String(i->key));
|
||||
for (StringSet::iterator i = names.begin(); i != names.end(); ++i) {
|
||||
XMLOpenElement _(doc, "attr", singletonAttrs("name", *i));
|
||||
printTermAsXML(attrs.get(toATerm(*i)), doc, context, drvsSeen);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void printTermAsXML(Expr e, XMLWriter & doc, PathSet & context,
|
||||
ExprSet & drvsSeen)
|
||||
{
|
||||
XMLAttrs attrs;
|
||||
string s;
|
||||
ATerm s2;
|
||||
int i;
|
||||
ATermList as, es, formals;
|
||||
ATerm body, pos;
|
||||
|
||||
checkInterrupt();
|
||||
|
||||
if (matchStr(e, s, context)) /* !!! show the context? */
|
||||
doc.writeEmptyElement("string", singletonAttrs("value", s));
|
||||
|
||||
else if (matchPath(e, s2))
|
||||
doc.writeEmptyElement("path", singletonAttrs("value", aterm2String(s2)));
|
||||
|
||||
else if (matchNull(e))
|
||||
doc.writeEmptyElement("null");
|
||||
|
||||
else if (matchInt(e, i))
|
||||
doc.writeEmptyElement("int", singletonAttrs("value", (format("%1%") % i).str()));
|
||||
|
||||
else if (e == eTrue)
|
||||
doc.writeEmptyElement("bool", singletonAttrs("value", "true"));
|
||||
|
||||
else if (e == eFalse)
|
||||
doc.writeEmptyElement("bool", singletonAttrs("value", "false"));
|
||||
|
||||
else if (matchAttrs(e, as)) {
|
||||
ATermMap attrs;
|
||||
queryAllAttrs(e, attrs);
|
||||
|
||||
Expr a = attrs.get(toATerm("type"));
|
||||
if (a && matchStr(a, s, context) && s == "derivation") {
|
||||
|
||||
XMLAttrs xmlAttrs;
|
||||
Path outPath, drvPath;
|
||||
|
||||
a = attrs.get(toATerm("drvPath"));
|
||||
if (matchStr(a, drvPath, context))
|
||||
xmlAttrs["drvPath"] = drvPath;
|
||||
|
||||
a = attrs.get(toATerm("outPath"));
|
||||
if (matchStr(a, outPath, context))
|
||||
xmlAttrs["outPath"] = outPath;
|
||||
|
||||
XMLOpenElement _(doc, "derivation", xmlAttrs);
|
||||
|
||||
if (drvsSeen.find(e) == drvsSeen.end()) {
|
||||
drvsSeen.insert(e);
|
||||
showAttrs(attrs, doc, context, drvsSeen);
|
||||
} else
|
||||
doc.writeEmptyElement("repeated");
|
||||
}
|
||||
|
||||
else {
|
||||
XMLOpenElement _(doc, "attrs");
|
||||
showAttrs(attrs, doc, context, drvsSeen);
|
||||
}
|
||||
}
|
||||
|
||||
else if (matchList(e, es)) {
|
||||
XMLOpenElement _(doc, "list");
|
||||
for (ATermIterator i(es); i; ++i)
|
||||
printTermAsXML(*i, doc, context, drvsSeen);
|
||||
}
|
||||
|
||||
else if (matchFunction(e, formals, body, pos)) {
|
||||
XMLOpenElement _(doc, "function");
|
||||
|
||||
for (ATermIterator i(formals); i; ++i) {
|
||||
Expr name; ValidValues valids; ATerm dummy;
|
||||
if (!matchFormal(*i, name, valids, dummy)) abort();
|
||||
XMLOpenElement _(doc, "arg", singletonAttrs("name", aterm2String(name)));
|
||||
|
||||
ATermList valids2;
|
||||
if (matchValidValues(valids, valids2)) {
|
||||
for (ATermIterator j(valids2); j; ++j) {
|
||||
XMLOpenElement _(doc, "value");
|
||||
printTermAsXML(*j, doc, context, drvsSeen);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
else
|
||||
doc.writeEmptyElement("unevaluated");
|
||||
}
|
||||
|
||||
|
||||
void printTermAsXML(Expr e, std::ostream & out, PathSet & context)
|
||||
{
|
||||
XMLWriter doc(true, out);
|
||||
XMLOpenElement root(doc, "expr");
|
||||
ExprSet drvsSeen;
|
||||
printTermAsXML(e, doc, context, drvsSeen);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -1,16 +0,0 @@
|
||||
#ifndef __EXPR_TO_XML_H
|
||||
#define __EXPR_TO_XML_H
|
||||
|
||||
#include <string>
|
||||
#include <map>
|
||||
|
||||
#include "nixexpr.hh"
|
||||
#include "aterm.hh"
|
||||
|
||||
namespace nix {
|
||||
|
||||
void printTermAsXML(Expr e, std::ostream & out, PathSet & context);
|
||||
|
||||
}
|
||||
|
||||
#endif /* !__EXPR_TO_XML_H */
|
||||
@@ -1,5 +1,4 @@
|
||||
#include "get-drvs.hh"
|
||||
#include "nixexpr-ast.hh"
|
||||
#include "util.hh"
|
||||
|
||||
|
||||
@@ -8,17 +7,10 @@ namespace nix {
|
||||
|
||||
string DrvInfo::queryDrvPath(EvalState & state) const
|
||||
{
|
||||
if (drvPath == "") {
|
||||
Expr a = attrs->get(toATerm("drvPath"));
|
||||
|
||||
/* Backwards compatibility hack with user environments made by
|
||||
Nix <= 0.10: these contain illegal Path("") expressions. */
|
||||
ATerm t;
|
||||
if (a && matchPath(evalExpr(state, a), t))
|
||||
return aterm2String(t);
|
||||
|
||||
if (drvPath == "" && attrs) {
|
||||
Bindings::iterator i = attrs->find(state.sDrvPath);
|
||||
PathSet context;
|
||||
(string &) drvPath = a ? coerceToPath(state, a, context) : "";
|
||||
(string &) drvPath = i != attrs->end() ? state.coerceToPath(*i->value, context) : "";
|
||||
}
|
||||
return drvPath;
|
||||
}
|
||||
@@ -26,11 +18,10 @@ string DrvInfo::queryDrvPath(EvalState & state) const
|
||||
|
||||
string DrvInfo::queryOutPath(EvalState & state) const
|
||||
{
|
||||
if (outPath == "") {
|
||||
Expr a = attrs->get(toATerm("outPath"));
|
||||
if (!a) throw TypeError("output path missing");
|
||||
if (outPath == "" && attrs) {
|
||||
Bindings::iterator i = attrs->find(state.sOutPath);
|
||||
PathSet context;
|
||||
(string &) outPath = coerceToPath(state, a, context);
|
||||
(string &) outPath = i != attrs->end() ? state.coerceToPath(*i->value, context) : "";
|
||||
}
|
||||
return outPath;
|
||||
}
|
||||
@@ -38,93 +29,85 @@ string DrvInfo::queryOutPath(EvalState & state) const
|
||||
|
||||
MetaInfo DrvInfo::queryMetaInfo(EvalState & state) const
|
||||
{
|
||||
MetaInfo meta;
|
||||
if (metaInfoRead) return meta;
|
||||
|
||||
Expr a = attrs->get(toATerm("meta"));
|
||||
if (!a) return meta; /* fine, empty meta information */
|
||||
(bool &) metaInfoRead = true;
|
||||
|
||||
Bindings::iterator a = attrs->find(state.sMeta);
|
||||
if (a == attrs->end()) return meta; /* fine, empty meta information */
|
||||
|
||||
ATermMap attrs2;
|
||||
queryAllAttrs(evalExpr(state, a), attrs2);
|
||||
state.forceAttrs(*a->value);
|
||||
|
||||
for (ATermMap::const_iterator i = attrs2.begin(); i != attrs2.end(); ++i) {
|
||||
Expr e = evalExpr(state, i->value);
|
||||
string s;
|
||||
PathSet context;
|
||||
if (matchStr(e, s, context))
|
||||
meta[aterm2String(i->key)] = s;
|
||||
/* For future compatibility, ignore attribute values that are
|
||||
not strings. */
|
||||
foreach (Bindings::iterator, i, *a->value->attrs) {
|
||||
MetaValue value;
|
||||
state.forceValue(*i->value);
|
||||
if (i->value->type == tString) {
|
||||
value.type = MetaValue::tpString;
|
||||
value.stringValue = i->value->string.s;
|
||||
} else if (i->value->type == tInt) {
|
||||
value.type = MetaValue::tpInt;
|
||||
value.intValue = i->value->integer;
|
||||
} else if (i->value->type == tList) {
|
||||
value.type = MetaValue::tpStrings;
|
||||
for (unsigned int j = 0; j < i->value->list.length; ++j)
|
||||
value.stringValues.push_back(state.forceStringNoCtx(*i->value->list.elems[j]));
|
||||
} else continue;
|
||||
((MetaInfo &) meta)[i->name] = value;
|
||||
}
|
||||
|
||||
return meta;
|
||||
}
|
||||
|
||||
|
||||
string DrvInfo::queryMetaInfo(EvalState & state, const string & name) const
|
||||
MetaValue DrvInfo::queryMetaInfo(EvalState & state, const string & name) const
|
||||
{
|
||||
/* !!! evaluates all meta attributes => inefficient */
|
||||
MetaInfo meta = queryMetaInfo(state);
|
||||
MetaInfo::iterator i = meta.find(name);
|
||||
return i == meta.end() ? "" : i->second;
|
||||
return queryMetaInfo(state)[name];
|
||||
}
|
||||
|
||||
|
||||
void DrvInfo::setMetaInfo(const MetaInfo & meta)
|
||||
{
|
||||
ATermMap metaAttrs;
|
||||
for (MetaInfo::const_iterator i = meta.begin(); i != meta.end(); ++i)
|
||||
metaAttrs.set(toATerm(i->first),
|
||||
makeAttrRHS(makeStr(i->second), makeNoPos()));
|
||||
attrs->set(toATerm("meta"), makeAttrs(metaAttrs));
|
||||
metaInfoRead = true;
|
||||
this->meta = meta;
|
||||
}
|
||||
|
||||
|
||||
/* Cache for already evaluated derivations. Usually putting ATerms in
|
||||
a STL container is unsafe (they're not scanning for GC roots), but
|
||||
here it doesn't matter; everything in this set is reachable from
|
||||
the stack as well. */
|
||||
typedef set<Expr> Exprs;
|
||||
/* Cache for already considered attrsets. */
|
||||
typedef set<Bindings *> Done;
|
||||
|
||||
|
||||
/* Evaluate expression `e'. If it evaluates to an attribute set of
|
||||
type `derivation', then put information about it in `drvs' (unless
|
||||
it's already in `doneExprs'). The result boolean indicates whether
|
||||
it makes sense for the caller to recursively search for derivations
|
||||
in `e'. */
|
||||
static bool getDerivation(EvalState & state, Expr e,
|
||||
const string & attrPath, DrvInfos & drvs, Exprs & doneExprs)
|
||||
/* Evaluate value `v'. If it evaluates to an attribute set of type
|
||||
`derivation', then put information about it in `drvs' (unless it's
|
||||
already in `doneExprs'). The result boolean indicates whether it
|
||||
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)
|
||||
{
|
||||
try {
|
||||
|
||||
ATermList es;
|
||||
e = evalExpr(state, e);
|
||||
if (!matchAttrs(e, es)) return true;
|
||||
|
||||
boost::shared_ptr<ATermMap> attrs(new ATermMap());
|
||||
queryAllAttrs(e, *attrs, false);
|
||||
|
||||
Expr a = attrs->get(toATerm("type"));
|
||||
if (!a || evalStringNoCtx(state, a) != "derivation") return true;
|
||||
state.forceValue(v);
|
||||
if (!state.isDerivation(v)) return true;
|
||||
|
||||
/* Remove spurious duplicates (e.g., an attribute set like
|
||||
`rec { x = derivation {...}; y = x;}'. */
|
||||
if (doneExprs.find(e) != doneExprs.end()) return false;
|
||||
doneExprs.insert(e);
|
||||
if (done.find(v.attrs) != done.end()) return false;
|
||||
done.insert(v.attrs);
|
||||
|
||||
DrvInfo drv;
|
||||
|
||||
a = attrs->get(toATerm("name"));
|
||||
Bindings::iterator i = v.attrs->find(state.sName);
|
||||
/* !!! We really would like to have a decent back trace here. */
|
||||
if (!a) throw TypeError("derivation name missing");
|
||||
drv.name = evalStringNoCtx(state, a);
|
||||
if (i == v.attrs->end()) throw TypeError("derivation name missing");
|
||||
drv.name = state.forceStringNoCtx(*i->value);
|
||||
|
||||
a = attrs->get(toATerm("system"));
|
||||
if (!a)
|
||||
Bindings::iterator i2 = v.attrs->find(state.sSystem);
|
||||
if (i2 == v.attrs->end())
|
||||
drv.system = "unknown";
|
||||
else
|
||||
drv.system = evalStringNoCtx(state, a);
|
||||
drv.system = state.forceStringNoCtx(*i2->value);
|
||||
|
||||
drv.attrs = attrs;
|
||||
drv.attrs = v.attrs;
|
||||
|
||||
drv.attrPath = attrPath;
|
||||
|
||||
@@ -137,11 +120,11 @@ static bool getDerivation(EvalState & state, Expr e,
|
||||
}
|
||||
|
||||
|
||||
bool getDerivation(EvalState & state, Expr e, DrvInfo & drv)
|
||||
bool getDerivation(EvalState & state, Value & v, DrvInfo & drv)
|
||||
{
|
||||
Exprs doneExprs;
|
||||
Done done;
|
||||
DrvInfos drvs;
|
||||
getDerivation(state, e, "", drvs, doneExprs);
|
||||
getDerivation(state, v, "", drvs, done);
|
||||
if (drvs.size() != 1) return false;
|
||||
drv = drvs.front();
|
||||
return true;
|
||||
@@ -154,77 +137,73 @@ static string addToPath(const string & s1, const string & s2)
|
||||
}
|
||||
|
||||
|
||||
static void getDerivations(EvalState & state, Expr e,
|
||||
const string & pathPrefix, const ATermMap & autoArgs,
|
||||
DrvInfos & drvs, Exprs & doneExprs)
|
||||
static void getDerivations(EvalState & state, Value & vIn,
|
||||
const string & pathPrefix, Bindings & autoArgs,
|
||||
DrvInfos & drvs, Done & done)
|
||||
{
|
||||
e = evalExpr(state, autoCallFunction(evalExpr(state, e), autoArgs));
|
||||
|
||||
Value v;
|
||||
state.autoCallFunction(autoArgs, vIn, v);
|
||||
|
||||
/* Process the expression. */
|
||||
ATermList es;
|
||||
DrvInfo drv;
|
||||
|
||||
if (!getDerivation(state, e, pathPrefix, drvs, doneExprs))
|
||||
return;
|
||||
if (!getDerivation(state, v, pathPrefix, drvs, done)) ;
|
||||
|
||||
if (matchAttrs(e, es)) {
|
||||
ATermMap drvMap(ATgetLength(es));
|
||||
queryAllAttrs(e, drvMap);
|
||||
else if (v.type == tAttrs) {
|
||||
|
||||
/* !!! undocumented hackery to support combining channels in
|
||||
nix-env.cc. */
|
||||
Expr e2 = drvMap.get(toATerm("_combineChannels"));
|
||||
bool combineChannels = e2 && evalBool(state, e2);
|
||||
|
||||
for (ATermMap::const_iterator i = drvMap.begin(); i != drvMap.end(); ++i) {
|
||||
startNest(nest, lvlDebug,
|
||||
format("evaluating attribute `%1%'") % aterm2String(i->key));
|
||||
string pathPrefix2 = addToPath(pathPrefix, aterm2String(i->key));
|
||||
if (combineChannels) {
|
||||
if (((string) aterm2String(i->key)) != "_combineChannels")
|
||||
getDerivations(state, i->value, pathPrefix2, autoArgs, drvs, doneExprs);
|
||||
}
|
||||
else if (getDerivation(state, i->value, pathPrefix2, drvs, doneExprs)) {
|
||||
bool combineChannels = v.attrs->find(state.symbols.create("_combineChannels")) != v.attrs->end();
|
||||
|
||||
/* Consider the attributes in sorted order to get more
|
||||
deterministic behaviour in nix-env operations (e.g. when
|
||||
there are names clashes between derivations, the derivation
|
||||
bound to the attribute with the "lower" name should take
|
||||
precedence). */
|
||||
typedef std::map<string, Symbol> SortedSymbols;
|
||||
SortedSymbols attrs;
|
||||
foreach (Bindings::iterator, i, *v.attrs)
|
||||
attrs.insert(std::pair<string, Symbol>(i->name, i->name));
|
||||
|
||||
foreach (SortedSymbols::iterator, i, attrs) {
|
||||
startNest(nest, lvlDebug, format("evaluating attribute `%1%'") % i->first);
|
||||
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)) {
|
||||
/* If the value of this attribute is itself an
|
||||
attribute set, should we recurse into it? => Only
|
||||
if it has a `recurseForDerivations = true'
|
||||
attribute. */
|
||||
ATermList es;
|
||||
Expr e = evalExpr(state, i->value);
|
||||
if (matchAttrs(e, es)) {
|
||||
ATermMap attrs(ATgetLength(es));
|
||||
queryAllAttrs(e, attrs, false);
|
||||
if (((e2 = attrs.get(toATerm("recurseForDerivations")))
|
||||
&& evalBool(state, e2)))
|
||||
getDerivations(state, e, pathPrefix2, autoArgs, drvs, doneExprs);
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (matchList(e, es)) {
|
||||
int n = 0;
|
||||
for (ATermIterator i(es); i; ++i, ++n) {
|
||||
else if (v.type == tList) {
|
||||
for (unsigned int n = 0; n < v.list.length; ++n) {
|
||||
startNest(nest, lvlDebug,
|
||||
format("evaluating list element"));
|
||||
string pathPrefix2 = addToPath(pathPrefix, (format("%1%") % n).str());
|
||||
if (getDerivation(state, *i, pathPrefix2, drvs, doneExprs))
|
||||
getDerivations(state, *i, pathPrefix2, autoArgs, drvs, doneExprs);
|
||||
if (getDerivation(state, *v.list.elems[n], pathPrefix2, drvs, done))
|
||||
getDerivations(state, *v.list.elems[n], pathPrefix2, autoArgs, drvs, done);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
throw TypeError("expression does not evaluate to a derivation (or a set or list of those)");
|
||||
else throw TypeError("expression does not evaluate to a derivation (or a set or list of those)");
|
||||
}
|
||||
|
||||
|
||||
void getDerivations(EvalState & state, Expr e, const string & pathPrefix,
|
||||
const ATermMap & autoArgs, DrvInfos & drvs)
|
||||
void getDerivations(EvalState & state, Value & v, const string & pathPrefix,
|
||||
Bindings & autoArgs, DrvInfos & drvs)
|
||||
{
|
||||
Exprs doneExprs;
|
||||
getDerivations(state, e, pathPrefix, autoArgs, drvs, doneExprs);
|
||||
Done done;
|
||||
getDerivations(state, v, pathPrefix, autoArgs, drvs, done);
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -1,18 +1,27 @@
|
||||
#ifndef __GET_DRVS_H
|
||||
#define __GET_DRVS_H
|
||||
|
||||
#include "eval.hh"
|
||||
|
||||
#include <string>
|
||||
#include <map>
|
||||
|
||||
#include <boost/shared_ptr.hpp>
|
||||
|
||||
#include "eval.hh"
|
||||
|
||||
|
||||
namespace nix {
|
||||
|
||||
|
||||
typedef std::map<string, string> MetaInfo;
|
||||
struct MetaValue
|
||||
{
|
||||
enum { tpNone, tpString, tpStrings, tpInt } type;
|
||||
string stringValue;
|
||||
Strings stringValues;
|
||||
int intValue;
|
||||
};
|
||||
|
||||
|
||||
typedef std::map<string, MetaValue> MetaInfo;
|
||||
|
||||
|
||||
struct DrvInfo
|
||||
@@ -20,21 +29,24 @@ struct DrvInfo
|
||||
private:
|
||||
string drvPath;
|
||||
string outPath;
|
||||
|
||||
bool metaInfoRead;
|
||||
MetaInfo meta;
|
||||
|
||||
public:
|
||||
string name;
|
||||
string attrPath; /* path towards the derivation */
|
||||
string system;
|
||||
|
||||
/* !!! these should really be hidden, and setMetaInfo() should
|
||||
make a copy since the ATermMap can be shared between multiple
|
||||
DrvInfos. */
|
||||
boost::shared_ptr<ATermMap> attrs;
|
||||
/* !!! make this private */
|
||||
Bindings * attrs;
|
||||
|
||||
DrvInfo() : metaInfoRead(false), attrs(0) { };
|
||||
|
||||
string queryDrvPath(EvalState & state) const;
|
||||
string queryOutPath(EvalState & state) const;
|
||||
MetaInfo queryMetaInfo(EvalState & state) const;
|
||||
string queryMetaInfo(EvalState & state, const string & name) const;
|
||||
MetaValue queryMetaInfo(EvalState & state, const string & name) const;
|
||||
|
||||
void setDrvPath(const string & s)
|
||||
{
|
||||
@@ -50,16 +62,19 @@ public:
|
||||
};
|
||||
|
||||
|
||||
#if HAVE_BOEHMGC
|
||||
typedef list<DrvInfo, traceable_allocator<DrvInfo> > DrvInfos;
|
||||
#else
|
||||
typedef list<DrvInfo> DrvInfos;
|
||||
#endif
|
||||
|
||||
|
||||
/* Evaluate expression `e'. If it evaluates to a derivation, store
|
||||
information about the derivation in `drv' and return true.
|
||||
Otherwise, return false. */
|
||||
bool getDerivation(EvalState & state, Expr e, DrvInfo & drv);
|
||||
/* 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);
|
||||
|
||||
void getDerivations(EvalState & state, Expr e, const string & pathPrefix,
|
||||
const ATermMap & autoArgs, DrvInfos & drvs);
|
||||
void getDerivations(EvalState & state, Value & v, const string & pathPrefix,
|
||||
Bindings & autoArgs, DrvInfos & drvs);
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -8,9 +8,7 @@
|
||||
|
||||
|
||||
%{
|
||||
#include "aterm.hh"
|
||||
#include "nixexpr.hh"
|
||||
#include "nixexpr-ast.hh"
|
||||
#define BISON_HEADER_HACK
|
||||
#include "parser-tab.hh"
|
||||
|
||||
@@ -21,13 +19,16 @@ namespace nix {
|
||||
|
||||
static void initLoc(YYLTYPE * loc)
|
||||
{
|
||||
loc->first_line = 1;
|
||||
loc->first_column = 1;
|
||||
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;
|
||||
loc->first_column = loc->last_column;
|
||||
|
||||
while (len--) {
|
||||
switch (*s++) {
|
||||
case '\r':
|
||||
@@ -35,17 +36,17 @@ static void adjustLoc(YYLTYPE * loc, const char * s, size_t len)
|
||||
s++;
|
||||
/* fall through */
|
||||
case '\n':
|
||||
++loc->first_line;
|
||||
loc->first_column = 1;
|
||||
++loc->last_line;
|
||||
loc->last_column = 1;
|
||||
break;
|
||||
default:
|
||||
++loc->first_column;
|
||||
++loc->last_column;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static Expr unescapeStr(const char * s)
|
||||
static Expr * unescapeStr(SymbolTable & symbols, const char * s)
|
||||
{
|
||||
string t;
|
||||
char c;
|
||||
@@ -65,7 +66,7 @@ static Expr unescapeStr(const char * s)
|
||||
}
|
||||
else t += c;
|
||||
}
|
||||
return makeStr(toATerm(t), ATempty);
|
||||
return new ExprString(symbols.create(t));
|
||||
}
|
||||
|
||||
|
||||
@@ -95,6 +96,7 @@ let { return LET; }
|
||||
in { return IN; }
|
||||
rec { return REC; }
|
||||
inherit { return INHERIT; }
|
||||
\.\.\. { return ELLIPSIS; }
|
||||
|
||||
\=\= { return EQ; }
|
||||
\!\= { return NEQ; }
|
||||
@@ -104,19 +106,20 @@ inherit { return INHERIT; }
|
||||
\/\/ { return UPDATE; }
|
||||
\+\+ { return CONCAT; }
|
||||
|
||||
{ID} { yylval->t = toATerm(yytext); return ID; /* !!! alloc */ }
|
||||
{ID} { yylval->id = strdup(yytext); return ID; }
|
||||
{INT} { int n = atoi(yytext); /* !!! overflow */
|
||||
yylval->t = ATmake("<int>", n);
|
||||
yylval->n = n;
|
||||
return INT;
|
||||
}
|
||||
|
||||
\" { BEGIN(STRING); return '"'; }
|
||||
<STRING>([^\$\"\\]|\$[^\{\"]|\\.)+ {
|
||||
/* !!! Not quite right: we want a follow restriction on "$", it
|
||||
shouldn't be followed by a "{". Right now "$\"" will be consumed
|
||||
as part of a string, rather than a "$" followed by the string
|
||||
terminator. Disallow "$\"" for now. */
|
||||
yylval->t = unescapeStr(yytext); /* !!! alloc */
|
||||
/* !!! Not quite right: we want a follow restriction on
|
||||
"$", it shouldn't be followed by a "{". Right now
|
||||
"$\"" will be consumed as part of a string, rather
|
||||
than a "$" followed by the string terminator.
|
||||
Disallow "$\"" for now. */
|
||||
yylval->e = unescapeStr(data->symbols, yytext);
|
||||
return STR;
|
||||
}
|
||||
<STRING>\$\{ { BEGIN(INITIAL); return DOLLAR_CURLY; }
|
||||
@@ -124,28 +127,32 @@ inherit { return INHERIT; }
|
||||
<STRING>. return yytext[0]; /* just in case: shouldn't be reached */
|
||||
|
||||
\'\'(\ *\n)? { BEGIN(IND_STRING); return IND_STRING_OPEN; }
|
||||
<IND_STRING>([^\$\']|\$[^\{\']|\'[^\'])+ {
|
||||
yylval->t = makeIndStr(toATerm(yytext));
|
||||
<IND_STRING>([^\$\']|\$[^\{\']|\'[^\'\$])+ {
|
||||
yylval->e = new ExprIndStr(yytext);
|
||||
return IND_STR;
|
||||
}
|
||||
<IND_STRING>\'\'\$ {
|
||||
yylval->t = makeIndStr(toATerm("$"));
|
||||
yylval->e = new ExprIndStr("$");
|
||||
return IND_STR;
|
||||
}
|
||||
<IND_STRING>\'\'\' {
|
||||
yylval->t = makeIndStr(toATerm("''"));
|
||||
yylval->e = new ExprIndStr("''");
|
||||
return IND_STR;
|
||||
}
|
||||
<IND_STRING>\'\'\\. {
|
||||
yylval->t = unescapeStr(yytext + 2);
|
||||
yylval->e = unescapeStr(data->symbols, yytext + 2);
|
||||
return IND_STR;
|
||||
}
|
||||
<IND_STRING>\$\{ { BEGIN(INITIAL); return DOLLAR_CURLY; }
|
||||
<IND_STRING>\'\' { BEGIN(INITIAL); return IND_STRING_CLOSE; }
|
||||
<IND_STRING>\' {
|
||||
yylval->e = new ExprIndStr("'");
|
||||
return IND_STR;
|
||||
}
|
||||
<IND_STRING>. return yytext[0]; /* just in case: shouldn't be reached */
|
||||
|
||||
{PATH} { yylval->t = toATerm(yytext); return PATH; /* !!! alloc */ }
|
||||
{URI} { yylval->t = toATerm(yytext); return URI; /* !!! alloc */ }
|
||||
{PATH} { yylval->path = strdup(yytext); return PATH; }
|
||||
{URI} { yylval->uri = strdup(yytext); return URI; }
|
||||
|
||||
[ \t\r\n]+ /* eat up whitespace */
|
||||
\#[^\r\n]* /* single-line comments */
|
||||
|
||||
@@ -92,25 +92,6 @@ int compareVersions(const string & v1, const string & v2)
|
||||
}
|
||||
|
||||
|
||||
static void testCompareVersions()
|
||||
{
|
||||
#define TEST(v1, v2, n) assert( \
|
||||
compareVersions(v1, v2) == n && compareVersions(v2, v1) == -n)
|
||||
TEST("1.0", "2.3", -1);
|
||||
TEST("2.1", "2.3", -1);
|
||||
TEST("2.3", "2.3", 0);
|
||||
TEST("2.5", "2.3", 1);
|
||||
TEST("3.1", "2.3", 1);
|
||||
TEST("2.3.1", "2.3", 1);
|
||||
TEST("2.3.1", "2.3a", 1);
|
||||
TEST("2.3pre1", "2.3", -1);
|
||||
TEST("2.3pre3", "2.3pre12", -1);
|
||||
TEST("2.3a", "2.3c", -1);
|
||||
TEST("2.3pre1", "2.3c", -1);
|
||||
TEST("2.3pre1", "2.3q", -1);
|
||||
}
|
||||
|
||||
|
||||
DrvNames drvNamesFromArgs(const Strings & opArgs)
|
||||
{
|
||||
DrvNames result;
|
||||
@@ -1,10 +1,13 @@
|
||||
%% Note: this SDF grammar is no longer used in the Nix expression
|
||||
%% parser and may not be up to date.
|
||||
|
||||
definition
|
||||
|
||||
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
%% Top level syntax.
|
||||
|
||||
module Nix
|
||||
module Main
|
||||
imports Nix-Exprs Nix-Layout
|
||||
|
||||
|
||||
@@ -38,6 +41,7 @@ exports
|
||||
"with" Expr ";" Expr -> Expr {cons("With")}
|
||||
|
||||
"rec" "{" Bind* "}" -> Expr {cons("Rec")}
|
||||
"let" Bind* "in" Expr -> Expr {cons("Let")}
|
||||
"let" "{" Bind* "}" -> Expr {cons("LetRec")}
|
||||
"{" Bind* "}" -> Expr {cons("Attrs")}
|
||||
|
||||
@@ -102,7 +106,8 @@ exports
|
||||
|
||||
[0-9]+ -> Int
|
||||
|
||||
"\"" ~[\n\"]* "\"" -> Str
|
||||
"\"" (~[\"\\] | ("\\" ~[]) )* "\"" -> Str
|
||||
"''" (~[\"\\] | ("\\" ~[]) )* "''" -> Str
|
||||
|
||||
[a-zA-Z0-9\.\_\-\+]* ("/"[a-zA-Z0-9\.\_\-\+]+)+ -> Path
|
||||
|
||||
|
||||
@@ -1,95 +0,0 @@
|
||||
init initNixExprHelpers
|
||||
|
||||
Pos | string int int | Pos |
|
||||
NoPos | | Pos |
|
||||
|
||||
Function | ATermList Expr Pos | Expr |
|
||||
Function1 | string Expr Pos | Expr |
|
||||
Assert | Expr Expr Pos | Expr |
|
||||
With | Expr Expr Pos | Expr |
|
||||
If | Expr Expr Expr | Expr |
|
||||
OpNot | Expr | Expr |
|
||||
OpEq | Expr Expr | Expr |
|
||||
OpNEq | Expr Expr | Expr |
|
||||
OpAnd | Expr Expr | Expr |
|
||||
OpOr | Expr Expr | Expr |
|
||||
OpImpl | Expr Expr | Expr |
|
||||
OpUpdate | Expr Expr | Expr |
|
||||
SubPath | Expr Expr | Expr |
|
||||
OpHasAttr | Expr string | Expr |
|
||||
OpPlus | Expr Expr | Expr |
|
||||
OpConcat | Expr Expr | Expr |
|
||||
ConcatStrings | ATermList | Expr |
|
||||
Call | Expr Expr | Expr |
|
||||
Select | Expr string | Expr |
|
||||
Var | string | Expr |
|
||||
Int | int | Expr |
|
||||
|
||||
# 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
|
||||
#
|
||||
# "--with-freetype2-library=" + freetype + "/lib"
|
||||
#
|
||||
# where `freetype' is a derivation (or a source to be copied to the
|
||||
# store). If we just concatenated the strings without keeping track
|
||||
# of the referenced store paths, then if the string is used as a
|
||||
# derivation attribute, the derivation will not have the correct
|
||||
# dependencies in its inputDrvs and inputSrcs.
|
||||
#
|
||||
# The semantics of the context is as follows: when a string with
|
||||
# context C is used as a derivation attribute, then the derivations in
|
||||
# C will be added to the inputDrvs of the derivation, and the other
|
||||
# store paths in C will be added to the inputSrcs of the derivations.
|
||||
#
|
||||
# For canonicity, the store paths should be in sorted order.
|
||||
Str | string ATermList | Expr |
|
||||
Str | string | Expr | ObsoleteStr
|
||||
|
||||
# Internal to the parser, doesn't occur in ASTs.
|
||||
IndStr | string | Expr |
|
||||
|
||||
# A path is a reference to a file system object that is to be copied
|
||||
# to the Nix store when used as a derivation attribute. When it is
|
||||
# concatenated to a string (i.e., `str + path'), it is also copied and
|
||||
# the resulting store path is concatenated to the string (with the
|
||||
# store path in the context). If a string or path is concatenated to
|
||||
# a path (i.e., `path + str' or `path + path'), the result is a new
|
||||
# path (if the right-hand side is a string, the context must be
|
||||
# empty).
|
||||
Path | string | Expr |
|
||||
|
||||
List | ATermList | Expr |
|
||||
BlackHole | | Expr |
|
||||
Undefined | | Expr |
|
||||
Removed | | Expr |
|
||||
PrimOp | int ATermBlob ATermList | Expr |
|
||||
Attrs | ATermList | Expr |
|
||||
Closed | Expr | Expr |
|
||||
Rec | ATermList ATermList | Expr |
|
||||
Bool | ATerm | Expr |
|
||||
Null | | Expr |
|
||||
|
||||
Bind | string Expr Pos | ATerm |
|
||||
Bind | string Expr | ATerm | ObsoleteBind
|
||||
Inherit | Expr ATermList Pos | ATerm |
|
||||
|
||||
Scope | | Expr |
|
||||
|
||||
Formal | string ValidValues DefaultValue | ATerm |
|
||||
|
||||
ValidValues | ATermList | ValidValues |
|
||||
UnrestrictedValues | | ValidValues |
|
||||
|
||||
DefaultValue | Expr | DefaultValue |
|
||||
NoDefaultValue | | DefaultValue |
|
||||
|
||||
True | | ATerm |
|
||||
False | | ATerm |
|
||||
|
||||
PrimOpDef | int ATermBlob | ATerm |
|
||||
|
||||
AttrRHS | Expr Pos | ATerm |
|
||||
|
||||
eTrue = makeBool(makeTrue())
|
||||
eFalse = makeBool(makeFalse())
|
||||
@@ -1,403 +1,314 @@
|
||||
#include "nixexpr.hh"
|
||||
#include "derivations.hh"
|
||||
#include "util.hh"
|
||||
#include "aterm.hh"
|
||||
|
||||
#include "nixexpr-ast.hh"
|
||||
#include "nixexpr-ast.cc"
|
||||
#include <cstdlib>
|
||||
|
||||
|
||||
namespace nix {
|
||||
|
||||
|
||||
/* Displaying abstract syntax trees. */
|
||||
|
||||
std::ostream & operator << (std::ostream & str, Expr & e)
|
||||
{
|
||||
e.show(str);
|
||||
return str;
|
||||
}
|
||||
|
||||
void Expr::show(std::ostream & str)
|
||||
{
|
||||
abort();
|
||||
}
|
||||
|
||||
void ExprInt::show(std::ostream & str)
|
||||
{
|
||||
str << n;
|
||||
}
|
||||
|
||||
void ExprString::show(std::ostream & str)
|
||||
{
|
||||
str << "\"" << s << "\""; // !!! escaping
|
||||
}
|
||||
|
||||
void ExprPath::show(std::ostream & str)
|
||||
{
|
||||
str << s;
|
||||
}
|
||||
|
||||
void ExprVar::show(std::ostream & str)
|
||||
{
|
||||
str << info.name;
|
||||
}
|
||||
|
||||
void ExprSelect::show(std::ostream & str)
|
||||
{
|
||||
str << "(" << *e << ")." << name;
|
||||
}
|
||||
|
||||
void ExprOpHasAttr::show(std::ostream & str)
|
||||
{
|
||||
str << "(" << *e << ") ? " << name;
|
||||
}
|
||||
|
||||
void ExprAttrs::show(std::ostream & str)
|
||||
{
|
||||
if (recursive) str << "rec ";
|
||||
str << "{ ";
|
||||
foreach (AttrDefs::iterator, i, attrs)
|
||||
if (i->second.inherited)
|
||||
str << "inherit " << i->first << " " << "; ";
|
||||
else
|
||||
str << i->first << " = " << *i->second.e << "; ";
|
||||
str << "}";
|
||||
}
|
||||
|
||||
void ExprList::show(std::ostream & str)
|
||||
{
|
||||
str << "[ ";
|
||||
foreach (vector<Expr *>::iterator, i, elems)
|
||||
str << "(" << **i << ") ";
|
||||
str << "]";
|
||||
}
|
||||
|
||||
void ExprLambda::show(std::ostream & str)
|
||||
{
|
||||
str << "(";
|
||||
if (matchAttrs) {
|
||||
str << "{ ";
|
||||
bool first = true;
|
||||
foreach (Formals::Formals_::iterator, i, formals->formals) {
|
||||
if (first) first = false; else str << ", ";
|
||||
str << i->name;
|
||||
if (i->def) str << " ? " << *i->def;
|
||||
}
|
||||
str << " }";
|
||||
if (!arg.empty()) str << " @ ";
|
||||
}
|
||||
if (!arg.empty()) str << arg;
|
||||
str << ": " << *body << ")";
|
||||
}
|
||||
|
||||
void ExprLet::show(std::ostream & str)
|
||||
{
|
||||
str << "let ";
|
||||
foreach (ExprAttrs::AttrDefs::iterator, i, attrs->attrs)
|
||||
if (i->second.inherited)
|
||||
str << "inherit " << i->first << "; ";
|
||||
else
|
||||
str << i->first << " = " << *i->second.e << "; ";
|
||||
str << "in " << *body;
|
||||
}
|
||||
|
||||
void ExprWith::show(std::ostream & str)
|
||||
{
|
||||
str << "with " << *attrs << "; " << *body;
|
||||
}
|
||||
|
||||
void ExprIf::show(std::ostream & str)
|
||||
{
|
||||
str << "if " << *cond << " then " << *then << " else " << *else_;
|
||||
}
|
||||
|
||||
void ExprAssert::show(std::ostream & str)
|
||||
{
|
||||
str << "assert " << *cond << "; " << *body;
|
||||
}
|
||||
|
||||
void ExprOpNot::show(std::ostream & str)
|
||||
{
|
||||
str << "! " << *e;
|
||||
}
|
||||
|
||||
void ExprConcatStrings::show(std::ostream & str)
|
||||
{
|
||||
bool first = true;
|
||||
foreach (vector<Expr *>::iterator, i, *es) {
|
||||
if (first) first = false; else str << " + ";
|
||||
str << **i;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
std::ostream & operator << (std::ostream & str, const Pos & pos)
|
||||
{
|
||||
if (!pos.line)
|
||||
str << "undefined position";
|
||||
else
|
||||
str << (format("`%1%:%2%:%3%'") % pos.file % pos.line % pos.column).str();
|
||||
return str;
|
||||
}
|
||||
|
||||
|
||||
Pos noPos;
|
||||
|
||||
|
||||
/* Computing levels/displacements for variables. */
|
||||
|
||||
void Expr::bindVars(const StaticEnv & env)
|
||||
{
|
||||
abort();
|
||||
}
|
||||
|
||||
void ExprInt::bindVars(const StaticEnv & env)
|
||||
{
|
||||
}
|
||||
|
||||
void ExprString::bindVars(const StaticEnv & env)
|
||||
{
|
||||
}
|
||||
|
||||
void ExprPath::bindVars(const StaticEnv & env)
|
||||
{
|
||||
}
|
||||
|
||||
void VarRef::bind(const StaticEnv & env)
|
||||
{
|
||||
/* Check whether the variable appears in the environment. If so,
|
||||
set its level and displacement. */
|
||||
const StaticEnv * curEnv;
|
||||
unsigned int level;
|
||||
int withLevel = -1;
|
||||
for (curEnv = &env, level = 0; curEnv; curEnv = curEnv->up, level++) {
|
||||
if (curEnv->isWith) {
|
||||
if (withLevel == -1) withLevel = level;
|
||||
} else {
|
||||
StaticEnv::Vars::const_iterator i = curEnv->vars.find(name);
|
||||
if (i != curEnv->vars.end()) {
|
||||
fromWith = false;
|
||||
this->level = level;
|
||||
displ = i->second;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Otherwise, the variable must be obtained from the nearest
|
||||
enclosing `with'. If there is no `with', then we can issue an
|
||||
"undefined variable" error now. */
|
||||
if (withLevel == -1) throw EvalError(format("undefined variable `%1%'") % name);
|
||||
|
||||
fromWith = true;
|
||||
this->level = withLevel;
|
||||
}
|
||||
|
||||
void ExprVar::bindVars(const StaticEnv & env)
|
||||
{
|
||||
info.bind(env);
|
||||
}
|
||||
|
||||
void ExprSelect::bindVars(const StaticEnv & env)
|
||||
{
|
||||
e->bindVars(env);
|
||||
}
|
||||
|
||||
void ExprOpHasAttr::bindVars(const StaticEnv & env)
|
||||
{
|
||||
e->bindVars(env);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
string showPos(ATerm pos)
|
||||
{
|
||||
ATerm path;
|
||||
int line, column;
|
||||
if (matchNoPos(pos)) return "undefined position";
|
||||
if (!matchPos(pos, path, line, column))
|
||||
throw badTerm("position expected", pos);
|
||||
return (format("`%1%', line %2%") % aterm2String(path) % line).str();
|
||||
else
|
||||
foreach (AttrDefs::iterator, i, attrs)
|
||||
if (i->second.inherited) i->second.var.bind(env);
|
||||
else i->second.e->bindVars(env);
|
||||
}
|
||||
|
||||
void ExprList::bindVars(const StaticEnv & env)
|
||||
{
|
||||
foreach (vector<Expr *>::iterator, i, elems)
|
||||
(*i)->bindVars(env);
|
||||
}
|
||||
|
||||
void ExprLambda::bindVars(const StaticEnv & env)
|
||||
{
|
||||
StaticEnv newEnv(false, &env);
|
||||
|
||||
|
||||
ATerm bottomupRewrite(TermFun & f, ATerm e)
|
||||
{
|
||||
checkInterrupt();
|
||||
|
||||
if (ATgetType(e) == AT_APPL) {
|
||||
AFun fun = ATgetAFun(e);
|
||||
int arity = ATgetArity(fun);
|
||||
ATerm args[arity];
|
||||
|
||||
for (int i = 0; i < arity; ++i)
|
||||
args[i] = bottomupRewrite(f, ATgetArgument(e, i));
|
||||
|
||||
e = (ATerm) ATmakeApplArray(fun, args);
|
||||
}
|
||||
|
||||
else if (ATgetType(e) == AT_LIST) {
|
||||
ATermList in = (ATermList) e;
|
||||
ATermList out = ATempty;
|
||||
|
||||
for (ATermIterator i(in); i; ++i)
|
||||
out = ATinsert(out, bottomupRewrite(f, *i));
|
||||
|
||||
e = (ATerm) ATreverse(out);
|
||||
}
|
||||
|
||||
return f(e);
|
||||
}
|
||||
|
||||
|
||||
void queryAllAttrs(Expr e, ATermMap & attrs, bool withPos)
|
||||
{
|
||||
ATermList bnds;
|
||||
if (!matchAttrs(e, bnds))
|
||||
throw TypeError(format("value is %1% while an attribute set was expected") % showType(e));
|
||||
|
||||
for (ATermIterator i(bnds); i; ++i) {
|
||||
ATerm name;
|
||||
Expr e;
|
||||
ATerm pos;
|
||||
if (!matchBind(*i, name, e, pos)) abort(); /* can't happen */
|
||||
attrs.set(name, withPos ? makeAttrRHS(e, pos) : e);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Expr queryAttr(Expr e, const string & name)
|
||||
{
|
||||
ATerm dummy;
|
||||
return queryAttr(e, name, dummy);
|
||||
}
|
||||
|
||||
|
||||
Expr queryAttr(Expr e, const string & name, ATerm & pos)
|
||||
{
|
||||
ATermList bnds;
|
||||
if (!matchAttrs(e, bnds))
|
||||
throw TypeError(format("value is %1% while an attribute set was expected") % showType(e));
|
||||
|
||||
for (ATermIterator i(bnds); i; ++i) {
|
||||
ATerm name2, pos2;
|
||||
Expr e;
|
||||
if (!matchBind(*i, name2, e, pos2))
|
||||
abort(); /* can't happen */
|
||||
if (aterm2String(name2) == name) {
|
||||
pos = pos2;
|
||||
return e;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
Expr makeAttrs(const ATermMap & attrs)
|
||||
{
|
||||
ATermList bnds = ATempty;
|
||||
for (ATermMap::const_iterator i = attrs.begin(); i != attrs.end(); ++i) {
|
||||
Expr e;
|
||||
ATerm pos;
|
||||
if (!matchAttrRHS(i->value, e, pos))
|
||||
abort(); /* can't happen */
|
||||
bnds = ATinsert(bnds, makeBind(i->key, e, pos));
|
||||
}
|
||||
return makeAttrs(bnds);
|
||||
}
|
||||
|
||||
|
||||
Expr substitute(const Substitution & subs, Expr e)
|
||||
{
|
||||
checkInterrupt();
|
||||
|
||||
//if (subs.size() == 0) return e;
|
||||
|
||||
ATerm name, pos, e2;
|
||||
|
||||
/* As an optimisation, don't substitute in subterms known to be
|
||||
closed. */
|
||||
if (matchClosed(e, e2)) return e;
|
||||
|
||||
if (matchVar(e, name)) {
|
||||
Expr sub = subs.lookup(name);
|
||||
if (sub == makeRemoved()) sub = 0;
|
||||
Expr wrapped;
|
||||
/* Add a "closed" wrapper around terms that aren't already
|
||||
closed. The check is necessary to prevent repeated
|
||||
wrapping, e.g., closed(closed(closed(...))), which kills
|
||||
caching. */
|
||||
return sub ? (matchClosed(sub, wrapped) ? sub : makeClosed(sub)) : e;
|
||||
}
|
||||
|
||||
/* In case of a function, filter out all variables bound by this
|
||||
function. */
|
||||
ATermList formals;
|
||||
ATerm body;
|
||||
if (matchFunction(e, formals, body, pos)) {
|
||||
ATermMap map(ATgetLength(formals));
|
||||
for (ATermIterator i(formals); i; ++i) {
|
||||
ATerm d1, d2;
|
||||
if (!matchFormal(*i, name, d1, d2)) abort();
|
||||
map.set(name, makeRemoved());
|
||||
}
|
||||
Substitution subs2(&subs, &map);
|
||||
return makeFunction(
|
||||
(ATermList) substitute(subs2, (ATerm) formals),
|
||||
substitute(subs2, body), pos);
|
||||
}
|
||||
|
||||
if (matchFunction1(e, name, body, pos)) {
|
||||
ATermMap map(1);
|
||||
map.set(name, makeRemoved());
|
||||
return makeFunction1(name, substitute(Substitution(&subs, &map), body), pos);
|
||||
}
|
||||
|
||||
/* Idem for a mutually recursive attribute set. */
|
||||
ATermList rbnds, nrbnds;
|
||||
if (matchRec(e, rbnds, nrbnds)) {
|
||||
ATermMap map(ATgetLength(rbnds) + ATgetLength(nrbnds));
|
||||
for (ATermIterator i(rbnds); i; ++i)
|
||||
if (matchBind(*i, name, e2, pos)) map.set(name, makeRemoved());
|
||||
else abort(); /* can't happen */
|
||||
for (ATermIterator i(nrbnds); i; ++i)
|
||||
if (matchBind(*i, name, e2, pos)) map.set(name, makeRemoved());
|
||||
else abort(); /* can't happen */
|
||||
return makeRec(
|
||||
(ATermList) substitute(Substitution(&subs, &map), (ATerm) rbnds),
|
||||
(ATermList) substitute(subs, (ATerm) nrbnds));
|
||||
}
|
||||
|
||||
if (ATgetType(e) == AT_APPL) {
|
||||
AFun fun = ATgetAFun(e);
|
||||
int arity = ATgetArity(fun);
|
||||
ATerm args[arity];
|
||||
bool changed = false;
|
||||
|
||||
for (int i = 0; i < arity; ++i) {
|
||||
ATerm arg = ATgetArgument(e, i);
|
||||
args[i] = substitute(subs, arg);
|
||||
if (args[i] != arg) changed = true;
|
||||
}
|
||||
|
||||
return changed ? (ATerm) ATmakeApplArray(fun, args) : e;
|
||||
}
|
||||
|
||||
if (ATgetType(e) == AT_LIST) {
|
||||
unsigned int len = ATgetLength((ATermList) e);
|
||||
ATerm es[len];
|
||||
ATermIterator i((ATermList) e);
|
||||
for (unsigned int j = 0; i; ++i, ++j)
|
||||
es[j] = substitute(subs, *i);
|
||||
ATermList out = ATempty;
|
||||
for (unsigned int j = len; j; --j)
|
||||
out = ATinsert(out, es[j - 1]);
|
||||
return (ATerm) out;
|
||||
}
|
||||
|
||||
return e;
|
||||
}
|
||||
|
||||
|
||||
/* We use memoisation to prevent exponential complexity on heavily
|
||||
shared ATerms (remember, an ATerm is a graph, not a tree!). Note
|
||||
that using an STL set is fine here wrt to ATerm garbage collection
|
||||
since all the ATerms in the set are already reachable from
|
||||
somewhere else. */
|
||||
static void checkVarDefs2(set<Expr> & done, const ATermMap & defs, Expr e)
|
||||
{
|
||||
if (done.find(e) != done.end()) return;
|
||||
done.insert(e);
|
||||
unsigned int displ = 0;
|
||||
|
||||
ATerm name, pos, value;
|
||||
ATermList formals;
|
||||
ATerm with, body;
|
||||
ATermList rbnds, nrbnds;
|
||||
if (!arg.empty()) newEnv.vars[arg] = displ++;
|
||||
|
||||
if (matchVar(e, name)) {
|
||||
if (!defs.get(name))
|
||||
throw EvalError(format("undefined variable `%1%'")
|
||||
% aterm2String(name));
|
||||
if (matchAttrs) {
|
||||
foreach (Formals::Formals_::iterator, i, formals->formals)
|
||||
newEnv.vars[i->name] = displ++;
|
||||
|
||||
foreach (Formals::Formals_::iterator, i, formals->formals)
|
||||
if (i->def) i->def->bindVars(newEnv);
|
||||
}
|
||||
|
||||
else if (matchFunction(e, formals, body, pos)) {
|
||||
ATermMap defs2(defs);
|
||||
for (ATermIterator i(formals); i; ++i) {
|
||||
ATerm d1, d2;
|
||||
if (!matchFormal(*i, name, d1, d2)) abort();
|
||||
defs2.set(name, (ATerm) ATempty);
|
||||
}
|
||||
for (ATermIterator i(formals); i; ++i) {
|
||||
ATerm valids, deflt;
|
||||
set<Expr> done2;
|
||||
if (!matchFormal(*i, name, valids, deflt)) abort();
|
||||
checkVarDefs2(done, defs, valids);
|
||||
checkVarDefs2(done2, defs2, deflt);
|
||||
}
|
||||
set<Expr> done2;
|
||||
checkVarDefs2(done2, defs2, body);
|
||||
}
|
||||
|
||||
else if (matchFunction1(e, name, body, pos)) {
|
||||
ATermMap defs2(defs);
|
||||
defs2.set(name, (ATerm) ATempty);
|
||||
set<Expr> done2;
|
||||
checkVarDefs2(done2, defs2, body);
|
||||
}
|
||||
|
||||
else if (matchRec(e, rbnds, nrbnds)) {
|
||||
checkVarDefs2(done, defs, (ATerm) nrbnds);
|
||||
ATermMap defs2(defs);
|
||||
for (ATermIterator i(rbnds); i; ++i) {
|
||||
if (!matchBind(*i, name, value, pos)) abort(); /* can't happen */
|
||||
defs2.set(name, (ATerm) ATempty);
|
||||
}
|
||||
for (ATermIterator i(nrbnds); i; ++i) {
|
||||
if (!matchBind(*i, name, value, pos)) abort(); /* can't happen */
|
||||
defs2.set(name, (ATerm) ATempty);
|
||||
}
|
||||
set<Expr> done2;
|
||||
checkVarDefs2(done2, defs2, (ATerm) rbnds);
|
||||
}
|
||||
body->bindVars(newEnv);
|
||||
}
|
||||
|
||||
else if (matchWith(e, with, body, pos)) {
|
||||
/* We can't check the body without evaluating the definitions
|
||||
(which is an arbitrary expression), so we don't do that
|
||||
here but only when actually evaluating the `with'. */
|
||||
checkVarDefs2(done, defs, with);
|
||||
}
|
||||
void ExprLet::bindVars(const StaticEnv & env)
|
||||
{
|
||||
StaticEnv newEnv(false, &env);
|
||||
|
||||
else if (ATgetType(e) == AT_APPL) {
|
||||
int arity = ATgetArity(ATgetAFun(e));
|
||||
for (int i = 0; i < arity; ++i)
|
||||
checkVarDefs2(done, defs, ATgetArgument(e, i));
|
||||
}
|
||||
|
||||
else if (ATgetType(e) == AT_LIST)
|
||||
for (ATermIterator i((ATermList) e); i; ++i)
|
||||
checkVarDefs2(done, defs, *i);
|
||||
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);
|
||||
|
||||
body->bindVars(newEnv);
|
||||
}
|
||||
|
||||
|
||||
void checkVarDefs(const ATermMap & defs, Expr e)
|
||||
void ExprWith::bindVars(const StaticEnv & env)
|
||||
{
|
||||
set<Expr> done;
|
||||
checkVarDefs2(done, defs, e);
|
||||
}
|
||||
|
||||
|
||||
struct Canonicalise : TermFun
|
||||
{
|
||||
ATerm operator () (ATerm e)
|
||||
{
|
||||
/* Remove position info. */
|
||||
ATerm path;
|
||||
int line, column;
|
||||
if (matchPos(e, path, line, column))
|
||||
return makeNoPos();
|
||||
|
||||
/* Sort attribute sets. */
|
||||
ATermList _;
|
||||
if (matchAttrs(e, _)) {
|
||||
ATermMap attrs;
|
||||
queryAllAttrs(e, attrs);
|
||||
StringSet names;
|
||||
for (ATermMap::const_iterator i = attrs.begin(); i != attrs.end(); ++i)
|
||||
names.insert(aterm2String(i->key));
|
||||
|
||||
ATermList attrs2 = ATempty;
|
||||
for (StringSet::reverse_iterator i = names.rbegin(); i != names.rend(); ++i)
|
||||
attrs2 = ATinsert(attrs2,
|
||||
makeBind(toATerm(*i), attrs.get(toATerm(*i)), makeNoPos()));
|
||||
|
||||
return makeAttrs(attrs2);
|
||||
/* Does this `with' have an enclosing `with'? If so, record its
|
||||
level so that `lookupVar' can look up variables in the previous
|
||||
`with' if this one doesn't contain the desired attribute. */
|
||||
const StaticEnv * curEnv;
|
||||
unsigned int level;
|
||||
prevWith = 0;
|
||||
for (curEnv = &env, level = 1; curEnv; curEnv = curEnv->up, level++)
|
||||
if (curEnv->isWith) {
|
||||
prevWith = level;
|
||||
break;
|
||||
}
|
||||
|
||||
return e;
|
||||
}
|
||||
};
|
||||
|
||||
attrs->bindVars(env);
|
||||
StaticEnv newEnv(true, &env);
|
||||
body->bindVars(newEnv);
|
||||
}
|
||||
|
||||
|
||||
Expr canonicaliseExpr(Expr e)
|
||||
void ExprIf::bindVars(const StaticEnv & env)
|
||||
{
|
||||
Canonicalise canonicalise;
|
||||
return bottomupRewrite(canonicalise, e);
|
||||
cond->bindVars(env);
|
||||
then->bindVars(env);
|
||||
else_->bindVars(env);
|
||||
}
|
||||
|
||||
|
||||
Expr makeBool(bool b)
|
||||
void ExprAssert::bindVars(const StaticEnv & env)
|
||||
{
|
||||
return b ? eTrue : eFalse;
|
||||
cond->bindVars(env);
|
||||
body->bindVars(env);
|
||||
}
|
||||
|
||||
|
||||
bool matchStr(Expr e, string & s, PathSet & context)
|
||||
void ExprOpNot::bindVars(const StaticEnv & env)
|
||||
{
|
||||
ATermList l;
|
||||
ATerm s_;
|
||||
|
||||
if (!matchStr(e, s_, l)) return false;
|
||||
|
||||
s = aterm2String(s_);
|
||||
|
||||
for (ATermIterator i(l); i; ++i)
|
||||
context.insert(aterm2String(*i));
|
||||
|
||||
return true;
|
||||
e->bindVars(env);
|
||||
}
|
||||
|
||||
|
||||
Expr makeStr(const string & s, const PathSet & context)
|
||||
void ExprConcatStrings::bindVars(const StaticEnv & env)
|
||||
{
|
||||
return makeStr(toATerm(s), toATermList(context));
|
||||
foreach (vector<Expr *>::iterator, i, *es)
|
||||
(*i)->bindVars(env);
|
||||
}
|
||||
|
||||
|
||||
string showType(Expr e)
|
||||
{
|
||||
ATerm t1, t2, t3;
|
||||
ATermList l1;
|
||||
ATermBlob b1;
|
||||
int i1;
|
||||
if (matchStr(e, t1, l1)) return "a string";
|
||||
if (matchPath(e, t1)) return "a path";
|
||||
if (matchNull(e)) return "null";
|
||||
if (matchInt(e, i1)) return "an integer";
|
||||
if (matchBool(e, t1)) return "a boolean";
|
||||
if (matchFunction(e, l1, t1, t2)) return "a function";
|
||||
if (matchFunction1(e, t1, t2, t3)) return "a function";
|
||||
if (matchAttrs(e, l1)) return "an attribute set";
|
||||
if (matchList(e, l1)) return "a list";
|
||||
if (matchPrimOp(e, i1, b1, l1)) return "a partially applied built-in function";
|
||||
return "an unknown type";
|
||||
}
|
||||
|
||||
|
||||
string showValue(Expr e)
|
||||
{
|
||||
PathSet context;
|
||||
string s;
|
||||
ATerm s2;
|
||||
int i;
|
||||
if (matchStr(e, s, context)) {
|
||||
string u;
|
||||
for (string::iterator i = s.begin(); i != s.end(); ++i)
|
||||
if (*i == '\"' || *i == '\\') u += "\\" + *i;
|
||||
else if (*i == '\n') u += "\\n";
|
||||
else if (*i == '\r') u += "\\r";
|
||||
else if (*i == '\t') u += "\\t";
|
||||
else u += *i;
|
||||
return "\"" + u + "\"";
|
||||
}
|
||||
if (matchPath(e, s2)) return aterm2String(s2);
|
||||
if (matchNull(e)) return "null";
|
||||
if (matchInt(e, i)) return (format("%1%") % i).str();
|
||||
if (e == eTrue) return "true";
|
||||
if (e == eFalse) return "false";
|
||||
/* !!! incomplete */
|
||||
return "<unknown>";
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -1,122 +1,278 @@
|
||||
#ifndef __NIXEXPR_H
|
||||
#define __NIXEXPR_H
|
||||
|
||||
#include <map>
|
||||
#include "symbol-table.hh"
|
||||
|
||||
#include "aterm-map.hh"
|
||||
#include "types.hh"
|
||||
#include <map>
|
||||
|
||||
|
||||
namespace nix {
|
||||
|
||||
|
||||
MakeError(EvalError, Error)
|
||||
MakeError(ParseError, Error)
|
||||
MakeError(AssertionError, EvalError)
|
||||
MakeError(ThrownError, AssertionError)
|
||||
MakeError(Abort, EvalError)
|
||||
MakeError(TypeError, EvalError)
|
||||
MakeError(ImportError, EvalError) // error building an imported derivation
|
||||
|
||||
|
||||
/* Nix expressions are represented as ATerms. The maximal sharing
|
||||
property of the ATerm library allows us to implement caching of
|
||||
normals forms efficiently. */
|
||||
typedef ATerm Expr;
|
||||
/* Position objects. */
|
||||
|
||||
typedef ATerm DefaultValue;
|
||||
typedef ATerm ValidValues;
|
||||
|
||||
typedef ATerm Pos;
|
||||
|
||||
|
||||
/* A STL vector of ATerms. Should be used with great care since it's
|
||||
stored on the heap, and the elements are therefore not roots to the
|
||||
ATerm garbage collector. */
|
||||
typedef vector<ATerm> ATermVector;
|
||||
|
||||
|
||||
/* A substitution is a linked list of ATermMaps that map names to
|
||||
identifiers. We use a list of ATermMaps rather than a single to
|
||||
make it easy to grow or shrink a substitution when entering a
|
||||
scope. */
|
||||
struct Substitution
|
||||
struct Pos
|
||||
{
|
||||
ATermMap * map;
|
||||
const Substitution * prev;
|
||||
string file;
|
||||
unsigned int line, column;
|
||||
Pos() : line(0), column(0) { };
|
||||
Pos(const string & file, unsigned int line, unsigned int column)
|
||||
: file(file), line(line), column(column) { };
|
||||
};
|
||||
|
||||
Substitution(const Substitution * prev, ATermMap * map)
|
||||
{
|
||||
this->prev = prev;
|
||||
this->map = map;
|
||||
}
|
||||
extern Pos noPos;
|
||||
|
||||
std::ostream & operator << (std::ostream & str, const Pos & pos);
|
||||
|
||||
|
||||
struct Env;
|
||||
struct Value;
|
||||
struct EvalState;
|
||||
struct StaticEnv;
|
||||
|
||||
|
||||
/* Abstract syntax of Nix expressions. */
|
||||
|
||||
struct Expr
|
||||
{
|
||||
virtual void show(std::ostream & str);
|
||||
virtual void bindVars(const StaticEnv & env);
|
||||
virtual void eval(EvalState & state, Env & env, Value & v);
|
||||
};
|
||||
|
||||
std::ostream & operator << (std::ostream & str, Expr & e);
|
||||
|
||||
#define COMMON_METHODS \
|
||||
void show(std::ostream & str); \
|
||||
void eval(EvalState & state, Env & env, Value & v); \
|
||||
void bindVars(const StaticEnv & env);
|
||||
|
||||
struct ExprInt : Expr
|
||||
{
|
||||
int n;
|
||||
ExprInt(int n) : n(n) { };
|
||||
COMMON_METHODS
|
||||
};
|
||||
|
||||
struct ExprString : Expr
|
||||
{
|
||||
Symbol s;
|
||||
ExprString(const Symbol & s) : s(s) { };
|
||||
COMMON_METHODS
|
||||
};
|
||||
|
||||
/* Temporary class used during parsing of indented strings. */
|
||||
struct ExprIndStr : Expr
|
||||
{
|
||||
string s;
|
||||
ExprIndStr(const string & s) : s(s) { };
|
||||
};
|
||||
|
||||
struct ExprPath : Expr
|
||||
{
|
||||
string s;
|
||||
ExprPath(const string & s) : s(s) { };
|
||||
COMMON_METHODS
|
||||
};
|
||||
|
||||
struct VarRef
|
||||
{
|
||||
Symbol name;
|
||||
|
||||
/* Whether the variable comes from an environment (e.g. a rec, let
|
||||
or function argument) or from a "with". */
|
||||
bool fromWith;
|
||||
|
||||
Expr lookup(Expr name) const
|
||||
{
|
||||
Expr x;
|
||||
for (const Substitution * s(this); s; s = s->prev)
|
||||
if ((x = s->map->get(name))) return x;
|
||||
return 0;
|
||||
}
|
||||
/* 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
|
||||
value is obtained by getting the attribute named `name' from
|
||||
the attribute set stored in the environment that is `level'
|
||||
levels up from the current one.*/
|
||||
unsigned int level;
|
||||
unsigned int displ;
|
||||
|
||||
VarRef() { };
|
||||
VarRef(const Symbol & name) : name(name) { };
|
||||
void bind(const StaticEnv & env);
|
||||
};
|
||||
|
||||
|
||||
/* Show a position. */
|
||||
string showPos(ATerm pos);
|
||||
|
||||
/* Generic bottomup traversal over ATerms. The traversal first
|
||||
recursively descends into subterms, and then applies the given term
|
||||
function to the resulting term. */
|
||||
struct TermFun
|
||||
struct ExprVar : Expr
|
||||
{
|
||||
virtual ~TermFun() { }
|
||||
virtual ATerm operator () (ATerm e) = 0;
|
||||
VarRef info;
|
||||
ExprVar(const Symbol & name) : info(name) { };
|
||||
COMMON_METHODS
|
||||
};
|
||||
|
||||
struct ExprSelect : Expr
|
||||
{
|
||||
Expr * e;
|
||||
Symbol name;
|
||||
ExprSelect(Expr * e, const Symbol & name) : e(e), name(name) { };
|
||||
COMMON_METHODS
|
||||
};
|
||||
|
||||
struct ExprOpHasAttr : Expr
|
||||
{
|
||||
Expr * e;
|
||||
Symbol name;
|
||||
ExprOpHasAttr(Expr * e, const Symbol & name) : e(e), name(name) { };
|
||||
COMMON_METHODS
|
||||
};
|
||||
|
||||
struct ExprAttrs : Expr
|
||||
{
|
||||
bool recursive;
|
||||
struct AttrDef {
|
||||
bool inherited;
|
||||
Expr * e; // if not inherited
|
||||
VarRef var; // if inherited
|
||||
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() { };
|
||||
};
|
||||
typedef std::map<Symbol, AttrDef> AttrDefs;
|
||||
AttrDefs attrs;
|
||||
ExprAttrs() : recursive(false) { };
|
||||
COMMON_METHODS
|
||||
};
|
||||
|
||||
struct ExprList : Expr
|
||||
{
|
||||
std::vector<Expr *> elems;
|
||||
ExprList() { };
|
||||
COMMON_METHODS
|
||||
};
|
||||
|
||||
struct Formal
|
||||
{
|
||||
Symbol name;
|
||||
Expr * def;
|
||||
Formal(const Symbol & name, Expr * def) : name(name), def(def) { };
|
||||
};
|
||||
|
||||
struct Formals
|
||||
{
|
||||
typedef std::list<Formal> Formals_;
|
||||
Formals_ formals;
|
||||
std::set<Symbol> argNames; // used during parsing
|
||||
bool ellipsis;
|
||||
};
|
||||
|
||||
struct ExprLambda : Expr
|
||||
{
|
||||
Pos pos;
|
||||
Symbol arg;
|
||||
bool matchAttrs;
|
||||
Formals * formals;
|
||||
Expr * body;
|
||||
ExprLambda(const Pos & pos, const Symbol & arg, bool matchAttrs, Formals * formals, Expr * body)
|
||||
: pos(pos), arg(arg), matchAttrs(matchAttrs), formals(formals), body(body)
|
||||
{
|
||||
if (!arg.empty() && formals && formals->argNames.find(arg) != formals->argNames.end())
|
||||
throw ParseError(format("duplicate formal function argument `%1%' at %2%")
|
||||
% arg % pos);
|
||||
};
|
||||
COMMON_METHODS
|
||||
};
|
||||
|
||||
struct ExprLet : Expr
|
||||
{
|
||||
ExprAttrs * attrs;
|
||||
Expr * body;
|
||||
ExprLet(ExprAttrs * attrs, Expr * body) : attrs(attrs), body(body) { };
|
||||
COMMON_METHODS
|
||||
};
|
||||
|
||||
struct ExprWith : Expr
|
||||
{
|
||||
Pos pos;
|
||||
Expr * attrs, * body;
|
||||
unsigned int prevWith;
|
||||
ExprWith(const Pos & pos, Expr * attrs, Expr * body) : pos(pos), attrs(attrs), body(body) { };
|
||||
COMMON_METHODS
|
||||
};
|
||||
|
||||
struct ExprIf : Expr
|
||||
{
|
||||
Expr * cond, * then, * else_;
|
||||
ExprIf(Expr * cond, Expr * then, Expr * else_) : cond(cond), then(then), else_(else_) { };
|
||||
COMMON_METHODS
|
||||
};
|
||||
|
||||
struct ExprAssert : Expr
|
||||
{
|
||||
Pos pos;
|
||||
Expr * cond, * body;
|
||||
ExprAssert(const Pos & pos, Expr * cond, Expr * body) : pos(pos), cond(cond), body(body) { };
|
||||
COMMON_METHODS
|
||||
};
|
||||
|
||||
struct ExprOpNot : Expr
|
||||
{
|
||||
Expr * e;
|
||||
ExprOpNot(Expr * e) : e(e) { };
|
||||
COMMON_METHODS
|
||||
};
|
||||
|
||||
#define MakeBinOp(name, s) \
|
||||
struct Expr##name : Expr \
|
||||
{ \
|
||||
Expr * e1, * e2; \
|
||||
Expr##name(Expr * e1, Expr * e2) : e1(e1), e2(e2) { }; \
|
||||
void show(std::ostream & str) \
|
||||
{ \
|
||||
str << *e1 << " " s " " << *e2; \
|
||||
} \
|
||||
void bindVars(const StaticEnv & env) \
|
||||
{ \
|
||||
e1->bindVars(env); e2->bindVars(env); \
|
||||
} \
|
||||
void eval(EvalState & state, Env & env, Value & v); \
|
||||
};
|
||||
|
||||
MakeBinOp(App, "")
|
||||
MakeBinOp(OpEq, "==")
|
||||
MakeBinOp(OpNEq, "!=")
|
||||
MakeBinOp(OpAnd, "&&")
|
||||
MakeBinOp(OpOr, "||")
|
||||
MakeBinOp(OpImpl, "->")
|
||||
MakeBinOp(OpUpdate, "//")
|
||||
MakeBinOp(OpConcatLists, "++")
|
||||
|
||||
struct ExprConcatStrings : Expr
|
||||
{
|
||||
vector<Expr *> * es;
|
||||
ExprConcatStrings(vector<Expr *> * es) : es(es) { };
|
||||
COMMON_METHODS
|
||||
};
|
||||
ATerm bottomupRewrite(TermFun & f, ATerm e);
|
||||
|
||||
|
||||
/* Query all attributes in an attribute set expression. The
|
||||
expression must be in normal form. */
|
||||
void queryAllAttrs(Expr e, ATermMap & attrs, bool withPos = false);
|
||||
|
||||
/* Query a specific attribute from an attribute set expression. The
|
||||
expression must be in normal form. */
|
||||
Expr queryAttr(Expr e, const string & name);
|
||||
Expr queryAttr(Expr e, const string & name, ATerm & pos);
|
||||
|
||||
/* Create an attribute set expression from an Attrs value. */
|
||||
Expr makeAttrs(const ATermMap & attrs);
|
||||
/* Static environments are used to map variable names onto (level,
|
||||
displacement) pairs used to obtain the value of the variable at
|
||||
runtime. */
|
||||
struct StaticEnv
|
||||
{
|
||||
bool isWith;
|
||||
const StaticEnv * up;
|
||||
typedef std::map<Symbol, unsigned int> Vars;
|
||||
Vars vars;
|
||||
StaticEnv(bool isWith, const StaticEnv * up) : isWith(isWith), up(up) { };
|
||||
};
|
||||
|
||||
|
||||
/* Perform a set of substitutions on an expression. */
|
||||
Expr substitute(const Substitution & subs, Expr e);
|
||||
|
||||
|
||||
/* Check whether all variables are defined in the given expression.
|
||||
Throw an exception if this isn't the case. */
|
||||
void checkVarDefs(const ATermMap & def, Expr e);
|
||||
|
||||
|
||||
/* Canonicalise a Nix expression by sorting attributes and removing
|
||||
location information. */
|
||||
Expr canonicaliseExpr(Expr e);
|
||||
|
||||
|
||||
/* Create an expression representing a boolean. */
|
||||
Expr makeBool(bool b);
|
||||
|
||||
|
||||
/* Manipulation of Str() nodes. Note: matchStr() does not clear
|
||||
context! */
|
||||
bool matchStr(Expr e, string & s, PathSet & context);
|
||||
|
||||
Expr makeStr(const string & s, const PathSet & context = PathSet());
|
||||
|
||||
|
||||
/* Showing types, values. */
|
||||
string showType(Expr e);
|
||||
|
||||
string showValue(Expr e);
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -8,12 +8,11 @@ namespace nix {
|
||||
|
||||
|
||||
/* Parse a Nix expression from the specified file. If `path' refers
|
||||
to a directory, the "/default.nix" is appended. */
|
||||
Expr parseExprFromFile(EvalState & state, Path path);
|
||||
to a directory, then "/default.nix" is appended. */
|
||||
Expr * parseExprFromFile(EvalState & state, Path path);
|
||||
|
||||
/* Parse a Nix expression from the specified string. */
|
||||
Expr parseExprFromString(EvalState & state, const string & s,
|
||||
const Path & basePath);
|
||||
Expr * parseExprFromString(EvalState & state, const string & s, const Path & basePath);
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -7,70 +7,126 @@
|
||||
%parse-param { yyscan_t scanner }
|
||||
%parse-param { ParseData * data }
|
||||
%lex-param { yyscan_t scanner }
|
||||
%lex-param { ParseData * data }
|
||||
|
||||
%code requires {
|
||||
|
||||
#ifndef BISON_HEADER
|
||||
#define BISON_HEADER
|
||||
|
||||
#include "util.hh"
|
||||
|
||||
#include "nixexpr.hh"
|
||||
|
||||
namespace nix {
|
||||
|
||||
struct ParseData
|
||||
{
|
||||
SymbolTable & symbols;
|
||||
Expr * result;
|
||||
Path basePath;
|
||||
Path path;
|
||||
string error;
|
||||
Symbol sLetBody;
|
||||
ParseData(SymbolTable & symbols)
|
||||
: symbols(symbols)
|
||||
, sLetBody(symbols.create("<let-body>"))
|
||||
{ };
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#define YY_DECL int yylex \
|
||||
(YYSTYPE * yylval_param, YYLTYPE * yylloc_param, yyscan_t yyscanner, nix::ParseData * data)
|
||||
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
%{
|
||||
/* Newer versions of Bison copy the declarations below to
|
||||
parser-tab.hh, which sucks bigtime since lexer.l doesn't want that
|
||||
stuff. So allow it to be excluded. */
|
||||
#ifndef BISON_HEADER_HACK
|
||||
#define BISON_HEADER_HACK
|
||||
|
||||
|
||||
#include "parser-tab.hh"
|
||||
#include "lexer-tab.hh"
|
||||
#define YYSTYPE YYSTYPE // workaround a bug in Bison 2.4
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "aterm.hh"
|
||||
#include "util.hh"
|
||||
|
||||
#include "parser-tab.hh"
|
||||
#include "lexer-tab.hh"
|
||||
|
||||
#include "nixexpr.hh"
|
||||
#include "nixexpr-ast.hh"
|
||||
|
||||
YY_DECL;
|
||||
|
||||
using namespace nix;
|
||||
|
||||
|
||||
namespace nix {
|
||||
|
||||
|
||||
struct ParseData
|
||||
{
|
||||
Expr result;
|
||||
Path basePath;
|
||||
Path path;
|
||||
string error;
|
||||
};
|
||||
|
||||
|
||||
static Expr fixAttrs(int recursive, ATermList as)
|
||||
static string showAttrPath(const vector<Symbol> & attrPath)
|
||||
{
|
||||
ATermList bs = ATempty, cs = ATempty;
|
||||
ATermList * is = recursive ? &cs : &bs;
|
||||
for (ATermIterator i(as); i; ++i) {
|
||||
ATermList names;
|
||||
Expr src;
|
||||
ATerm pos;
|
||||
if (matchInherit(*i, src, names, pos)) {
|
||||
bool fromScope = matchScope(src);
|
||||
for (ATermIterator j(names); j; ++j) {
|
||||
Expr rhs = fromScope ? makeVar(*j) : makeSelect(src, *j);
|
||||
*is = ATinsert(*is, makeBind(*j, rhs, pos));
|
||||
}
|
||||
} else bs = ATinsert(bs, *i);
|
||||
string s;
|
||||
foreach (vector<Symbol>::const_iterator, i, attrPath) {
|
||||
if (!s.empty()) s += '.';
|
||||
s += *i;
|
||||
}
|
||||
if (recursive)
|
||||
return makeRec(bs, cs);
|
||||
else
|
||||
return makeAttrs(bs);
|
||||
return s;
|
||||
}
|
||||
|
||||
|
||||
static Expr stripIndentation(ATermList es)
|
||||
static void dupAttr(const vector<Symbol> & attrPath, const Pos & pos, const Pos & prevPos)
|
||||
{
|
||||
if (es == ATempty) return makeStr("");
|
||||
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)
|
||||
{
|
||||
vector<Symbol> attrPath; attrPath.push_back(attr);
|
||||
throw ParseError(format("attribute `%1%' at %2% already defined at %3%")
|
||||
% showAttrPath(attrPath) % pos % prevPos);
|
||||
}
|
||||
|
||||
|
||||
static void addAttr(ExprAttrs * attrs, const vector<Symbol> & attrPath,
|
||||
Expr * e, const Pos & pos)
|
||||
{
|
||||
unsigned int n = 0;
|
||||
foreach (vector<Symbol>::const_iterator, i, attrPath) {
|
||||
n++;
|
||||
ExprAttrs::AttrDefs::iterator j = attrs->attrs.find(*i);
|
||||
if (j != attrs->attrs.end()) {
|
||||
if (!j->second.inherited) {
|
||||
ExprAttrs * attrs2 = dynamic_cast<ExprAttrs *>(j->second.e);
|
||||
if (!attrs2 || n == attrPath.size()) dupAttr(attrPath, pos, j->second.pos);
|
||||
attrs = attrs2;
|
||||
} else
|
||||
dupAttr(attrPath, pos, j->second.pos);
|
||||
} else {
|
||||
if (n == attrPath.size())
|
||||
attrs->attrs[*i] = ExprAttrs::AttrDef(e, pos);
|
||||
else {
|
||||
ExprAttrs * nested = new ExprAttrs;
|
||||
attrs->attrs[*i] = ExprAttrs::AttrDef(nested, pos);
|
||||
attrs = nested;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void addFormal(const Pos & pos, Formals * formals, const Formal & formal)
|
||||
{
|
||||
if (formals->argNames.find(formal.name) != formals->argNames.end())
|
||||
throw ParseError(format("duplicate formal function argument `%1%' at %2%")
|
||||
% formal.name % pos);
|
||||
formals->formals.push_front(formal);
|
||||
formals->argNames.insert(formal.name);
|
||||
}
|
||||
|
||||
|
||||
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
|
||||
@@ -78,9 +134,9 @@ static Expr stripIndentation(ATermList es)
|
||||
bool atStartOfLine = true; /* = seen only whitespace in the current line */
|
||||
unsigned int minIndent = 1000000;
|
||||
unsigned int curIndent = 0;
|
||||
ATerm e;
|
||||
for (ATermIterator i(es); i; ++i) {
|
||||
if (!matchIndStr(*i, e)) {
|
||||
foreach (vector<Expr *>::iterator, i, es) {
|
||||
ExprIndStr * e = dynamic_cast<ExprIndStr *>(*i);
|
||||
if (!e) {
|
||||
/* Anti-quotations end the current start-of-line whitespace. */
|
||||
if (atStartOfLine) {
|
||||
atStartOfLine = false;
|
||||
@@ -88,12 +144,11 @@ static Expr stripIndentation(ATermList es)
|
||||
}
|
||||
continue;
|
||||
}
|
||||
string s = aterm2String(e);
|
||||
for (unsigned int j = 0; j < s.size(); ++j) {
|
||||
for (unsigned int j = 0; j < e->s.size(); ++j) {
|
||||
if (atStartOfLine) {
|
||||
if (s[j] == ' ')
|
||||
if (e->s[j] == ' ')
|
||||
curIndent++;
|
||||
else if (s[j] == '\n') {
|
||||
else if (e->s[j] == '\n') {
|
||||
/* Empty line, doesn't influence minimum
|
||||
indentation. */
|
||||
curIndent = 0;
|
||||
@@ -101,7 +156,7 @@ static Expr stripIndentation(ATermList es)
|
||||
atStartOfLine = false;
|
||||
if (curIndent < minIndent) minIndent = curIndent;
|
||||
}
|
||||
} else if (s[j] == '\n') {
|
||||
} else if (e->s[j] == '\n') {
|
||||
atStartOfLine = true;
|
||||
curIndent = 0;
|
||||
}
|
||||
@@ -109,52 +164,52 @@ static Expr stripIndentation(ATermList es)
|
||||
}
|
||||
|
||||
/* Strip spaces from each line. */
|
||||
ATermList es2 = ATempty;
|
||||
vector<Expr *> * es2 = new vector<Expr *>;
|
||||
atStartOfLine = true;
|
||||
unsigned int curDropped = 0;
|
||||
unsigned int n = ATgetLength(es);
|
||||
for (ATermIterator i(es); i; ++i, --n) {
|
||||
if (!matchIndStr(*i, e)) {
|
||||
unsigned int n = es.size();
|
||||
for (vector<Expr *>::iterator i = es.begin(); i != es.end(); ++i, --n) {
|
||||
ExprIndStr * e = dynamic_cast<ExprIndStr *>(*i);
|
||||
if (!e) {
|
||||
atStartOfLine = false;
|
||||
curDropped = 0;
|
||||
es2 = ATinsert(es2, *i);
|
||||
es2->push_back(*i);
|
||||
continue;
|
||||
}
|
||||
|
||||
string s = aterm2String(e);
|
||||
string s2;
|
||||
for (unsigned int j = 0; j < s.size(); ++j) {
|
||||
for (unsigned int j = 0; j < e->s.size(); ++j) {
|
||||
if (atStartOfLine) {
|
||||
if (s[j] == ' ') {
|
||||
if (e->s[j] == ' ') {
|
||||
if (curDropped++ >= minIndent)
|
||||
s2 += s[j];
|
||||
s2 += e->s[j];
|
||||
}
|
||||
else if (s[j] == '\n') {
|
||||
else if (e->s[j] == '\n') {
|
||||
curDropped = 0;
|
||||
s2 += s[j];
|
||||
s2 += e->s[j];
|
||||
} else {
|
||||
atStartOfLine = false;
|
||||
curDropped = 0;
|
||||
s2 += s[j];
|
||||
s2 += e->s[j];
|
||||
}
|
||||
} else {
|
||||
s2 += s[j];
|
||||
if (s[j] == '\n') atStartOfLine = true;
|
||||
s2 += e->s[j];
|
||||
if (e->s[j] == '\n') atStartOfLine = true;
|
||||
}
|
||||
}
|
||||
|
||||
/* Remove the last line if it is empty and consists only of
|
||||
spaces. */
|
||||
if (n == 1) {
|
||||
unsigned int p = s2.find_last_of('\n');
|
||||
string::size_type p = s2.find_last_of('\n');
|
||||
if (p != string::npos && s2.find_first_not_of(' ', p + 1) == string::npos)
|
||||
s2 = string(s2, 0, p + 1);
|
||||
}
|
||||
|
||||
es2 = ATinsert(es2, makeStr(s2));
|
||||
|
||||
es2->push_back(new ExprString(symbols.create(s2)));
|
||||
}
|
||||
|
||||
return makeConcatStrings(ATreverse(es2));
|
||||
return new ExprConcatStrings(es2);
|
||||
}
|
||||
|
||||
|
||||
@@ -162,13 +217,12 @@ void backToString(yyscan_t scanner);
|
||||
void backToIndString(yyscan_t scanner);
|
||||
|
||||
|
||||
static Pos makeCurPos(YYLTYPE * loc, ParseData * data)
|
||||
static Pos makeCurPos(const YYLTYPE & loc, ParseData * data)
|
||||
{
|
||||
return makePos(toATerm(data->path),
|
||||
loc->first_line, loc->first_column);
|
||||
return Pos(data->path, loc.first_line, loc.first_column);
|
||||
}
|
||||
|
||||
#define CUR_POS makeCurPos(yylocp, data)
|
||||
#define CUR_POS makeCurPos(*yylocp, data)
|
||||
|
||||
|
||||
}
|
||||
@@ -176,47 +230,44 @@ static Pos makeCurPos(YYLTYPE * loc, ParseData * data)
|
||||
|
||||
void yyerror(YYLTYPE * loc, yyscan_t scanner, ParseData * data, const char * error)
|
||||
{
|
||||
data->error = (format("%1%, at `%2%':%3%:%4%")
|
||||
% error % data->path % loc->first_line % loc->first_column).str();
|
||||
data->error = (format("%1%, at %2%")
|
||||
% error % makeCurPos(*loc, data)).str();
|
||||
}
|
||||
|
||||
|
||||
/* Make sure that the parse stack is scanned by the ATerm garbage
|
||||
collector. */
|
||||
static void * mallocAndProtect(size_t size)
|
||||
{
|
||||
void * p = malloc(size);
|
||||
if (p) ATprotectMemory(p, size);
|
||||
return p;
|
||||
}
|
||||
|
||||
static void freeAndUnprotect(void * p)
|
||||
{
|
||||
ATunprotectMemory(p);
|
||||
free(p);
|
||||
}
|
||||
|
||||
#define YYMALLOC mallocAndProtect
|
||||
#define YYFREE freeAndUnprotect
|
||||
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
%}
|
||||
|
||||
%union {
|
||||
ATerm t;
|
||||
ATermList ts;
|
||||
nix::Expr * e;
|
||||
nix::ExprList * list;
|
||||
nix::ExprAttrs * attrs;
|
||||
nix::Formals * formals;
|
||||
nix::Formal * formal;
|
||||
int n;
|
||||
char * id; // !!! -> Symbol
|
||||
char * path;
|
||||
char * uri;
|
||||
std::vector<nix::Symbol> * ids;
|
||||
std::vector<nix::Expr *> * string_parts;
|
||||
}
|
||||
|
||||
%type <t> start expr expr_function expr_if expr_op
|
||||
%type <t> expr_app expr_select expr_simple bind inheritsrc formal
|
||||
%type <ts> binds ids expr_list formals string_parts ind_string_parts
|
||||
%token <t> ID INT STR IND_STR PATH URI
|
||||
%type <e> start expr expr_function expr_if expr_op
|
||||
%type <e> expr_app expr_select expr_simple
|
||||
%type <list> expr_list
|
||||
%type <attrs> binds
|
||||
%type <formals> formals
|
||||
%type <formal> formal
|
||||
%type <ids> ids attrpath
|
||||
%type <string_parts> string_parts ind_string_parts
|
||||
%token <id> ID ATTRPATH
|
||||
%token <e> STR IND_STR
|
||||
%token <n> INT
|
||||
%token <path> PATH
|
||||
%token <uri> URI
|
||||
%token IF THEN ELSE ASSERT WITH LET IN REC INHERIT EQ NEQ AND OR IMPL
|
||||
%token DOLLAR_CURLY /* == ${ */
|
||||
%token IND_STRING_OPEN IND_STRING_CLOSE
|
||||
%token ELLIPSIS
|
||||
|
||||
%nonassoc IMPL
|
||||
%left OR
|
||||
@@ -236,206 +287,170 @@ start: expr { data->result = $1; };
|
||||
expr: expr_function;
|
||||
|
||||
expr_function
|
||||
: '{' formals '}' ':' expr_function
|
||||
{ $$ = makeFunction($2, $5, CUR_POS); }
|
||||
| ID ':' expr_function
|
||||
{ $$ = makeFunction1($1, $3, CUR_POS); }
|
||||
: ID ':' expr_function
|
||||
{ $$ = new ExprLambda(CUR_POS, data->symbols.create($1), false, 0, $3); }
|
||||
| '{' formals '}' ':' expr_function
|
||||
{ $$ = new ExprLambda(CUR_POS, data->symbols.create(""), true, $2, $5); }
|
||||
| '{' formals '}' '@' ID ':' expr_function
|
||||
{ $$ = new ExprLambda(CUR_POS, data->symbols.create($5), true, $2, $7); }
|
||||
| ID '@' '{' formals '}' ':' expr_function
|
||||
{ $$ = new ExprLambda(CUR_POS, data->symbols.create($1), true, $4, $7); }
|
||||
| ASSERT expr ';' expr_function
|
||||
{ $$ = makeAssert($2, $4, CUR_POS); }
|
||||
{ $$ = new ExprAssert(CUR_POS, $2, $4); }
|
||||
| WITH expr ';' expr_function
|
||||
{ $$ = makeWith($2, $4, CUR_POS); }
|
||||
{ $$ = new ExprWith(CUR_POS, $2, $4); }
|
||||
| LET binds IN expr_function
|
||||
{ $$ = makeSelect(fixAttrs(1, ATinsert($2, makeBind(toATerm("<let-body>"), $4, CUR_POS))), toATerm("<let-body>")); }
|
||||
{ $$ = new ExprLet($2, $4); }
|
||||
| expr_if
|
||||
;
|
||||
|
||||
expr_if
|
||||
: IF expr THEN expr ELSE expr
|
||||
{ $$ = makeIf($2, $4, $6); }
|
||||
: IF expr THEN expr ELSE expr { $$ = new ExprIf($2, $4, $6); }
|
||||
| expr_op
|
||||
;
|
||||
|
||||
expr_op
|
||||
: '!' expr_op %prec NEG { $$ = makeOpNot($2); }
|
||||
| expr_op EQ expr_op { $$ = makeOpEq($1, $3); }
|
||||
| expr_op NEQ expr_op { $$ = makeOpNEq($1, $3); }
|
||||
| expr_op AND expr_op { $$ = makeOpAnd($1, $3); }
|
||||
| expr_op OR expr_op { $$ = makeOpOr($1, $3); }
|
||||
| expr_op IMPL expr_op { $$ = makeOpImpl($1, $3); }
|
||||
| expr_op UPDATE expr_op { $$ = makeOpUpdate($1, $3); }
|
||||
| expr_op '~' expr_op { $$ = makeSubPath($1, $3); }
|
||||
| expr_op '?' ID { $$ = makeOpHasAttr($1, $3); }
|
||||
| expr_op '+' expr_op { $$ = makeOpPlus($1, $3); }
|
||||
| expr_op CONCAT expr_op { $$ = makeOpConcat($1, $3); }
|
||||
: '!' expr_op %prec NEG { $$ = new ExprOpNot($2); }
|
||||
| expr_op EQ expr_op { $$ = new ExprOpEq($1, $3); }
|
||||
| expr_op NEQ expr_op { $$ = new ExprOpNEq($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); }
|
||||
| expr_op UPDATE expr_op { $$ = new ExprOpUpdate($1, $3); }
|
||||
| expr_op '?' ID { $$ = new ExprOpHasAttr($1, data->symbols.create($3)); }
|
||||
| expr_op '+' expr_op
|
||||
{ vector<Expr *> * l = new vector<Expr *>;
|
||||
l->push_back($1);
|
||||
l->push_back($3);
|
||||
$$ = new ExprConcatStrings(l);
|
||||
}
|
||||
| expr_op CONCAT expr_op { $$ = new ExprOpConcatLists($1, $3); }
|
||||
| expr_app
|
||||
;
|
||||
|
||||
expr_app
|
||||
: expr_app expr_select
|
||||
{ $$ = makeCall($1, $2); }
|
||||
{ $$ = new ExprApp($1, $2); }
|
||||
| expr_select { $$ = $1; }
|
||||
;
|
||||
|
||||
expr_select
|
||||
: expr_select '.' ID
|
||||
{ $$ = makeSelect($1, $3); }
|
||||
{ $$ = new ExprSelect($1, data->symbols.create($3)); }
|
||||
| expr_simple { $$ = $1; }
|
||||
;
|
||||
|
||||
expr_simple
|
||||
: ID { $$ = makeVar($1); }
|
||||
| INT { $$ = makeInt(ATgetInt((ATermInt) $1)); }
|
||||
: ID { $$ = new ExprVar(data->symbols.create($1)); }
|
||||
| INT { $$ = new ExprInt($1); }
|
||||
| '"' string_parts '"' {
|
||||
/* For efficiency, and to simplify parse trees a bit. */
|
||||
if ($2 == ATempty) $$ = makeStr(toATerm(""), ATempty);
|
||||
else if (ATgetNext($2) == ATempty) $$ = ATgetFirst($2);
|
||||
else $$ = makeConcatStrings(ATreverse($2));
|
||||
if ($2->empty()) $$ = new ExprString(data->symbols.create(""));
|
||||
else if ($2->size() == 1) $$ = $2->front();
|
||||
else $$ = new ExprConcatStrings($2);
|
||||
}
|
||||
| IND_STRING_OPEN ind_string_parts IND_STRING_CLOSE {
|
||||
$$ = stripIndentation(ATreverse($2));
|
||||
$$ = stripIndentation(data->symbols, *$2);
|
||||
}
|
||||
| PATH { $$ = makePath(toATerm(absPath(aterm2String($1), data->basePath))); }
|
||||
| URI { $$ = makeStr($1, ATempty); }
|
||||
| PATH { $$ = new ExprPath(absPath($1, data->basePath)); }
|
||||
| URI { $$ = new ExprString(data->symbols.create($1)); }
|
||||
| '(' expr ')' { $$ = $2; }
|
||||
/* Let expressions `let {..., body = ...}' are just desugared
|
||||
into `(rec {..., body = ...}).body'. */
|
||||
| LET '{' binds '}'
|
||||
{ $$ = makeSelect(fixAttrs(1, $3), toATerm("body")); }
|
||||
{ $3->recursive = true; $$ = new ExprSelect($3, data->symbols.create("body")); }
|
||||
| REC '{' binds '}'
|
||||
{ $$ = fixAttrs(1, $3); }
|
||||
{ $3->recursive = true; $$ = $3; }
|
||||
| '{' binds '}'
|
||||
{ $$ = fixAttrs(0, $2); }
|
||||
| '[' expr_list ']' { $$ = makeList($2); }
|
||||
{ $$ = $2; }
|
||||
| '[' expr_list ']' { $$ = $2; }
|
||||
;
|
||||
|
||||
string_parts
|
||||
: string_parts STR { $$ = ATinsert($1, $2); }
|
||||
| string_parts DOLLAR_CURLY expr '}' { backToString(scanner); $$ = ATinsert($1, $3); }
|
||||
| { $$ = ATempty; }
|
||||
: string_parts STR { $$ = $1; $1->push_back($2); }
|
||||
| string_parts DOLLAR_CURLY expr '}' { backToString(scanner); $$ = $1; $1->push_back($3); }
|
||||
| { $$ = new vector<Expr *>; }
|
||||
;
|
||||
|
||||
ind_string_parts
|
||||
: ind_string_parts IND_STR { $$ = ATinsert($1, $2); }
|
||||
| ind_string_parts DOLLAR_CURLY expr '}' { backToIndString(scanner); $$ = ATinsert($1, $3); }
|
||||
| { $$ = ATempty; }
|
||||
: ind_string_parts IND_STR { $$ = $1; $1->push_back($2); }
|
||||
| ind_string_parts DOLLAR_CURLY expr '}' { backToIndString(scanner); $$ = $1; $1->push_back($3); }
|
||||
| { $$ = new vector<Expr *>; }
|
||||
;
|
||||
|
||||
binds
|
||||
: binds bind { $$ = ATinsert($1, $2); }
|
||||
| { $$ = ATempty; }
|
||||
: binds attrpath '=' expr ';' { $$ = $1; addAttr($$, *$2, $4, makeCurPos(@2, data)); }
|
||||
| binds INHERIT ids ';'
|
||||
{ $$ = $1;
|
||||
foreach (vector<Symbol>::iterator, i, *$3) {
|
||||
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);
|
||||
}
|
||||
}
|
||||
| binds INHERIT '(' expr ')' ids ';'
|
||||
{ $$ = $1;
|
||||
/* !!! Should ensure sharing of the expression in $4. */
|
||||
foreach (vector<Symbol>::iterator, i, *$6) {
|
||||
if ($$->attrs.find(*i) != $$->attrs.end())
|
||||
dupAttr(*i, makeCurPos(@6, data), $$->attrs[*i].pos);
|
||||
$$->attrs[*i] = ExprAttrs::AttrDef(new ExprSelect($4, *i), makeCurPos(@6, data));
|
||||
}}
|
||||
|
||||
| { $$ = new ExprAttrs; }
|
||||
;
|
||||
|
||||
bind
|
||||
: ID '=' expr ';'
|
||||
{ $$ = makeBind($1, $3, CUR_POS); }
|
||||
| INHERIT inheritsrc ids ';'
|
||||
{ $$ = makeInherit($2, $3, CUR_POS); }
|
||||
ids
|
||||
: ids ID { $$ = $1; $1->push_back(data->symbols.create($2)); /* !!! dangerous */ }
|
||||
| { $$ = new vector<Symbol>; }
|
||||
;
|
||||
|
||||
inheritsrc
|
||||
: '(' expr ')' { $$ = $2; }
|
||||
| { $$ = makeScope(); }
|
||||
attrpath
|
||||
: attrpath '.' ID { $$ = $1; $1->push_back(data->symbols.create($3)); }
|
||||
| ID { $$ = new vector<Symbol>; $$->push_back(data->symbols.create($1)); }
|
||||
;
|
||||
|
||||
ids: ids ID { $$ = ATinsert($1, $2); } | { $$ = ATempty; };
|
||||
|
||||
expr_list
|
||||
: expr_select expr_list { $$ = ATinsert($2, $1); }
|
||||
/* yes, this is right-recursive, but it doesn't matter since
|
||||
otherwise we would need ATreverse which requires unbounded
|
||||
stack space */
|
||||
| { $$ = ATempty; }
|
||||
: expr_list expr_select { $$ = $1; $1->elems.push_back($2); /* !!! dangerous */ }
|
||||
| { $$ = new ExprList; }
|
||||
;
|
||||
|
||||
formals
|
||||
: formal ',' formals { $$ = ATinsert($3, $1); } /* idem - right recursive */
|
||||
| formal { $$ = ATinsert(ATempty, $1); }
|
||||
| { $$ = ATempty; }
|
||||
: formal ',' formals
|
||||
{ $$ = $3; addFormal(CUR_POS, $$, *$1); }
|
||||
| formal
|
||||
{ $$ = new Formals; addFormal(CUR_POS, $$, *$1); $$->ellipsis = false; }
|
||||
|
|
||||
{ $$ = new Formals; $$->ellipsis = false; }
|
||||
| ELLIPSIS
|
||||
{ $$ = new Formals; $$->ellipsis = true; }
|
||||
;
|
||||
|
||||
formal
|
||||
: ID { $$ = makeFormal($1, makeUnrestrictedValues(), makeNoDefaultValue()); }
|
||||
| ID ':' '[' expr_list ']' { $$ = makeFormal($1, makeValidValues($4), makeNoDefaultValue()); }
|
||||
| ID '?' expr { $$ = makeFormal($1, makeUnrestrictedValues(), makeDefaultValue($3)); }
|
||||
: ID { $$ = new Formal(data->symbols.create($1), 0); }
|
||||
| ID '?' expr { $$ = new Formal(data->symbols.create($1), $3); }
|
||||
;
|
||||
|
||||
%%
|
||||
|
||||
|
||||
#include "eval.hh"
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <fcntl.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <eval.hh>
|
||||
|
||||
|
||||
namespace nix {
|
||||
|
||||
|
||||
static void checkAttrs(ATermMap & names, ATermList bnds)
|
||||
{
|
||||
for (ATermIterator i(bnds); i; ++i) {
|
||||
ATerm name;
|
||||
Expr e;
|
||||
ATerm pos;
|
||||
if (!matchBind(*i, name, e, pos)) abort(); /* can't happen */
|
||||
if (names.get(name))
|
||||
throw EvalError(format("duplicate attribute `%1%' at %2%")
|
||||
% aterm2String(name) % showPos(pos));
|
||||
names.set(name, name);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void checkAttrSets(ATerm e)
|
||||
{
|
||||
ATermList formals;
|
||||
ATerm body, pos;
|
||||
if (matchFunction(e, formals, body, pos)) {
|
||||
ATermMap names(ATgetLength(formals));
|
||||
for (ATermIterator i(formals); i; ++i) {
|
||||
ATerm name;
|
||||
ATerm d1, d2;
|
||||
if (!matchFormal(*i, name, d1, d2)) abort();
|
||||
if (names.get(name))
|
||||
throw EvalError(format("duplicate formal function argument `%1%' at %2%")
|
||||
% aterm2String(name) % showPos(pos));
|
||||
names.set(name, name);
|
||||
}
|
||||
}
|
||||
|
||||
ATermList bnds;
|
||||
if (matchAttrs(e, bnds)) {
|
||||
ATermMap names(ATgetLength(bnds));
|
||||
checkAttrs(names, bnds);
|
||||
}
|
||||
|
||||
ATermList rbnds, nrbnds;
|
||||
if (matchRec(e, rbnds, nrbnds)) {
|
||||
ATermMap names(ATgetLength(rbnds) + ATgetLength(nrbnds));
|
||||
checkAttrs(names, rbnds);
|
||||
checkAttrs(names, nrbnds);
|
||||
}
|
||||
|
||||
if (ATgetType(e) == AT_APPL) {
|
||||
int arity = ATgetArity(ATgetAFun(e));
|
||||
for (int i = 0; i < arity; ++i)
|
||||
checkAttrSets(ATgetArgument(e, i));
|
||||
}
|
||||
|
||||
else if (ATgetType(e) == AT_LIST)
|
||||
for (ATermIterator i((ATermList) e); i; ++i)
|
||||
checkAttrSets(*i);
|
||||
}
|
||||
|
||||
|
||||
static Expr parse(EvalState & state,
|
||||
const char * text, const Path & path,
|
||||
const Path & basePath)
|
||||
static Expr * parse(EvalState & state, const char * text,
|
||||
const Path & path, const Path & basePath)
|
||||
{
|
||||
yyscan_t scanner;
|
||||
ParseData data;
|
||||
ParseData data(state.symbols);
|
||||
data.basePath = basePath;
|
||||
data.path = path;
|
||||
|
||||
@@ -444,30 +459,22 @@ static Expr parse(EvalState & state,
|
||||
int res = yyparse(scanner, &data);
|
||||
yylex_destroy(scanner);
|
||||
|
||||
if (res) throw EvalError(data.error);
|
||||
if (res) throw ParseError(data.error);
|
||||
|
||||
try {
|
||||
checkVarDefs(state.primOps, data.result);
|
||||
data.result->bindVars(state.staticBaseEnv);
|
||||
} catch (Error & e) {
|
||||
throw EvalError(format("%1%, in `%2%'") % e.msg() % path);
|
||||
throw ParseError(format("%1%, in `%2%'") % e.msg() % path);
|
||||
}
|
||||
|
||||
checkAttrSets(data.result);
|
||||
|
||||
return data.result;
|
||||
}
|
||||
|
||||
|
||||
Expr parseExprFromFile(EvalState & state, Path path)
|
||||
Expr * parseExprFromFile(EvalState & state, Path path)
|
||||
{
|
||||
assert(path[0] == '/');
|
||||
|
||||
#if 0
|
||||
/* Perhaps this is already an imploded parse tree? */
|
||||
Expr e = ATreadFromNamedFile(path.c_str());
|
||||
if (e) return e;
|
||||
#endif
|
||||
|
||||
/* If `path' is a symlink, follow it. This is so that relative
|
||||
path references work. */
|
||||
struct stat st;
|
||||
@@ -479,8 +486,6 @@ Expr parseExprFromFile(EvalState & state, Path path)
|
||||
}
|
||||
|
||||
/* If `path' refers to a directory, append `/default.nix'. */
|
||||
if (stat(path.c_str(), &st))
|
||||
throw SysError(format("getting status of `%1%'") % path);
|
||||
if (S_ISDIR(st.st_mode))
|
||||
path = canonPath(path + "/default.nix");
|
||||
|
||||
@@ -489,7 +494,7 @@ Expr parseExprFromFile(EvalState & state, Path path)
|
||||
}
|
||||
|
||||
|
||||
Expr parseExprFromString(EvalState & state,
|
||||
Expr * parseExprFromString(EvalState & state,
|
||||
const string & s, const Path & basePath)
|
||||
{
|
||||
return parse(state, s.c_str(), "(string)", basePath);
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user