Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8057a192e3 | ||
|
|
9fa1bee575 | ||
|
|
f45c731cd7 | ||
|
|
88936411bc | ||
|
|
0111ba98ea | ||
|
|
b008674e46 | ||
|
|
826dc0d07d | ||
|
|
97c6009c47 | ||
|
|
ca9c02dff1 | ||
|
|
5526a282b5 | ||
|
|
dadf7a5b46 | ||
|
|
3e067ac11c | ||
|
|
5ea138dc4b | ||
|
|
79a3ba7fa3 | ||
|
|
5f18cd2e84 | ||
|
|
52172607cf | ||
|
|
01a5ea9914 | ||
|
|
8add116acd | ||
|
|
d6143801fd | ||
|
|
9842077cb2 | ||
|
|
5e9c3da412 | ||
|
|
99ed558a93 | ||
|
|
1943b60ad8 | ||
|
|
96fbbbde55 | ||
|
|
d6fd6d8aff | ||
|
|
536c85ea49 | ||
|
|
66fa9e6a4d | ||
|
|
e42df686f3 | ||
|
|
92926be2fe | ||
|
|
b424d29d1b | ||
|
|
3007f57377 | ||
|
|
def5160b61 | ||
|
|
0a4e90395c | ||
|
|
f12492c66d | ||
|
|
aebea2e489 | ||
|
|
649bb60617 | ||
|
|
42d6f640c1 | ||
|
|
299141ecbd | ||
|
|
1b3a78a459 | ||
|
|
82248abd8f | ||
|
|
12f9129f60 | ||
|
|
b7629778ef | ||
|
|
68dcbb187e | ||
|
|
5ee7d8fbab | ||
|
|
2754a07ead | ||
|
|
06f62defe6 | ||
|
|
9c29a2ed35 | ||
|
|
e775d4d84f | ||
|
|
fbf0b2fa45 | ||
|
|
228ea7c2f9 | ||
|
|
cc511fd65b | ||
|
|
a9045c727f | ||
|
|
9fa12fc201 | ||
|
|
3ad53e43c8 | ||
|
|
772778c0ec | ||
|
|
e087bfef5f | ||
|
|
a6ce6d9e7c | ||
|
|
8cc19ed089 | ||
|
|
52edef3495 | ||
|
|
8d100dbef1 | ||
|
|
5c487761c4 | ||
|
|
3631dc6b2f | ||
|
|
aa61bc7472 | ||
|
|
0f96966a44 | ||
|
|
566afa1e9c | ||
|
|
444b03a36f | ||
|
|
d5a01d0f9d | ||
|
|
a7b4aaa2c3 | ||
|
|
3100b29fc5 | ||
|
|
094a08f839 | ||
|
|
4387d19359 | ||
|
|
24d5875514 | ||
|
|
b215b23e9e | ||
|
|
56d29dcd62 | ||
|
|
2d5e8e267b | ||
|
|
5ad89398d1 | ||
|
|
21c2d8d102 | ||
|
|
d62fc71b85 | ||
|
|
4bb4d5479a | ||
|
|
8eed07cda4 | ||
|
|
6c98e6a5de | ||
|
|
8b8ee53bc7 | ||
|
|
5943f41b8b | ||
|
|
08964d7328 | ||
|
|
69c88f5028 | ||
|
|
408a7bfac1 | ||
|
|
8d8d47abd2 | ||
|
|
46a369ad95 | ||
|
|
a3d6585c5a | ||
|
|
9de6bc5d05 | ||
|
|
bf3725da2a | ||
|
|
17dc306aa3 | ||
|
|
f794465ca8 | ||
|
|
8541d27fce | ||
|
|
3acc8adcad | ||
|
|
ea89df2b76 | ||
|
|
48c19c4633 | ||
|
|
198dbe7fa1 | ||
|
|
10dcee99ed | ||
|
|
4c9e3fa641 | ||
|
|
182e15b661 | ||
|
|
88164325fa | ||
|
|
f581ce0b0c | ||
|
|
91ef4d9a81 | ||
|
|
a9a8baaccb | ||
|
|
c31ebc50aa | ||
|
|
e28b683324 | ||
|
|
bbc107ef1e | ||
|
|
3a95e1a17c | ||
|
|
82951e5582 | ||
|
|
620e92e880 | ||
|
|
df27db712d | ||
|
|
d0fc615af6 | ||
|
|
4c34d384e6 | ||
|
|
a28b4445a4 | ||
|
|
2980d1fba9 | ||
|
|
dde6486eab | ||
|
|
c8daeba303 | ||
|
|
167e36a5c3 | ||
|
|
ac238d619c | ||
|
|
600daf972f | ||
|
|
e345182055 | ||
|
|
ecedd9c50c | ||
|
|
70f75be199 | ||
|
|
ad328bea15 | ||
|
|
bfaa5635de | ||
|
|
904f50412c | ||
|
|
90b8a34f82 | ||
|
|
d1de836938 | ||
|
|
f766e146f4 | ||
|
|
e35d6f78dc | ||
|
|
522ecab9b8 | ||
|
|
7586095504 | ||
|
|
a562d544d8 | ||
|
|
9c41c66c5b | ||
|
|
2bbc4a214e | ||
|
|
2e90a5a2a7 | ||
|
|
0a7084567f | ||
|
|
a807edfae8 | ||
|
|
a3f205b249 | ||
|
|
2001895f3d | ||
|
|
cf46f19444 | ||
|
|
8e3a7bd712 | ||
|
|
9958bd6992 | ||
|
|
e666e1156f | ||
|
|
f406288cc7 | ||
|
|
95c74eae26 | ||
|
|
f46612be96 | ||
|
|
d534f137f0 | ||
|
|
0f358ca5b6 | ||
|
|
cb6651e878 | ||
|
|
e464b0247d | ||
|
|
28bf183d2d | ||
|
|
c1f91570b3 | ||
|
|
20582e9ae3 | ||
|
|
b9c2b4d5b4 | ||
|
|
b9124a5c33 | ||
|
|
76e88871b2 | ||
|
|
00092b2d35 | ||
|
|
b674665765 | ||
|
|
09eb230900 | ||
|
|
983220bcd4 | ||
|
|
9fd9dedf12 | ||
|
|
1bda006b74 | ||
|
|
aac14222f5 | ||
|
|
54fe8e0773 | ||
|
|
47ae3ce2ca | ||
|
|
e03a8a1c92 | ||
|
|
8fbe96cb31 | ||
|
|
e6e495649c | ||
|
|
2923b55f9d | ||
|
|
31114ec3a5 | ||
|
|
c845c0ccad | ||
|
|
b14717ab90 | ||
|
|
6c4ac29917 | ||
|
|
e56f71edaf | ||
|
|
4fca02077c | ||
|
|
479e9172b3 | ||
|
|
4ba47205c6 | ||
|
|
6762424e24 | ||
|
|
d4c3b6327f | ||
|
|
1f7901ec3b | ||
|
|
5e2ffd0b8a | ||
|
|
8cf672091e | ||
|
|
9dbda2b3fe | ||
|
|
a2785b7391 | ||
|
|
295027f533 | ||
|
|
d3004c78d9 | ||
|
|
360056e174 | ||
|
|
f7b650d234 | ||
|
|
15e1b2c223 | ||
|
|
9e2fc6951c | ||
|
|
cfd968dd94 | ||
|
|
f3077fd88d | ||
|
|
cc8641815b | ||
|
|
2688fb73f1 | ||
|
|
8207359227 | ||
|
|
8b8fe6139e | ||
|
|
babe54bf97 | ||
|
|
e94806d030 | ||
|
|
a9e6752bbd | ||
|
|
4aa1e5c554 | ||
|
|
d950cfe70b | ||
|
|
56e30e161c | ||
|
|
f0eab0636b | ||
|
|
862c4c5ec5 | ||
|
|
767101824a | ||
|
|
e5c589d271 | ||
|
|
3e89ef597c | ||
|
|
9c2d63084b | ||
|
|
198d0338be | ||
|
|
b9e5b908ed | ||
|
|
4ccd48ce24 | ||
|
|
62f72eb9e1 | ||
|
|
e82767910c | ||
|
|
325d1cfebf | ||
|
|
d025142f52 | ||
|
|
b6c989b801 | ||
|
|
108e14bb18 | ||
|
|
6763084ae5 | ||
|
|
01d56c1eec | ||
|
|
967d066d8e | ||
|
|
1df702d347 | ||
|
|
9c2decaa19 | ||
|
|
5170c5691a | ||
|
|
79bba3782c | ||
|
|
ca94b38371 | ||
|
|
46f852cda0 | ||
|
|
afa7e01878 | ||
|
|
234ce610e0 | ||
|
|
7b10562370 | ||
|
|
8a25d787d7 | ||
|
|
c770a2422a | ||
|
|
4d1b64f118 | ||
|
|
eb7849e3a2 | ||
|
|
90d9c58d4d | ||
|
|
89a8207029 | ||
|
|
157170059d | ||
|
|
97421eb5ec | ||
|
|
d50d7a2874 | ||
|
|
9cd63d2244 | ||
|
|
f3eb29c653 | ||
|
|
9de6d10d11 | ||
|
|
ab42bf1dab | ||
|
|
d059bf48e4 | ||
|
|
f9613da180 | ||
|
|
6183cf2f19 | ||
|
|
f15083c10a | ||
|
|
66a3ac6a56 | ||
|
|
6ecf4f13f6 | ||
|
|
e6ab52cdd1 | ||
|
|
73acb8b836 | ||
|
|
fbf59d95f6 | ||
|
|
3a8f841612 | ||
|
|
b4ea83249b | ||
|
|
7f8e805c8e | ||
|
|
67c6f3eded | ||
|
|
50395b71a9 | ||
|
|
e3ce54ccee | ||
|
|
609586a16d | ||
|
|
7861260a5f | ||
|
|
7892ad15ab | ||
|
|
dbce685e91 | ||
|
|
8c79100839 | ||
|
|
3a4623afbb | ||
|
|
2605f4f4e6 | ||
|
|
477b0fbeca | ||
|
|
f5954e2d94 | ||
|
|
1ef2d5765b | ||
|
|
b1112bbef1 | ||
|
|
566a30c007 | ||
|
|
e98c029717 | ||
|
|
fd63c8bfcd | ||
|
|
0f65793f94 | ||
|
|
680ab6f83d | ||
|
|
6193105710 | ||
|
|
564fb7d9fa | ||
|
|
ed59bf7a18 | ||
|
|
6852289c46 | ||
|
|
1832ab71db | ||
|
|
aa115e22df | ||
|
|
fe241ece29 | ||
|
|
a6f348599c | ||
|
|
04559a0d45 | ||
|
|
e4d6bcb6cd | ||
|
|
f2bdc87595 | ||
|
|
15c15da482 | ||
|
|
d287b62b64 | ||
|
|
b74d92755d | ||
|
|
09a6321aeb | ||
|
|
58ef4d9a95 | ||
|
|
667d5f1936 | ||
|
|
eb3036da87 | ||
|
|
6586414bc7 | ||
|
|
5ee8944155 | ||
|
|
ae72be1b8b | ||
|
|
099125435f | ||
|
|
98a423b75a | ||
|
|
425cc612ad | ||
|
|
400e556b34 | ||
|
|
11800e6198 | ||
|
|
cd94665f38 | ||
|
|
ae60643c15 | ||
|
|
89380c03e9 | ||
|
|
2a8e5c8b11 | ||
|
|
d694c599e2 | ||
|
|
8319b1ab9f | ||
|
|
cf49472d60 | ||
|
|
6ec7460af1 | ||
|
|
6b1e671ac6 | ||
|
|
440adfbdd1 | ||
|
|
f4190c38ba | ||
|
|
000132cbd1 | ||
|
|
37f7098464 | ||
|
|
91b8814f0d | ||
|
|
4911a10a4e | ||
|
|
49cd7387ad |
+6
-11
@@ -35,8 +35,11 @@ Makefile.in
|
||||
|
||||
# /doc/manual/
|
||||
/doc/manual/manual.html
|
||||
/doc/manual/manual.xmli
|
||||
/doc/manual/manual.pdf
|
||||
/doc/manual/manual.is-valid
|
||||
/doc/manual/*.1
|
||||
/doc/manual/*.5
|
||||
/doc/manual/*.8
|
||||
/doc/manual/images
|
||||
/doc/manual/version.txt
|
||||
@@ -60,14 +63,12 @@ Makefile.in
|
||||
/scripts/GeneratePatches.pm
|
||||
/scripts/download-using-manifests.pl
|
||||
/scripts/copy-from-other-stores.pl
|
||||
/scripts/download-from-binary-cache.pl
|
||||
/scripts/find-runtime-roots.pl
|
||||
/scripts/build-remote.pl
|
||||
/scripts/nix-reduce-build
|
||||
/scripts/nix-http-export.cgi
|
||||
|
||||
# /src/bin2c/
|
||||
/src/bin2c/bin2c
|
||||
|
||||
# /src/bsdiff-4.3/
|
||||
/src/bsdiff-4.3/bsdiff
|
||||
/src/bsdiff-4.3/bspatch
|
||||
@@ -85,15 +86,12 @@ Makefile.in
|
||||
|
||||
# /src/nix-env/
|
||||
/src/nix-env/nix-env
|
||||
/src/nix-env/help.txt.hh
|
||||
|
||||
# /src/nix-hash/
|
||||
/src/nix-hash/nix-hash
|
||||
/src/nix-hash/help.txt.hh
|
||||
|
||||
# /src/nix-instantiate/
|
||||
/src/nix-instantiate/nix-instantiate
|
||||
/src/nix-instantiate/help.txt.hh
|
||||
|
||||
# /src/nix-log2xml/
|
||||
/src/nix-log2xml/nix-log2xml
|
||||
@@ -104,15 +102,12 @@ Makefile.in
|
||||
|
||||
# /src/nix-setuid-helper/
|
||||
/src/nix-setuid-helper/nix-setuid-helper
|
||||
/src/nix-setuid-helper/help.txt.hh
|
||||
|
||||
# /src/nix-store/
|
||||
/src/nix-store/help.txt.hh
|
||||
/src/nix-store/nix-store
|
||||
|
||||
# /src/nix-worker/
|
||||
/src/nix-worker/nix-worker
|
||||
/src/nix-worker/help.txt.hh
|
||||
# /src/nix-daemon/
|
||||
/src/nix-daemon/nix-daemon
|
||||
|
||||
# /tests/
|
||||
/tests/test-tmp
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
SUBDIRS = src perl scripts corepkgs doc misc tests
|
||||
EXTRA_DIST = substitute.mk nix.spec nix.spec.in bootstrap.sh \
|
||||
NEWS version misc/systemd/nix-worker.service
|
||||
NEWS version misc/systemd/nix-daemon.service
|
||||
|
||||
pkginclude_HEADERS = config.h
|
||||
|
||||
|
||||
+51
-8
@@ -25,12 +25,12 @@ AC_ARG_WITH(system, AC_HELP_STRING([--with-system=SYSTEM],
|
||||
|
||||
case "$host_os" in
|
||||
linux-gnu*)
|
||||
# For backward compatibility, strip the `-gnu' part.
|
||||
system="$machine_name-linux";;
|
||||
# For backward compatibility, strip the `-gnu' part.
|
||||
system="$machine_name-linux";;
|
||||
*)
|
||||
# Strip the version number from names such as `gnu0.3',
|
||||
# `darwin10.2.0', etc.
|
||||
system="$machine_name-`echo $host_os | "$SED" -e's/@<:@0-9.@:>@*$//g'`";;
|
||||
system="$machine_name-`echo $host_os | "$SED" -e's/@<:@0-9.@:>@*$//g'`";;
|
||||
esac])
|
||||
|
||||
sys_name=$(uname -s | tr 'A-Z ' 'a-z_')
|
||||
@@ -40,7 +40,7 @@ case $sys_name in
|
||||
sys_name=cygwin
|
||||
;;
|
||||
esac
|
||||
|
||||
|
||||
AC_MSG_RESULT($system)
|
||||
AC_SUBST(system)
|
||||
AC_DEFINE_UNQUOTED(SYSTEM, ["$system"], [platform identifier (`cpu-os')])
|
||||
@@ -115,6 +115,27 @@ AC_CHECK_HEADERS([sys/mount.h], [], [],
|
||||
])
|
||||
|
||||
|
||||
# Check for vfork.
|
||||
#AC_FUNC_FORK()
|
||||
|
||||
|
||||
# Check for lutimes, optionally used for changing the mtime of
|
||||
# symlinks.
|
||||
AC_CHECK_FUNCS([lutimes])
|
||||
|
||||
|
||||
# Check whether the store optimiser can optimise symlinks.
|
||||
AC_MSG_CHECKING([whether it is possible to create a link to a symlink])
|
||||
ln -s bla tmp_link
|
||||
if ln tmp_link tmp_link2 2> /dev/null; then
|
||||
AC_MSG_RESULT(yes)
|
||||
AC_DEFINE(CAN_LINK_SYMLINK, 1, [Whether link() works on symlinks.])
|
||||
else
|
||||
AC_MSG_RESULT(no)
|
||||
fi
|
||||
rm -f tmp_link tmp_link2
|
||||
|
||||
|
||||
# Check for <locale>.
|
||||
AC_LANG_PUSH(C++)
|
||||
AC_CHECK_HEADERS([locale])
|
||||
@@ -161,6 +182,7 @@ NEED_PROG(perl, perl)
|
||||
NEED_PROG(sed, sed)
|
||||
NEED_PROG(tar, tar)
|
||||
NEED_PROG(bzip2, bzip2)
|
||||
NEED_PROG(xz, xz)
|
||||
AC_PATH_PROG(dot, dot)
|
||||
AC_PATH_PROG(dblatex, dblatex)
|
||||
AC_PATH_PROG(gzip, gzip)
|
||||
@@ -249,7 +271,7 @@ if test "$gc" = yes; then
|
||||
fi
|
||||
|
||||
|
||||
# Check for the required Perl dependencies (DBI and DBD::SQLite).
|
||||
# Check for the required Perl dependencies (DBI, DBD::SQLite and WWW::Curl).
|
||||
perlFlags="-I$perllibdir"
|
||||
|
||||
AC_ARG_WITH(dbi, AC_HELP_STRING([--with-dbi=PATH],
|
||||
@@ -260,13 +282,24 @@ AC_ARG_WITH(dbd-sqlite, AC_HELP_STRING([--with-dbd-sqlite=PATH],
|
||||
[prefix of the Perl DBD::SQLite library]),
|
||||
perlFlags="$perlFlags -I$withval")
|
||||
|
||||
AC_ARG_WITH(www-curl, AC_HELP_STRING([--with-www-curl=PATH],
|
||||
[prefix of the Perl WWW::Curl library]),
|
||||
perlFlags="$perlFlags -I$withval")
|
||||
|
||||
AC_MSG_CHECKING([whether DBD::SQLite works])
|
||||
if ! $perl $perlFlags -e 'use DBI; use DBD::SQLite;' 2>&5; then
|
||||
AC_MSG_RESULT(no)
|
||||
AC_MSG_FAILURE([The Perl modules DBI and/or DBD::SQLite are missing.])
|
||||
fi
|
||||
AC_MSG_RESULT(yes)
|
||||
|
||||
|
||||
AC_MSG_CHECKING([whether WWW::Curl works])
|
||||
if ! $perl $perlFlags -e 'use WWW::Curl;' 2>&5; then
|
||||
AC_MSG_RESULT(no)
|
||||
AC_MSG_FAILURE([The Perl module WWW::Curl is missing.])
|
||||
fi
|
||||
AC_MSG_RESULT(yes)
|
||||
|
||||
AC_SUBST(perlFlags)
|
||||
|
||||
|
||||
@@ -310,10 +343,20 @@ eval dynlib_suffix=$shrext_cmds
|
||||
AC_SUBST(dynlib_suffix)
|
||||
|
||||
|
||||
# Do we have GNU tar?
|
||||
AC_MSG_CHECKING([if you have a recent GNU tar])
|
||||
if $tar --version 2> /dev/null | grep -q GNU && tar cvf /dev/null --warning=no-timestamp ./config.log > /dev/null; then
|
||||
AC_MSG_RESULT(yes)
|
||||
tarFlags="--warning=no-timestamp"
|
||||
else
|
||||
AC_MSG_RESULT(no)
|
||||
fi
|
||||
AC_SUBST(tarFlags)
|
||||
|
||||
|
||||
AM_CONFIG_HEADER([config.h])
|
||||
AC_CONFIG_FILES([Makefile
|
||||
src/Makefile
|
||||
src/bin2c/Makefile
|
||||
src/boost/Makefile
|
||||
src/boost/format/Makefile
|
||||
src/libutil/Makefile
|
||||
@@ -324,7 +367,7 @@ AC_CONFIG_FILES([Makefile
|
||||
src/libexpr/Makefile
|
||||
src/nix-instantiate/Makefile
|
||||
src/nix-env/Makefile
|
||||
src/nix-worker/Makefile
|
||||
src/nix-daemon/Makefile
|
||||
src/nix-setuid-helper/Makefile
|
||||
src/nix-log2xml/Makefile
|
||||
src/bsdiff-4.3/Makefile
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
all-local: config.nix
|
||||
|
||||
files = nar.nix buildenv.nix buildenv.pl unpack-channel.nix unpack-channel.sh derivation.nix fetchurl.nix
|
||||
files = nar.nix buildenv.nix buildenv.pl unpack-channel.nix derivation.nix fetchurl.nix \
|
||||
imported-drv-to-derivation.nix
|
||||
|
||||
install-exec-local:
|
||||
$(INSTALL) -d $(DESTDIR)$(datadir)/nix/corepkgs
|
||||
|
||||
@@ -2,18 +2,22 @@ with import <nix/config.nix>;
|
||||
|
||||
{ derivations, manifest }:
|
||||
|
||||
derivation {
|
||||
derivation {
|
||||
name = "user-environment";
|
||||
system = builtins.currentSystem;
|
||||
builder = perl;
|
||||
args = [ "-w" ./buildenv.pl ];
|
||||
|
||||
|
||||
manifest = manifest;
|
||||
|
||||
# !!! grmbl, need structured data for passing this in a clean way.
|
||||
paths = derivations;
|
||||
active = map (x: if x ? meta && x.meta ? active then x.meta.active else "true") derivations;
|
||||
priority = map (x: if x ? meta && x.meta ? priority then x.meta.priority else "5") derivations;
|
||||
derivations =
|
||||
map (d:
|
||||
[ (d.meta.active or "true")
|
||||
(d.meta.priority or 5)
|
||||
(builtins.length d.outputs)
|
||||
] ++ map (output: builtins.getAttr output d) d.outputs)
|
||||
derivations;
|
||||
|
||||
# Building user environments remotely just causes huge amounts of
|
||||
# network traffic, so don't do that.
|
||||
|
||||
+25
-23
@@ -43,12 +43,12 @@ sub createLinks {
|
||||
$srcFile =~ /\/log$/)
|
||||
{
|
||||
# Do nothing.
|
||||
}
|
||||
}
|
||||
|
||||
elsif (-d $srcFile) {
|
||||
|
||||
lstat $dstFile;
|
||||
|
||||
|
||||
if (-d _) {
|
||||
createLinks($srcFile, $dstFile, $priority);
|
||||
}
|
||||
@@ -59,7 +59,7 @@ sub createLinks {
|
||||
die "collission between directory `$srcFile' and non-directory `$target'";
|
||||
}
|
||||
unlink $dstFile or die "error unlinking `$dstFile': $!";
|
||||
mkdir $dstFile, 0755 ||
|
||||
mkdir $dstFile, 0755 ||
|
||||
die "error creating directory `$dstFile': $!";
|
||||
createLinks($target, $dstFile, $priorities{$dstFile});
|
||||
createLinks($srcFile, $dstFile, $priority);
|
||||
@@ -78,15 +78,15 @@ sub createLinks {
|
||||
if (-l $dstFile) {
|
||||
my $target = readlink $dstFile;
|
||||
my $prevPriority = $priorities{$dstFile};
|
||||
die ( "Collission between `$srcFile' and `$target'. "
|
||||
. "Suggested solution: use `nix-env --set-flag "
|
||||
die ( "collission between `$srcFile' and `$target'; "
|
||||
. "use `nix-env --set-flag "
|
||||
. "priority NUMBER PKGNAME' to change the priority of "
|
||||
. "one of the conflicting packages.\n" )
|
||||
. "one of the conflicting packages\n" )
|
||||
if $prevPriority == $priority;
|
||||
next if $prevPriority < $priority;
|
||||
unlink $dstFile or die;
|
||||
}
|
||||
|
||||
|
||||
symlink($srcFile, $dstFile) ||
|
||||
die "error creating link `$dstFile': $!";
|
||||
$priorities{$dstFile} = $priority;
|
||||
@@ -125,27 +125,29 @@ sub addPkg {
|
||||
|
||||
# Convert the stuff we get from the environment back into a coherent
|
||||
# data type.
|
||||
my @paths = split ' ', $ENV{"paths"};
|
||||
my @active = split ' ', $ENV{"active"};
|
||||
my @priority = split ' ', $ENV{"priority"};
|
||||
|
||||
die if scalar @paths != scalar @active;
|
||||
die if scalar @paths != scalar @priority;
|
||||
|
||||
my %pkgs;
|
||||
|
||||
for (my $n = 0; $n < scalar @paths; $n++) {
|
||||
$pkgs{$paths[$n]} =
|
||||
{ active => $active[$n]
|
||||
, priority => $priority[$n] };
|
||||
my @pkgs;
|
||||
my @derivations = split ' ', $ENV{"derivations"};
|
||||
while (scalar @derivations) {
|
||||
my $active = shift @derivations;
|
||||
my $priority = shift @derivations;
|
||||
my $outputs = shift @derivations;
|
||||
for (my $n = 0; $n < $outputs; $n++) {
|
||||
my $path = shift @derivations;
|
||||
push @pkgs,
|
||||
{ path => $path
|
||||
, active => $active ne "false"
|
||||
, priority => int($priority) };
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
# Symlink to the packages that have been installed explicitly by the
|
||||
# user.
|
||||
foreach my $pkg (sort (keys %pkgs)) {
|
||||
# user. Process in priority order to reduce unnecessary
|
||||
# symlink/unlink steps.
|
||||
@pkgs = sort { $a->{priority} <=> $b->{priority} || $a->{path} cmp $b->{path} } @pkgs;
|
||||
foreach my $pkg (@pkgs) {
|
||||
#print $pkg, " ", $pkgs{$pkg}->{priority}, "\n";
|
||||
addPkg($pkg, $pkgs{$pkg}->{priority}) if $pkgs{$pkg}->{active} ne "false";
|
||||
addPkg($pkg->{path}, $pkg->{priority}) if $pkg->{active};
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -6,8 +6,10 @@ in {
|
||||
perl = "@perl@";
|
||||
shell = "@shell@";
|
||||
coreutils = "@coreutils@";
|
||||
bzip2 = fromEnv "NIX_BZIP2" "@bzip2@";
|
||||
bzip2 = "@bzip2@";
|
||||
xz = "@xz@";
|
||||
tar = "@tar@";
|
||||
tarFlags = "@tarFlags@";
|
||||
tr = "@tr@";
|
||||
curl = "@curl@";
|
||||
nixBinDir = fromEnv "NIX_BIN_DIR" "@bindir@";
|
||||
|
||||
@@ -6,7 +6,7 @@ drvAttrs @ { outputs ? [ "out" ], ... }:
|
||||
let
|
||||
|
||||
strict = derivationStrict drvAttrs;
|
||||
|
||||
|
||||
commonAttrs = drvAttrs // (builtins.listToAttrs outputsList) //
|
||||
{ all = map (x: x.value) outputsList;
|
||||
inherit drvAttrs;
|
||||
@@ -21,7 +21,7 @@ let
|
||||
inherit outputName;
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
outputsList = map outputToAttrListElement outputs;
|
||||
|
||||
|
||||
in (builtins.head outputsList).value
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
with import <nix/config.nix>;
|
||||
|
||||
{system ? builtins.currentSystem, url, outputHash ? "", outputHashAlgo ? "", md5 ? "", sha1 ? "", sha256 ? ""}:
|
||||
{system ? builtins.currentSystem, url, outputHash ? "", outputHashAlgo ? "", md5 ? "", sha1 ? "", sha256 ? "", executable ? false}:
|
||||
|
||||
assert (outputHash != "" && outputHashAlgo != "")
|
||||
|| md5 != "" || sha1 != "" || sha256 != "";
|
||||
@@ -8,10 +8,10 @@ assert (outputHash != "" && outputHashAlgo != "")
|
||||
let
|
||||
|
||||
builder = builtins.toFile "fetchurl.sh"
|
||||
''
|
||||
(''
|
||||
echo "downloading $url into $out"
|
||||
${curl} --fail --location --max-redirs 20 --insecure "$url" > "$out"
|
||||
'';
|
||||
'' + (if executable then "${coreutils}/chmod +x $out" else ""));
|
||||
|
||||
in
|
||||
|
||||
@@ -25,6 +25,7 @@ derivation {
|
||||
if sha256 != "" then "sha256" else if sha1 != "" then "sha1" else "md5";
|
||||
outputHash = if outputHash != "" then outputHash else
|
||||
if sha256 != "" then sha256 else if sha1 != "" then sha1 else md5;
|
||||
outputHashMode = if executable then "recursive" else "flat";
|
||||
|
||||
inherit system url;
|
||||
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
attrs @ { drvPath, outputs, ... }:
|
||||
|
||||
let
|
||||
|
||||
commonAttrs = (builtins.listToAttrs outputsList) //
|
||||
{ all = map (x: x.value) outputsList;
|
||||
inherit drvPath;
|
||||
type = "derivation";
|
||||
};
|
||||
|
||||
outputToAttrListElement = outputName:
|
||||
{ name = outputName;
|
||||
value = commonAttrs // {
|
||||
outPath = builtins.getAttr outputName attrs;
|
||||
inherit outputName;
|
||||
};
|
||||
};
|
||||
|
||||
outputsList = map outputToAttrListElement outputs;
|
||||
|
||||
in (builtins.head outputsList).value
|
||||
+15
-6
@@ -6,28 +6,37 @@ let
|
||||
''
|
||||
export PATH=${nixBinDir}:${coreutils}
|
||||
|
||||
if [ $compressionType = "xz" ]; then
|
||||
ext=xz
|
||||
compressor="${xz} -9"
|
||||
else
|
||||
ext=bz2
|
||||
compressor="${bzip2}"
|
||||
fi
|
||||
|
||||
echo "packing ‘$storePath’..."
|
||||
mkdir $out
|
||||
dst=$out/tmp.nar.bz2
|
||||
dst=$out/tmp.nar.$ext
|
||||
|
||||
set -o pipefail
|
||||
nix-store --dump "$storePath" | ${bzip2} > $dst
|
||||
nix-store --dump "$storePath" | $compressor > $dst
|
||||
|
||||
nix-hash --flat --type $hashAlgo --base32 $dst > $out/narbz2-hash
|
||||
hash=$(nix-hash --flat --type $hashAlgo --base32 $dst)
|
||||
echo -n $hash > $out/nar-compressed-hash
|
||||
|
||||
mv $out/tmp.nar.bz2 $out/$(cat $out/narbz2-hash).nar.bz2
|
||||
mv $dst $out/$hash.nar.$ext
|
||||
'';
|
||||
|
||||
in
|
||||
|
||||
{ storePath, hashAlgo }:
|
||||
{ storePath, hashAlgo, compressionType }:
|
||||
|
||||
derivation {
|
||||
name = "nar";
|
||||
system = builtins.currentSystem;
|
||||
builder = shell;
|
||||
args = [ "-e" builder ];
|
||||
inherit storePath hashAlgo;
|
||||
inherit storePath hashAlgo compressionType;
|
||||
|
||||
# Don't build in a chroot because Nix's dependencies may not be there.
|
||||
__noChroot = true;
|
||||
|
||||
@@ -1,14 +1,36 @@
|
||||
with import <nix/config.nix>;
|
||||
|
||||
{ name, channelName, src }:
|
||||
let
|
||||
|
||||
builder = builtins.toFile "unpack-channel.sh"
|
||||
''
|
||||
mkdir $out
|
||||
cd $out
|
||||
pat="\.xz\$"
|
||||
if [[ "$src" =~ $pat ]]; then
|
||||
${xz} -d < $src | ${tar} xf - ${tarFlags}
|
||||
else
|
||||
${bzip2} -d < $src | ${tar} xf - ${tarFlags}
|
||||
fi
|
||||
mv * $out/$channelName
|
||||
if [ -n "$binaryCacheURL" ]; then
|
||||
mkdir $out/binary-caches
|
||||
echo -n "$binaryCacheURL" > $out/binary-caches/$channelName
|
||||
fi
|
||||
'';
|
||||
|
||||
in
|
||||
|
||||
{ name, channelName, src, binaryCacheURL ? "" }:
|
||||
|
||||
derivation {
|
||||
system = builtins.currentSystem;
|
||||
builder = shell;
|
||||
args = [ "-e" ./unpack-channel.sh ];
|
||||
inherit name channelName src bzip2 tar tr;
|
||||
args = [ "-e" builder ];
|
||||
inherit name channelName src binaryCacheURL;
|
||||
|
||||
PATH = "${nixBinDir}:${coreutils}";
|
||||
|
||||
|
||||
# No point in doing this remotely.
|
||||
preferLocalBuild = true;
|
||||
|
||||
|
||||
@@ -1,4 +0,0 @@
|
||||
mkdir $out
|
||||
cd $out
|
||||
$bzip2 -d < $src | $tar xf -
|
||||
mv * $out/$channelName
|
||||
@@ -1,33 +0,0 @@
|
||||
To produce a `stable' release from the trunk:
|
||||
|
||||
-1. Update the release notes; make sure that the release date is
|
||||
correct.
|
||||
|
||||
0. Make sure that the trunk builds in the release supervisor.
|
||||
|
||||
1. Branch the trunk, e.g., `svn cp .../trunk
|
||||
.../branches/0.5-release'.
|
||||
|
||||
2. Switch to the branch, e.g., `svn switch .../branches/0.5-release'.
|
||||
|
||||
3. In `configure.ac', change `STABLE=0' into `STABLE=1' and commit.
|
||||
|
||||
4. In the release supervisor, add a one-time job to build
|
||||
`.../branches/0.5-release'.
|
||||
|
||||
5. Make sure that the release succeeds.
|
||||
|
||||
6. Move the branch to a tag, e.g., `svn mv .../branches/0.5-release
|
||||
.../tags/0.5'.
|
||||
|
||||
Note that the branch should not be used for maintenance; it should
|
||||
be deleted after the release has been created. A maintenance
|
||||
branch (e.g., `.../branches/0.5') should be created from the
|
||||
original revision of the trunk (since maintenance releases should
|
||||
also be tested first; hence, we cannot have `STABLE=1'). The same
|
||||
procedure can then be followed to produce maintenance releases;
|
||||
just substitute `.../branches/VERSION' for the trunk.
|
||||
|
||||
7. Switch back to the trunk.
|
||||
|
||||
8. Bump the version number in `configure.ac' (in AC_INIT).
|
||||
@@ -23,7 +23,7 @@ man1_MANS = nix-env.1 nix-build.1 nix-store.1 nix-instantiate.1 \
|
||||
|
||||
man5_MANS = nix.conf.5
|
||||
|
||||
man8_MANS = nix-worker.8
|
||||
man8_MANS = nix-daemon.8
|
||||
|
||||
FIGURES = figures/user-environments.png
|
||||
|
||||
|
||||
+112
-56
@@ -19,7 +19,7 @@ is also available as <function>builtins.derivation</function>.</para>
|
||||
|
||||
<variablelist>
|
||||
|
||||
|
||||
|
||||
<varlistentry><term><function>abort</function> <replaceable>s</replaceable></term>
|
||||
|
||||
<listitem><para>Abort Nix expression evaluation, print error
|
||||
@@ -27,7 +27,7 @@ is also available as <function>builtins.derivation</function>.</para>
|
||||
|
||||
</varlistentry>
|
||||
|
||||
|
||||
|
||||
<varlistentry><term><function>builtins.add</function>
|
||||
<replaceable>e1</replaceable> <replaceable>e2</replaceable></term>
|
||||
|
||||
@@ -37,7 +37,7 @@ is also available as <function>builtins.derivation</function>.</para>
|
||||
|
||||
</varlistentry>
|
||||
|
||||
|
||||
|
||||
<varlistentry><term><function>builtins.attrNames</function>
|
||||
<replaceable>attrs</replaceable></term>
|
||||
|
||||
@@ -55,7 +55,7 @@ attrValues = attrs: map (name: builtins.getAttr name attrs) (builtins.attrNames
|
||||
|
||||
</varlistentry>
|
||||
|
||||
|
||||
|
||||
<varlistentry><term><function>baseNameOf</function> <replaceable>s</replaceable></term>
|
||||
|
||||
<listitem><para>Return the <emphasis>base name</emphasis> of the
|
||||
@@ -65,7 +65,7 @@ attrValues = attrs: map (name: builtins.getAttr name attrs) (builtins.attrNames
|
||||
|
||||
</varlistentry>
|
||||
|
||||
|
||||
|
||||
<varlistentry><term><varname>builtins</varname></term>
|
||||
|
||||
<listitem><para>The attribute set <varname>builtins</varname>
|
||||
@@ -82,7 +82,7 @@ if builtins ? getEnv then builtins.getEnv "PATH" else ""</programlisting>
|
||||
|
||||
</varlistentry>
|
||||
|
||||
|
||||
|
||||
<varlistentry><term><function>builtins.compareVersions</function>
|
||||
<replaceable>s1</replaceable> <replaceable>s2</replaceable></term>
|
||||
|
||||
@@ -99,7 +99,16 @@ if builtins ? getEnv then builtins.getEnv "PATH" else ""</programlisting>
|
||||
|
||||
</varlistentry>
|
||||
|
||||
|
||||
|
||||
<varlistentry><term><function>builtins.concatLists</function>
|
||||
<replaceable>lists</replaceable></term>
|
||||
|
||||
<listitem><para>Concatenate a list of lists into a single
|
||||
list.</para></listitem>
|
||||
|
||||
</varlistentry>
|
||||
|
||||
|
||||
<varlistentry
|
||||
xml:id='builtin-currentSystem'><term><varname>builtins.currentSystem</varname></term>
|
||||
|
||||
@@ -124,7 +133,7 @@ if builtins ? getEnv then builtins.getEnv "PATH" else ""</programlisting>
|
||||
</varlistentry>
|
||||
-->
|
||||
|
||||
|
||||
|
||||
<!--
|
||||
<varlistentry><term><function>dependencyClosure</function></term>
|
||||
|
||||
@@ -133,7 +142,7 @@ if builtins ? getEnv then builtins.getEnv "PATH" else ""</programlisting>
|
||||
</varlistentry>
|
||||
-->
|
||||
|
||||
|
||||
|
||||
<varlistentry><term><function>derivation</function>
|
||||
<replaceable>attrs</replaceable></term>
|
||||
|
||||
@@ -152,7 +161,7 @@ if builtins ? getEnv then builtins.getEnv "PATH" else ""</programlisting>
|
||||
|
||||
</varlistentry>
|
||||
|
||||
|
||||
|
||||
<varlistentry><term><function>builtins.div</function>
|
||||
<replaceable>e1</replaceable> <replaceable>e2</replaceable></term>
|
||||
|
||||
@@ -162,7 +171,40 @@ if builtins ? getEnv then builtins.getEnv "PATH" else ""</programlisting>
|
||||
|
||||
</varlistentry>
|
||||
|
||||
|
||||
|
||||
<varlistentry><term><function>builtins.elem</function>
|
||||
<replaceable>x</replaceable> <replaceable>xs</replaceable></term>
|
||||
|
||||
<listitem><para>Return <literal>true</literal> if a value equal to
|
||||
<replaceable>x</replaceable> occurs in the list
|
||||
<replaceable>xs</replaceable>, and <literal>false</literal>
|
||||
otherwise.</para></listitem>
|
||||
|
||||
</varlistentry>
|
||||
|
||||
|
||||
<varlistentry><term><function>builtins.elemAt</function>
|
||||
<replaceable>xs</replaceable> <replaceable>n</replaceable></term>
|
||||
|
||||
<listitem><para>Return element <replaceable>n</replaceable> from
|
||||
the list <replaceable>xs</replaceable>. Elements are counted
|
||||
starting from 0. A fatal error occurs in the index is out of
|
||||
bounds.</para></listitem>
|
||||
|
||||
</varlistentry>
|
||||
|
||||
|
||||
<varlistentry><term><function>builtins.filter</function>
|
||||
<replaceable>f</replaceable> <replaceable>xs</replaceable></term>
|
||||
|
||||
<listitem><para>Return a list consisting of the elements of
|
||||
<replaceable>xs</replaceable> for which the function
|
||||
<replaceable>f</replaceable> returns
|
||||
<literal>true</literal>.</para></listitem>
|
||||
|
||||
</varlistentry>
|
||||
|
||||
|
||||
<varlistentry><term><function>builtins.filterSource</function>
|
||||
<replaceable>e1</replaceable> <replaceable>e2</replaceable></term>
|
||||
|
||||
@@ -214,7 +256,7 @@ stdenv.mkDerivation {
|
||||
|
||||
</varlistentry>
|
||||
|
||||
|
||||
|
||||
<varlistentry><term><function>builtins.getAttr</function>
|
||||
<replaceable>s</replaceable> <replaceable>attrs</replaceable></term>
|
||||
|
||||
@@ -227,7 +269,7 @@ stdenv.mkDerivation {
|
||||
|
||||
</varlistentry>
|
||||
|
||||
|
||||
|
||||
<varlistentry><term><function>builtins.getEnv</function>
|
||||
<replaceable>s</replaceable></term>
|
||||
|
||||
@@ -245,7 +287,7 @@ stdenv.mkDerivation {
|
||||
|
||||
</varlistentry>
|
||||
|
||||
|
||||
|
||||
<varlistentry><term><function>builtins.hasAttr</function>
|
||||
<replaceable>s</replaceable> <replaceable>attrs</replaceable></term>
|
||||
|
||||
@@ -259,7 +301,19 @@ stdenv.mkDerivation {
|
||||
|
||||
</varlistentry>
|
||||
|
||||
|
||||
|
||||
<varlistentry><term><function>builtins.hashString</function>
|
||||
<replaceable>type</replaceable> <replaceable>s</replaceable></term>
|
||||
|
||||
<listitem><para>Return a base-16 representation of the
|
||||
cryptographic hash of string <replaceable>s</replaceable>. The
|
||||
hash algorithm specified by <replaceable>type</replaceable> must
|
||||
be one of <literal>"md5"</literal>, <literal>"sha1"</literal> or
|
||||
<literal>"sha256"</literal>.</para></listitem>
|
||||
|
||||
</varlistentry>
|
||||
|
||||
|
||||
<varlistentry><term><function>builtins.head</function>
|
||||
<replaceable>list</replaceable></term>
|
||||
|
||||
@@ -270,13 +324,15 @@ stdenv.mkDerivation {
|
||||
|
||||
</varlistentry>
|
||||
|
||||
|
||||
|
||||
<varlistentry><term><function>import</function>
|
||||
<replaceable>path</replaceable></term>
|
||||
|
||||
<listitem><para>Load, parse and return the Nix expression in the
|
||||
file <replaceable>path</replaceable>. Evaluation aborts if the
|
||||
file doesn’t exist or contains an incorrect Nix
|
||||
file <replaceable>path</replaceable>. If <replaceable>path
|
||||
</replaceable> is a directory, the file <filename>default.nix
|
||||
</filename> in that directory is loaded. Evaluation aborts if
|
||||
the file doesn’t exist or contains an incorrect Nix
|
||||
expression. <function>import</function> implements Nix’s module
|
||||
system: you can put any Nix expression (such as an attribute set
|
||||
or a function) in a separate file, and use it from Nix expressions
|
||||
@@ -288,7 +344,7 @@ stdenv.mkDerivation {
|
||||
built-in). Therefore, it cannot refer to variables that are in
|
||||
scope at the call site. For instance, if you have a calling
|
||||
expression
|
||||
|
||||
|
||||
<programlisting>
|
||||
rec {
|
||||
x = 123;
|
||||
@@ -323,7 +379,7 @@ x: x + 456</programlisting>
|
||||
|
||||
</varlistentry>
|
||||
|
||||
|
||||
|
||||
<varlistentry><term><function>builtins.intersectAttrs</function>
|
||||
<replaceable>e1</replaceable> <replaceable>e2</replaceable></term>
|
||||
|
||||
@@ -332,8 +388,8 @@ x: x + 456</programlisting>
|
||||
exist in the set <replaceable>e1</replaceable>.</para></listitem>
|
||||
|
||||
</varlistentry>
|
||||
|
||||
|
||||
|
||||
|
||||
<varlistentry><term><function>builtins.isAttrs</function>
|
||||
<replaceable>e</replaceable></term>
|
||||
|
||||
@@ -343,7 +399,7 @@ x: x + 456</programlisting>
|
||||
|
||||
</varlistentry>
|
||||
|
||||
|
||||
|
||||
<varlistentry><term><function>builtins.isList</function>
|
||||
<replaceable>e</replaceable></term>
|
||||
|
||||
@@ -353,7 +409,7 @@ x: x + 456</programlisting>
|
||||
|
||||
</varlistentry>
|
||||
|
||||
|
||||
|
||||
<varlistentry><term><function>builtins.isFunction</function>
|
||||
<replaceable>e</replaceable></term>
|
||||
|
||||
@@ -363,7 +419,7 @@ x: x + 456</programlisting>
|
||||
|
||||
</varlistentry>
|
||||
|
||||
|
||||
|
||||
<varlistentry><term><function>builtins.isString</function>
|
||||
<replaceable>e</replaceable></term>
|
||||
|
||||
@@ -373,7 +429,7 @@ x: x + 456</programlisting>
|
||||
|
||||
</varlistentry>
|
||||
|
||||
|
||||
|
||||
<varlistentry><term><function>builtins.isInt</function>
|
||||
<replaceable>e</replaceable></term>
|
||||
|
||||
@@ -383,7 +439,7 @@ x: x + 456</programlisting>
|
||||
|
||||
</varlistentry>
|
||||
|
||||
|
||||
|
||||
<varlistentry><term><function>builtins.isBool</function>
|
||||
<replaceable>e</replaceable></term>
|
||||
|
||||
@@ -393,7 +449,7 @@ x: x + 456</programlisting>
|
||||
|
||||
</varlistentry>
|
||||
|
||||
|
||||
|
||||
<varlistentry><term><function>isNull</function>
|
||||
<replaceable>e</replaceable></term>
|
||||
|
||||
@@ -403,12 +459,12 @@ x: x + 456</programlisting>
|
||||
|
||||
<warning><para>This function is <emphasis>deprecated</emphasis>;
|
||||
just write <literal>e == null</literal> instead.</para></warning>
|
||||
|
||||
|
||||
</listitem>
|
||||
|
||||
</varlistentry>
|
||||
|
||||
|
||||
|
||||
<varlistentry><term><function>builtins.length</function>
|
||||
<replaceable>e</replaceable></term>
|
||||
|
||||
@@ -417,7 +473,7 @@ x: x + 456</programlisting>
|
||||
|
||||
</varlistentry>
|
||||
|
||||
|
||||
|
||||
<varlistentry><term><function>builtins.lessThan</function>
|
||||
<replaceable>e1</replaceable> <replaceable>e2</replaceable></term>
|
||||
|
||||
@@ -430,7 +486,7 @@ x: x + 456</programlisting>
|
||||
|
||||
</varlistentry>
|
||||
|
||||
|
||||
|
||||
<varlistentry><term><function>builtins.listToAttrs</function>
|
||||
<replaceable>e</replaceable></term>
|
||||
|
||||
@@ -457,7 +513,7 @@ builtins.listToAttrs
|
||||
</para></listitem>
|
||||
|
||||
</varlistentry>
|
||||
|
||||
|
||||
<varlistentry><term><function>map</function>
|
||||
<replaceable>f</replaceable> <replaceable>list</replaceable></term>
|
||||
|
||||
@@ -470,10 +526,10 @@ map (x: "foo" + x) [ "bar" "bla" "abc" ]</programlisting>
|
||||
|
||||
evaluates to <literal>[ "foobar" "foobla" "fooabc"
|
||||
]</literal>.</para></listitem>
|
||||
|
||||
|
||||
</varlistentry>
|
||||
|
||||
|
||||
|
||||
<varlistentry><term><function>builtins.mul</function>
|
||||
<replaceable>e1</replaceable> <replaceable>e2</replaceable></term>
|
||||
|
||||
@@ -483,7 +539,7 @@ map (x: "foo" + x) [ "bar" "bla" "abc" ]</programlisting>
|
||||
|
||||
</varlistentry>
|
||||
|
||||
|
||||
|
||||
<varlistentry><term><function>builtins.parseDrvName</function>
|
||||
<replaceable>s</replaceable></term>
|
||||
|
||||
@@ -498,7 +554,7 @@ map (x: "foo" + x) [ "bar" "bla" "abc" ]</programlisting>
|
||||
|
||||
</varlistentry>
|
||||
|
||||
|
||||
|
||||
<varlistentry><term><function>builtins.pathExists</function>
|
||||
<replaceable>path</replaceable></term>
|
||||
|
||||
@@ -531,7 +587,7 @@ in config.someSetting</programlisting>
|
||||
</varlistentry>
|
||||
-->
|
||||
|
||||
|
||||
|
||||
<varlistentry><term><function>builtins.readFile</function>
|
||||
<replaceable>path</replaceable></term>
|
||||
|
||||
@@ -539,8 +595,8 @@ in config.someSetting</programlisting>
|
||||
<replaceable>path</replaceable> as a string.</para></listitem>
|
||||
|
||||
</varlistentry>
|
||||
|
||||
|
||||
|
||||
|
||||
<varlistentry><term><function>removeAttrs</function>
|
||||
<replaceable>attrs</replaceable> <replaceable>list</replaceable></term>
|
||||
|
||||
@@ -556,7 +612,7 @@ removeAttrs { x = 1; y = 2; z = 3; } [ "a" "x" "z" ]</screen>
|
||||
|
||||
</varlistentry>
|
||||
|
||||
|
||||
|
||||
<varlistentry><term><function>builtins.stringLength</function>
|
||||
<replaceable>e</replaceable></term>
|
||||
|
||||
@@ -566,7 +622,7 @@ removeAttrs { x = 1; y = 2; z = 3; } [ "a" "x" "z" ]</screen>
|
||||
|
||||
</varlistentry>
|
||||
|
||||
|
||||
|
||||
<varlistentry><term><function>builtins.sub</function>
|
||||
<replaceable>e1</replaceable> <replaceable>e2</replaceable></term>
|
||||
|
||||
@@ -576,7 +632,7 @@ removeAttrs { x = 1; y = 2; z = 3; } [ "a" "x" "z" ]</screen>
|
||||
|
||||
</varlistentry>
|
||||
|
||||
|
||||
|
||||
<varlistentry><term><function>builtins.substring</function>
|
||||
<replaceable>start</replaceable> <replaceable>len</replaceable>
|
||||
<replaceable>s</replaceable></term>
|
||||
@@ -594,7 +650,7 @@ removeAttrs { x = 1; y = 2; z = 3; } [ "a" "x" "z" ]</screen>
|
||||
|
||||
</varlistentry>
|
||||
|
||||
|
||||
|
||||
<varlistentry><term><function>builtins.tail</function>
|
||||
<replaceable>list</replaceable></term>
|
||||
|
||||
@@ -604,7 +660,7 @@ removeAttrs { x = 1; y = 2; z = 3; } [ "a" "x" "z" ]</screen>
|
||||
|
||||
</varlistentry>
|
||||
|
||||
|
||||
|
||||
<varlistentry><term><function>throw</function>
|
||||
<replaceable>s</replaceable></term>
|
||||
|
||||
@@ -618,7 +674,7 @@ removeAttrs { x = 1; y = 2; z = 3; } [ "a" "x" "z" ]</screen>
|
||||
|
||||
</varlistentry>
|
||||
|
||||
|
||||
|
||||
<varlistentry
|
||||
xml:id='builtin-toFile'><term><function>builtins.toFile</function>
|
||||
<replaceable>name</replaceable> <replaceable>s</replaceable></term>
|
||||
@@ -636,7 +692,7 @@ removeAttrs { x = 1; y = 2; z = 3; } [ "a" "x" "z" ]</screen>
|
||||
|
||||
stdenv.mkDerivation {
|
||||
name = "hello-2.1.1";
|
||||
|
||||
|
||||
builder = builtins.toFile "builder.sh" "
|
||||
source $stdenv/setup
|
||||
|
||||
@@ -655,7 +711,7 @@ stdenv.mkDerivation {
|
||||
};
|
||||
inherit perl;
|
||||
}</programlisting>
|
||||
|
||||
|
||||
</para>
|
||||
|
||||
<para>It is even possible for one file to refer to another, e.g.,
|
||||
@@ -693,7 +749,7 @@ in foo</programlisting>
|
||||
|
||||
</varlistentry>
|
||||
|
||||
|
||||
|
||||
<varlistentry><term><function>builtins.toPath</function> <replaceable>s</replaceable></term>
|
||||
|
||||
<listitem><para>Convert the string value
|
||||
@@ -706,7 +762,7 @@ in foo</programlisting>
|
||||
|
||||
</varlistentry>
|
||||
|
||||
|
||||
|
||||
<varlistentry><term><function>toString</function> <replaceable>e</replaceable></term>
|
||||
|
||||
<listitem><para>Convert the expression
|
||||
@@ -718,7 +774,7 @@ in foo</programlisting>
|
||||
|
||||
</varlistentry>
|
||||
|
||||
|
||||
|
||||
<varlistentry xml:id='builtin-toXML'><term><function>builtins.toXML</function> <replaceable>e</replaceable></term>
|
||||
|
||||
<listitem><para>Return a string containing an XML representation
|
||||
@@ -763,7 +819,7 @@ in foo</programlisting>
|
||||
|
||||
<example xml:id='ex-toxml'><title>Passing information to a builder
|
||||
using <function>toXML</function></title>
|
||||
|
||||
|
||||
<programlisting><![CDATA[
|
||||
{ stdenv, fetchurl, libxslt, jira, uberwiki }:
|
||||
|
||||
@@ -771,7 +827,7 @@ stdenv.mkDerivation (rec {
|
||||
name = "web-server";
|
||||
|
||||
buildInputs = [ libxslt ];
|
||||
|
||||
|
||||
builder = builtins.toFile "builder.sh" "
|
||||
source $stdenv/setup
|
||||
mkdir $out
|
||||
@@ -804,7 +860,7 @@ stdenv.mkDerivation (rec {
|
||||
|
||||
<example xml:id='ex-toxml-result'><title>XML representation produced by
|
||||
<function>toXML</function></title>
|
||||
|
||||
|
||||
<programlisting><![CDATA[<?xml version='1.0' encoding='utf-8'?>
|
||||
<expr>
|
||||
<list>
|
||||
@@ -833,7 +889,7 @@ stdenv.mkDerivation (rec {
|
||||
|
||||
</varlistentry>
|
||||
|
||||
|
||||
|
||||
<varlistentry><term><function>builtins.trace</function>
|
||||
<replaceable>e1</replaceable> <replaceable>e2</replaceable></term>
|
||||
|
||||
@@ -844,7 +900,7 @@ stdenv.mkDerivation (rec {
|
||||
|
||||
</varlistentry>
|
||||
|
||||
|
||||
|
||||
</variablelist>
|
||||
|
||||
|
||||
|
||||
+120
-35
@@ -30,18 +30,21 @@ gc-keep-derivations = true # Idem
|
||||
env-keep-derivations = false
|
||||
</programlisting>
|
||||
|
||||
<para>The following settings are currently available:
|
||||
<para>You can override settings using the <option>--option</option>
|
||||
flag, e.g. <literal>--option gc-keep-outputs false</literal>.</para>
|
||||
|
||||
<para>The following settings are currently available:
|
||||
|
||||
<variablelist>
|
||||
|
||||
|
||||
|
||||
<varlistentry xml:id="conf-gc-keep-outputs"><term><literal>gc-keep-outputs</literal></term>
|
||||
|
||||
<listitem><para>If <literal>true</literal>, the garbage collector
|
||||
will keep the outputs of non-garbage derivations. If
|
||||
<literal>false</literal> (default), outputs will be deleted unless
|
||||
they are GC roots themselves (or reachable from other roots).</para>
|
||||
|
||||
|
||||
<para>In general, outputs must be registered as roots separately.
|
||||
However, even if the output of a derivation is registered as a
|
||||
root, the collector will still delete store paths that are used
|
||||
@@ -50,7 +53,7 @@ env-keep-derivations = false
|
||||
this option to <literal>true</literal>.</para></listitem>
|
||||
|
||||
</varlistentry>
|
||||
|
||||
|
||||
|
||||
<varlistentry xml:id="conf-gc-keep-derivations"><term><literal>gc-keep-derivations</literal></term>
|
||||
|
||||
@@ -68,7 +71,7 @@ env-keep-derivations = false
|
||||
|
||||
</varlistentry>
|
||||
|
||||
|
||||
|
||||
<varlistentry><term><literal>env-keep-derivations</literal></term>
|
||||
|
||||
<listitem><para>If <literal>false</literal> (default), derivations
|
||||
@@ -92,7 +95,7 @@ env-keep-derivations = false
|
||||
|
||||
</varlistentry>
|
||||
|
||||
|
||||
|
||||
<varlistentry xml:id="conf-build-max-jobs"><term><literal>build-max-jobs</literal></term>
|
||||
|
||||
<listitem><para>This option defines the maximum number of jobs
|
||||
@@ -198,10 +201,8 @@ env-keep-derivations = false
|
||||
under the uid of the Nix process (that is, the uid of the caller
|
||||
if <envar>NIX_REMOTE</envar> is empty, the uid under which the Nix
|
||||
daemon runs if <envar>NIX_REMOTE</envar> is
|
||||
<literal>daemon</literal>, or the uid that owns the setuid
|
||||
<command>nix-worker</command> program if <envar>NIX_REMOTE</envar>
|
||||
is <literal>slave</literal>). Obviously, this should not be used
|
||||
in multi-user settings with untrusted users.</para>
|
||||
<literal>daemon</literal>). Obviously, this should not be used in
|
||||
multi-user settings with untrusted users.</para>
|
||||
|
||||
</listitem>
|
||||
|
||||
@@ -233,7 +234,27 @@ env-keep-derivations = false
|
||||
|
||||
</varlistentry>
|
||||
|
||||
|
||||
|
||||
<varlistentry xml:id="conf-build-chroot-dirs"><term><literal>build-chroot-dirs</literal></term>
|
||||
|
||||
<listitem><para>When builds are performed in a chroot environment,
|
||||
Nix will mount some directories from the normal file system
|
||||
hierarchy inside the chroot. These are the Nix store, the
|
||||
temporary build directory (usually
|
||||
<filename>/tmp/nix-build-<replaceable>drvname</replaceable>-<replaceable>number</replaceable></filename>),
|
||||
the <literal>/proc</literal> filesystem, and the directories
|
||||
listed here. The default is <literal>/dev /dev/pts</literal>,
|
||||
since these contain files needed by many builds (such as
|
||||
<filename>/dev/null</filename>). You can use the syntax
|
||||
<literal><replaceable>target</replaceable>=<replaceable>source</replaceable></literal>
|
||||
to mount a path in a different location in the chroot; for
|
||||
instance, <literal>/bin=/nix-bin</literal> will mount the
|
||||
directory <literal>/nix-bin</literal> as <literal>/bin</literal>
|
||||
inside the chroot.</para></listitem>
|
||||
|
||||
</varlistentry>
|
||||
|
||||
|
||||
<varlistentry><term><literal>build-use-substitutes</literal></term>
|
||||
|
||||
<listitem><para>If set to <literal>true</literal> (default), Nix
|
||||
@@ -242,34 +263,17 @@ env-keep-derivations = false
|
||||
|
||||
</varlistentry>
|
||||
|
||||
|
||||
<varlistentry xml:id="conf-build-chroot-dirs"><term><literal>build-chroot-dirs</literal></term>
|
||||
|
||||
<listitem><para>When builds are performed in a chroot environment,
|
||||
Nix will mount (using <command>mount --bind</command> on Linux)
|
||||
some directories from the normal file system hierarchy inside the
|
||||
chroot. These are the Nix store, the temporary build directory
|
||||
(usually
|
||||
<filename>/tmp/nix-<replaceable>pid</replaceable>-<replaceable>number</replaceable></filename>)
|
||||
and the directories listed here. The default is <literal>dev
|
||||
/proc</literal>. Files in <filename>/dev</filename> (such as
|
||||
<filename>/dev/null</filename>) are needed by many builds, and
|
||||
some files in <filename>/proc</filename> may also be needed
|
||||
occasionally.</para>
|
||||
<varlistentry><term><literal>build-fallback</literal></term>
|
||||
|
||||
<para>The value used on NixOS is
|
||||
|
||||
<programlisting>
|
||||
build-use-chroot = /dev /proc /bin</programlisting>
|
||||
|
||||
to make the <filename>/bin/sh</filename> symlink available (which
|
||||
is still needed by many builders).</para>
|
||||
|
||||
</listitem>
|
||||
<listitem><para>If set to <literal>true</literal>, Nix will fall
|
||||
back to building from source if a binary substitute fails. This
|
||||
is equivalent to the <option>--fallback</option> flag. The
|
||||
default is <literal>false</literal>.</para></listitem>
|
||||
|
||||
</varlistentry>
|
||||
|
||||
|
||||
|
||||
<varlistentry><term><literal>build-cache-failures</literal></term>
|
||||
|
||||
<listitem><para>If set to <literal>true</literal>, Nix will
|
||||
@@ -307,6 +311,74 @@ build-use-chroot = /dev /proc /bin</programlisting>
|
||||
</varlistentry>
|
||||
|
||||
|
||||
<varlistentry><term><literal>use-binary-caches</literal></term>
|
||||
|
||||
<listitem><para>If set to <literal>true</literal> (the default),
|
||||
Nix will check the binary caches specified by
|
||||
<option>binary-caches</option> and related options to obtain
|
||||
binary substitutes.</para></listitem>
|
||||
|
||||
</varlistentry>
|
||||
|
||||
|
||||
<varlistentry><term><literal>binary-caches</literal></term>
|
||||
|
||||
<listitem><para>A list of URLs of binary caches, separated by
|
||||
whitespace. The default is
|
||||
<literal>http://nixos.org/binary-cache</literal>.</para></listitem>
|
||||
|
||||
</varlistentry>
|
||||
|
||||
|
||||
<varlistentry><term><literal>binary-caches-files</literal></term>
|
||||
|
||||
<listitem><para>A list of names of files that will be read to
|
||||
obtain additional binary cache URLs. The default is
|
||||
<literal>/nix/var/nix/profiles/per-user/<replaceable>username</replaceable>/channels/binary-caches/*</literal>.
|
||||
Note that when you’re using the Nix daemon,
|
||||
<replaceable>username</replaceable> is always equal to
|
||||
<literal>root</literal>, so Nix will only use the binary caches
|
||||
provided by the channels installed by root. Do not set this
|
||||
option to read files created by untrusted users!</para></listitem>
|
||||
|
||||
</varlistentry>
|
||||
|
||||
|
||||
<varlistentry><term><literal>trusted-binary-caches</literal></term>
|
||||
|
||||
<listitem><para>A list of URLs of binary caches, separated by
|
||||
whitespace. These are not used by default, but can be enabled by
|
||||
users of the Nix daemon by specifying <literal>--option
|
||||
binary-caches <replaceable>urls</replaceable></literal> on the
|
||||
command line. Daemon users are only allowed to pass a subset of
|
||||
the URLs listed in <literal>binary-caches</literal> and
|
||||
<literal>trusted-binary-caches</literal>.</para></listitem>
|
||||
|
||||
</varlistentry>
|
||||
|
||||
|
||||
<varlistentry><term><literal>binary-caches-parallel-connections</literal></term>
|
||||
|
||||
<listitem><para>The maximum number of parallel HTTP connections
|
||||
used by the binary cache substituter to get NAR info files. This
|
||||
number should be high to minimise latency. It defaults to
|
||||
150.</para></listitem>
|
||||
|
||||
</varlistentry>
|
||||
|
||||
|
||||
<varlistentry><term><literal>force-manifest</literal></term>
|
||||
|
||||
<listitem><para>If this option is set to <literal>false</literal>
|
||||
(default) and a Nix channel provides both a manifest and a binary
|
||||
cache, only the binary cache will be used. If set to
|
||||
<literal>true</literal>, the manifest will be fetched as well.
|
||||
This is useful if you want to use binary patches (which are
|
||||
currently not supported by binary caches).</para></listitem>
|
||||
|
||||
</varlistentry>
|
||||
|
||||
|
||||
<varlistentry><term><literal>system</literal></term>
|
||||
|
||||
<listitem><para>This option specifies the canonical Nix system
|
||||
@@ -337,7 +409,20 @@ build-use-chroot = /dev /proc /bin</programlisting>
|
||||
<literal>true</literal>.</para></listitem>
|
||||
|
||||
</varlistentry>
|
||||
|
||||
|
||||
|
||||
<varlistentry><term><literal>auto-optimise-store</literal></term>
|
||||
|
||||
<listitem><para>If set to <literal>true</literal> (the default),
|
||||
Nix automatically detects files in the store that have identical
|
||||
contents, and replaces them with hard links to a single copy.
|
||||
This saves disk space. If set to <literal>false</literal>, you
|
||||
can still run <command>nix-store --optimise</command> to get rid
|
||||
of duplicate files.</para></listitem>
|
||||
|
||||
</varlistentry>
|
||||
|
||||
|
||||
</variablelist>
|
||||
|
||||
</para>
|
||||
|
||||
@@ -283,7 +283,7 @@ $ mount -o bind /mnt/otherdisk/nix /nix</screen>
|
||||
|
||||
<listitem><para>This variable should be set to
|
||||
<literal>daemon</literal> if you want to use the Nix daemon to
|
||||
executed Nix operations, which is necessary in <link
|
||||
execute Nix operations. This is necessary in <link
|
||||
linkend="ssec-multi-user">multi-user Nix installations</link>.
|
||||
Otherwise, it should be left unset.</para></listitem>
|
||||
|
||||
@@ -304,9 +304,9 @@ $ mount -o bind /mnt/otherdisk/nix /nix</screen>
|
||||
<literal>/var/run/nix/remote-stores</literal>.</para>
|
||||
|
||||
<para>Note that if you’re building through the <link
|
||||
linkend="sec-nix-worker">Nix daemon</link>, the only setting for
|
||||
linkend="sec-nix-daemon">Nix daemon</link>, the only setting for
|
||||
this variable that matters is the one that the
|
||||
<command>nix-worker</command> process uses. So if you want to
|
||||
<command>nix-daemon</command> process uses. So if you want to
|
||||
change it, you have to restart the daemon.</para></listitem>
|
||||
|
||||
</varlistentry>
|
||||
|
||||
@@ -160,6 +160,18 @@
|
||||
</glossentry>
|
||||
|
||||
|
||||
<glossentry xml:id="gloss-nar"><glossterm>NAR</glossterm>
|
||||
|
||||
<glossdef><para>A <emphasis>N</emphasis>ix
|
||||
<emphasis>AR</emphasis>chive. This is a serialisation of a path in
|
||||
the Nix store. It can contain regular files, directories and
|
||||
symbolic links. NARs are generated and unpacked using
|
||||
<command>nix-store --dump</command> and <command>nix-store
|
||||
--restore</command>.</para></glossdef>
|
||||
|
||||
</glossentry>
|
||||
|
||||
|
||||
|
||||
</glosslist>
|
||||
|
||||
|
||||
@@ -116,6 +116,8 @@ a source distribution.</para>
|
||||
|
||||
<itemizedlist>
|
||||
|
||||
<listitem><para>GNU Make.</para></listitem>
|
||||
|
||||
<listitem><para>A fairly recent version of GCC/G++. Version 2.95
|
||||
and higher should work. Clang will also work.</para></listitem>
|
||||
|
||||
@@ -217,7 +219,8 @@ $ ./configure <replaceable>options...</replaceable>
|
||||
$ make
|
||||
$ make install</screen>
|
||||
|
||||
</para>
|
||||
Nix requires GNU Make so you may need to invoke
|
||||
<command>gmake</command> instead.</para>
|
||||
|
||||
<para>When building from the Git repository, these should be preceded
|
||||
by the command:
|
||||
@@ -409,11 +412,11 @@ $ chown -R root /nix/store /nix/var/nix</screen>
|
||||
|
||||
</para>
|
||||
|
||||
<para>The <link linkend="sec-nix-worker">Nix daemon</link> should be
|
||||
<para>The <link linkend="sec-nix-daemon">Nix daemon</link> should be
|
||||
started as follows (as <literal>root</literal>):
|
||||
|
||||
<screen>
|
||||
$ nix-worker --daemon</screen>
|
||||
$ nix-daemon</screen>
|
||||
|
||||
You’ll want to put that line somewhere in your system’s boot
|
||||
scripts.</para>
|
||||
@@ -447,11 +450,11 @@ named anything. It should own the Nix store and database:
|
||||
<screen>
|
||||
$ chown -R nix /nix/store /nix/var/nix</screen>
|
||||
|
||||
and of course <command>nix-worker --daemon</command> should be started
|
||||
under that user, e.g.,
|
||||
and of course <command>nix-daemon</command> should be started under
|
||||
that user, e.g.,
|
||||
|
||||
<screen>
|
||||
$ su - nix -c "exec /nix/bin/nix-worker --daemon"</screen>
|
||||
$ su - nix -c "exec /nix/bin/nix-daemon"</screen>
|
||||
|
||||
</para>
|
||||
|
||||
|
||||
@@ -59,7 +59,7 @@
|
||||
<xi:include href="nix-prefetch-url.xml" />
|
||||
<xi:include href="nix-pull.xml" />
|
||||
<xi:include href="nix-push.xml" />
|
||||
<xi:include href="nix-worker.xml" />
|
||||
<xi:include href="nix-daemon.xml" />
|
||||
</section>
|
||||
|
||||
<section>
|
||||
|
||||
@@ -96,7 +96,7 @@ also <xref linkend="sec-common-options" />.</phrase></para>
|
||||
<variablelist>
|
||||
|
||||
<varlistentry><term><option>--drv-link</option> <replaceable>drvlink</replaceable></term>
|
||||
|
||||
|
||||
<listitem><para>Add a symlink named
|
||||
<replaceable>drvlink</replaceable> to the store derivation
|
||||
produced by <command>nix-instantiate</command>. The derivation is
|
||||
@@ -108,14 +108,14 @@ also <xref linkend="sec-common-options" />.</phrase></para>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry><term><option>--add-drv-link</option></term>
|
||||
|
||||
|
||||
<listitem><para>Shorthand for <option>--drv-link</option>
|
||||
<filename>./derivation</filename>.</para></listitem>
|
||||
|
||||
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry><term><option>--no-out-link</option></term>
|
||||
|
||||
|
||||
<listitem><para>Do not create a symlink to the output path. Note
|
||||
that as a result the output does not become a root of the garbage
|
||||
collector, and so might be deleted by <command>nix-store
|
||||
@@ -125,7 +125,7 @@ also <xref linkend="sec-common-options" />.</phrase></para>
|
||||
|
||||
<varlistentry xml:id='opt-out-link'><term><option>--out-link</option> /
|
||||
<option>-o</option> <replaceable>outlink</replaceable></term>
|
||||
|
||||
|
||||
<listitem><para>Change the name of the symlink to the output path
|
||||
created from <filename>result</filename> to
|
||||
<replaceable>outlink</replaceable>.</para></listitem>
|
||||
@@ -143,7 +143,7 @@ also <xref linkend="sec-common-options" />.</phrase></para>
|
||||
<variablelist>
|
||||
|
||||
<varlistentry><term><option>--command</option> <replaceable>cmd</replaceable></term>
|
||||
|
||||
|
||||
<listitem><para>In the environment of the derivation, executeq the
|
||||
command <replaceable>cmd</replaceable> instead of the default
|
||||
interactive shell.</para></listitem>
|
||||
@@ -151,7 +151,7 @@ also <xref linkend="sec-common-options" />.</phrase></para>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry><term><option>--exclude</option> <replaceable>regexp</replaceable></term>
|
||||
|
||||
|
||||
<listitem><para>Do not build any dependencies whose store path
|
||||
matches the regular expression <replaceable>regexp</replaceable>.
|
||||
This option may be specified multiple times.</para></listitem>
|
||||
@@ -190,6 +190,25 @@ $ ./pan/gui/pan
|
||||
|
||||
</para>
|
||||
|
||||
<para>If a derivation has multiple outputs,
|
||||
<command>nix-build</command> will build the default (first) output.
|
||||
You can also build all outputs:
|
||||
<screen>
|
||||
$ nix-build '<nixpkgs>' -A openssl.all
|
||||
</screen>
|
||||
This will create a symlink for each output named
|
||||
<filename>result-<replaceable>outputname</replaceable></filename>.
|
||||
The suffix is omitted if the output name is <literal>out</literal>.
|
||||
So if <literal>openssl</literal> has outputs <literal>out</literal>,
|
||||
<literal>bin</literal> and <literal>man</literal>,
|
||||
<command>nix-build</command> will create symlinks
|
||||
<literal>result</literal>, <literal>result-bin</literal> and
|
||||
<literal>result-man</literal>. It’s also possible to build a specific
|
||||
output:
|
||||
<screen>
|
||||
$ nix-build '<nixpkgs>' -A openssl.man
|
||||
</screen>
|
||||
This will create a symlink <literal>result-man</literal>.</para>
|
||||
|
||||
</refsection>
|
||||
|
||||
@@ -201,6 +220,6 @@ $ ./pan/gui/pan
|
||||
</variablelist>
|
||||
|
||||
</refsection>
|
||||
|
||||
|
||||
|
||||
</refentry>
|
||||
|
||||
@@ -103,7 +103,7 @@ respectively.</para>
|
||||
<para>To subscribe to the Nixpkgs channel and install the GNU Hello package:</para>
|
||||
|
||||
<screen>
|
||||
$ nix-channel --add http://nixos.org/releases/nixpkgs/channels/nixpkgs-unstable
|
||||
$ nix-channel --add http://nixos.org/channels/nixpkgs-unstable
|
||||
$ nix-channel --update
|
||||
$ nix-env -iA nixpkgs.hello</screen>
|
||||
|
||||
|
||||
@@ -26,6 +26,7 @@
|
||||
<arg choice='plain'><option>--print-dead</option></arg>
|
||||
<arg choice='plain'><option>--delete</option></arg>
|
||||
</group>
|
||||
<arg><option>--dry-run</option></arg>
|
||||
</cmdsynopsis>
|
||||
</refsynopsisdiv>
|
||||
|
||||
|
||||
@@ -28,6 +28,8 @@
|
||||
<arg><option>--xz</option></arg>
|
||||
<arg><option>--show-progress</option></arg>
|
||||
<arg><option>--include-outputs</option></arg>
|
||||
<arg><option>--use-substitutes</option></arg>
|
||||
<arg><option>-s</option></arg>
|
||||
<arg choice='plain'>
|
||||
<replaceable>user@</replaceable><replaceable>machine</replaceable>
|
||||
</arg>
|
||||
@@ -65,7 +67,7 @@ those paths. If this bothers you, use
|
||||
<refsection><title>Options</title>
|
||||
|
||||
<variablelist>
|
||||
|
||||
|
||||
<varlistentry><term><option>--to</option></term>
|
||||
|
||||
<listitem><para>Copy the closure of
|
||||
@@ -119,10 +121,22 @@ those paths. If this bothers you, use
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry><term><option>--include-outputs</option></term>
|
||||
|
||||
<listitem><para>Also copy the outputs of store derivations included
|
||||
in the closure.</para></listitem>
|
||||
|
||||
|
||||
<listitem><para>Also copy the outputs of store derivations
|
||||
included in the closure.</para></listitem>
|
||||
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry><term><option>--use-substitutes</option> / <option>-s</option></term>
|
||||
|
||||
<listitem><para>Attempt to download missing paths on the target
|
||||
machine using Nix’s substitute mechanism. Any paths that cannot
|
||||
be substituted on the target are still copied normally from the
|
||||
source. This is useful, for instance, if the connection between
|
||||
the source and target machine is slow, but the connection between
|
||||
the target machine and <literal>nixos.org</literal> (the default
|
||||
binary cache server) is fast.</para></listitem>
|
||||
|
||||
</varlistentry>
|
||||
|
||||
</variablelist>
|
||||
@@ -140,7 +154,7 @@ those paths. If this bothers you, use
|
||||
<command>ssh</command> on the command line.</para></listitem>
|
||||
|
||||
</varlistentry>
|
||||
|
||||
|
||||
</variablelist>
|
||||
|
||||
</refsection>
|
||||
|
||||
@@ -1,24 +1,23 @@
|
||||
<refentry xmlns="http://docbook.org/ns/docbook"
|
||||
xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||
xmlns:xi="http://www.w3.org/2001/XInclude"
|
||||
xml:id="sec-nix-worker">
|
||||
xml:id="sec-nix-daemon">
|
||||
|
||||
<refmeta>
|
||||
<refentrytitle>nix-worker</refentrytitle>
|
||||
<refentrytitle>nix-daemon</refentrytitle>
|
||||
<manvolnum>8</manvolnum>
|
||||
<refmiscinfo class="source">Nix</refmiscinfo>
|
||||
<refmiscinfo class="version"><xi:include href="version.txt" parse="text"/></refmiscinfo>
|
||||
</refmeta>
|
||||
|
||||
<refnamediv>
|
||||
<refname>nix-worker</refname>
|
||||
<refname>nix-daemon</refname>
|
||||
<refpurpose>Nix multi-user support daemon</refpurpose>
|
||||
</refnamediv>
|
||||
|
||||
<refsynopsisdiv>
|
||||
<cmdsynopsis>
|
||||
<command>nix-worker</command>
|
||||
<arg choice="plain"><option>--daemon</option></arg>
|
||||
<command>nix-daemon</command>
|
||||
</cmdsynopsis>
|
||||
</refsynopsisdiv>
|
||||
|
||||
+15
-5
@@ -93,11 +93,10 @@ also <xref linkend="sec-common-options" />.</phrase></para>
|
||||
|
||||
<listitem><para>Specifies the profile to be used by those
|
||||
operations that operate on a profile (designated below as the
|
||||
<emphasis>active profile</emphasis>). A profile is sequence of
|
||||
<emphasis>active profile</emphasis>). A profile is a sequence of
|
||||
user environments called <emphasis>generations</emphasis>, one of
|
||||
which is the <emphasis>current generation</emphasis>. The default
|
||||
profile is the target of the symbolic link
|
||||
<filename>~/.nix-profile</filename> (see below).</para></listitem>
|
||||
which is the <emphasis>current
|
||||
generation</emphasis>.</para></listitem>
|
||||
|
||||
</varlistentry>
|
||||
|
||||
@@ -105,7 +104,8 @@ also <xref linkend="sec-common-options" />.</phrase></para>
|
||||
|
||||
<listitem><para>For the <option>--install</option>,
|
||||
<option>--upgrade</option>, <option>--uninstall</option>,
|
||||
<option>--switch-generation</option> and
|
||||
<option>--switch-generation</option>,
|
||||
<option>--delete-generations</option> and
|
||||
<option>--rollback</option> operations, this flag will cause
|
||||
<command>nix-env</command> to print what
|
||||
<emphasis>would</emphasis> be done if this flag had not been
|
||||
@@ -1255,6 +1255,16 @@ error: no generation older than the current (91) exists</screen>
|
||||
<refsection condition="manpage"><title>Environment variables</title>
|
||||
|
||||
<variablelist>
|
||||
|
||||
<varlistentry><term><envar>NIX_PROFILE</envar></term>
|
||||
|
||||
<listitem><para>Location of the Nix profile. Defaults to the
|
||||
target of the symlink <filename>~/.nix-profile</filename>, if it
|
||||
exists, or <filename>/nix/var/nix/profiles/default</filename>
|
||||
otherwise.</para></listitem>
|
||||
|
||||
</varlistentry>
|
||||
|
||||
<xi:include href="env-common.xml#xmlns(db=http://docbook.org/ns/docbook)xpointer(//db:variablelist[@xml:id='env-common']/*)" />
|
||||
</variablelist>
|
||||
|
||||
|
||||
@@ -91,8 +91,9 @@ cryptographic hash as <literal>nix-store --dump
|
||||
|
||||
<varlistentry><term><option>--type</option> <replaceable>hashAlgo</replaceable></term>
|
||||
|
||||
<listitem><para>Specify a cryptographic hash, which can be one of
|
||||
<literal>md5</literal>, <literal>sha1</literal>, and
|
||||
<listitem><para>Use the specified cryptographic hash algorithm,
|
||||
which can be one of <literal>md5</literal>,
|
||||
<literal>sha1</literal>, and
|
||||
<literal>sha256</literal>.</para></listitem>
|
||||
|
||||
</varlistentry>
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
<refsynopsisdiv>
|
||||
<cmdsynopsis>
|
||||
<command>nix-prefetch-url</command>
|
||||
<arg><option>--type</option> <replaceable>hashAlgo</replaceable></arg>
|
||||
<arg choice='plain'><replaceable>url</replaceable></arg>
|
||||
<arg><replaceable>hash</replaceable></arg>
|
||||
</cmdsynopsis>
|
||||
@@ -45,11 +46,6 @@ download it again when you build your Nix expression. Since
|
||||
as <command>nix-prefetch-url</command>, the redundant download can be
|
||||
avoided.</para>
|
||||
|
||||
<para>The environment variable <envar>NIX_HASH_ALGO</envar> specifies
|
||||
which hash algorithm to use. It can be either <literal>md5</literal>,
|
||||
<literal>sha1</literal>, or <literal>sha256</literal>. The default is
|
||||
<literal>sha256</literal>.</para>
|
||||
|
||||
<para>If <replaceable>hash</replaceable> is specified, then a download
|
||||
is not performed if the Nix store already contains a file with the
|
||||
same hash and base name. Otherwise, the file is downloaded, and an
|
||||
@@ -63,6 +59,24 @@ of the downloaded file in the Nix store is also printed.</para>
|
||||
</refsection>
|
||||
|
||||
|
||||
<refsection><title>Options</title>
|
||||
|
||||
<variablelist>
|
||||
|
||||
<varlistentry><term><option>--type</option> <replaceable>hashAlgo</replaceable></term>
|
||||
|
||||
<listitem><para>Use the specified cryptographic hash algorithm,
|
||||
which can be one of <literal>md5</literal>,
|
||||
<literal>sha1</literal>, and
|
||||
<literal>sha256</literal>.</para></listitem>
|
||||
|
||||
</varlistentry>
|
||||
|
||||
</variablelist>
|
||||
|
||||
</refsection>
|
||||
|
||||
|
||||
<refsection><title>Examples</title>
|
||||
|
||||
<screen>
|
||||
|
||||
+338
-76
@@ -12,24 +12,19 @@
|
||||
|
||||
<refnamediv>
|
||||
<refname>nix-push</refname>
|
||||
<refpurpose>push store paths onto a network cache</refpurpose>
|
||||
<refpurpose>generate a binary cache</refpurpose>
|
||||
</refnamediv>
|
||||
|
||||
<refsynopsisdiv>
|
||||
<cmdsynopsis>
|
||||
<command>nix-push</command>
|
||||
<group choice='req'>
|
||||
<arg choice='req'>
|
||||
<arg choice='plain'><replaceable>archivesPutURL</replaceable></arg>
|
||||
<arg choice='plain'><replaceable>archivesGetURL</replaceable></arg>
|
||||
<arg choice='plain'><replaceable>manifestPutURL</replaceable></arg>
|
||||
</arg>
|
||||
<arg choice='req'>
|
||||
<arg choice='plain'><option>--copy</option></arg>
|
||||
<arg choice='plain'><replaceable>archivesDir</replaceable></arg>
|
||||
<arg choice='plain'><replaceable>manifestFile</replaceable></arg>
|
||||
</arg>
|
||||
</group>
|
||||
<arg choice='plain'><option>--dest</option> <replaceable>dest-dir</replaceable></arg>
|
||||
<arg><option>--bzip2</option></arg>
|
||||
<arg><option>--force</option></arg>
|
||||
<arg><option>--link</option></arg>
|
||||
<arg><option>--manifest</option></arg>
|
||||
<arg><option>--manifest-path</option> <replaceable>filename</replaceable></arg>
|
||||
<arg><option>--url-prefix</option> <replaceable>url</replaceable></arg>
|
||||
<arg choice='plain' rep='repeat'><replaceable>paths</replaceable></arg>
|
||||
</cmdsynopsis>
|
||||
</refsynopsisdiv>
|
||||
@@ -37,93 +32,360 @@
|
||||
|
||||
<refsection><title>Description</title>
|
||||
|
||||
<para>The command <command>nix-push</command> builds a set of store
|
||||
paths (if necessary), and then packages and uploads all store paths in
|
||||
the resulting closures to a server. A network cache thus populated
|
||||
can subsequently be used to speed up software deployment on other
|
||||
machines using the <command>nix-pull</command> command.</para>
|
||||
<para>The command <command>nix-push</command> produces a
|
||||
<emphasis>binary cache</emphasis>, a directory containing compressed
|
||||
Nix archives (NARs) plus some metadata of the closure of the specified
|
||||
store paths. This directory can then be made available through a web
|
||||
server to other Nix installations, allowing them to skip building from
|
||||
source and instead download binaries from the cache
|
||||
automatically.</para>
|
||||
|
||||
<para><command>nix-push</command> performs the following actions.
|
||||
|
||||
<orderedlist>
|
||||
|
||||
<listitem><para>Each path in <replaceable>paths</replaceable> is
|
||||
realised (using <link
|
||||
linkend='rsec-nix-store-realise'><literal>nix-store
|
||||
--realise</literal></link>).</para></listitem>
|
||||
built (using <link
|
||||
linkend='rsec-nix-store-realise'><command>nix-store
|
||||
--realise</command></link>).</para></listitem>
|
||||
|
||||
<listitem><para>All paths in the closure of the store expressions
|
||||
stored in <replaceable>paths</replaceable> are determined (using
|
||||
<literal>nix-store --query --requisites
|
||||
--include-outputs</literal>). It should be noted that since the
|
||||
<option>--include-outputs</option> flag is used, you get a combined
|
||||
source/binary distribution.</para></listitem>
|
||||
<listitem><para>All paths in the closure of
|
||||
<replaceable>paths</replaceable> are determined (using
|
||||
<command>nix-store --query --requisites
|
||||
--include-outputs</command>). Note that since the
|
||||
<option>--include-outputs</option> flag is used, if
|
||||
<replaceable>paths</replaceable> includes a store derivation, you
|
||||
get a combined source/binary distribution (e.g., source tarballs
|
||||
will be included).</para></listitem>
|
||||
|
||||
<listitem><para>All store paths determined in the previous step are
|
||||
packaged and compressed into a <command>bzip</command>ped NAR
|
||||
archive (extension <filename>.nar.bz2</filename>).</para></listitem>
|
||||
packaged into a NAR (using <command>nix-store --dump</command>) and
|
||||
compressed using <command>xz</command> or <command>bzip2</command>.
|
||||
The resulting files have the extension <filename>.nar.xz</filename>
|
||||
or <filename>.nar.bz2</filename>. Also for each store path, Nix
|
||||
generates a file with extension <filename>.narinfo</filename>
|
||||
containing metadata such as the references, cryptographic hash and
|
||||
size of each path.</para></listitem>
|
||||
|
||||
<listitem><para>A <emphasis>manifest</emphasis> is created that
|
||||
contains information on the store paths, their eventual URLs in the
|
||||
cache, and cryptographic hashes of the contents of the NAR
|
||||
archives.</para></listitem>
|
||||
<listitem><para>Optionally, a single <emphasis>manifest</emphasis>
|
||||
file is created that contains the same metadata as the
|
||||
<filename>.narinfo</filename> files. This is for compatibility with
|
||||
Nix versions prior to 1.2 (see <command>nix-pull</command> for
|
||||
details).</para></listitem>
|
||||
|
||||
<listitem><para>Each store path is uploaded to the remote directory
|
||||
specified by <replaceable>archivesPutURL</replaceable>. HTTP PUT
|
||||
requests are used to do this. However, before a file
|
||||
<varname>x</varname> is uploaded to
|
||||
<literal><replaceable>archivesPutURL</replaceable>/</literal><varname>x</varname>,
|
||||
<command>nix-push</command> first determines whether this upload is
|
||||
unnecessary by issuing a HTTP HEAD request on
|
||||
<literal><replaceable>archivesGetURL</replaceable>/</literal><varname>x</varname>.
|
||||
This allows a cache to be shared between many partially overlapping
|
||||
<command>nix-push</command> invocations. (We use two URLs because
|
||||
the upload URL typically refers to a CGI script, while the download
|
||||
URL just refers to a file system directory on the
|
||||
server.)</para></listitem>
|
||||
<listitem><para>A file named <option>nix-cache-info</option> is
|
||||
placed in the destination directory. The existence of this file
|
||||
marks the directory as a binary cache.</para></listitem>
|
||||
|
||||
<listitem><para>The manifest is uploaded using an HTTP PUT request
|
||||
to <replaceable>manifestPutURL</replaceable>. The corresponding
|
||||
URL to download the manifest can then be used by
|
||||
<command>nix-pull</command>.</para></listitem>
|
||||
|
||||
</orderedlist>
|
||||
|
||||
</para>
|
||||
|
||||
<!--
|
||||
<para>TODO: <option>- -copy</option></para>
|
||||
-->
|
||||
|
||||
</refsection>
|
||||
|
||||
|
||||
<refsection><title>Options</title>
|
||||
|
||||
<variablelist>
|
||||
|
||||
<varlistentry><term><option>--dest</option> <replaceable>dest-dir</replaceable></term>
|
||||
|
||||
<listitem><para>Set the destination directory to
|
||||
<replaceable>dir</replaceable>, which is created if it does not
|
||||
exist. This flag is required.</para></listitem>
|
||||
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry><term><option>--bzip2</option></term>
|
||||
|
||||
<listitem><para>Compress NARs using <command>bzip2</command>
|
||||
instead of <command>xz -9</command>. The latter compresses about
|
||||
30% better on typical archives, decompresses about twice as fast,
|
||||
but compresses a lot slower and is not supported by Nix prior to
|
||||
version 1.2.</para></listitem>
|
||||
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry><term><option>--force</option></term>
|
||||
|
||||
<listitem><para>Overwrite <filename>.narinfo</filename> files if
|
||||
they already exist.</para></listitem>
|
||||
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry><term><option>--link</option></term>
|
||||
|
||||
<listitem><para>By default, NARs are generated in the Nix store
|
||||
and then copied to <replaceable>dest-dir</replaceable>. If this
|
||||
option is given, hard links are used instead. This only works if
|
||||
<replaceable>dest-dir</replaceable> is on the same filesystem as
|
||||
the Nix store.</para></listitem>
|
||||
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry><term><option>--manifest</option></term>
|
||||
|
||||
<listitem><para>Force the generation of a manifest suitable for
|
||||
use by <command>nix-pull</command>. The manifest is stored as
|
||||
<filename><replaceable>dest-dir</replaceable>/MANIFEST</filename>.</para></listitem>
|
||||
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry><term><option>--manifest-path</option> <replaceable>filename</replaceable></term>
|
||||
|
||||
<listitem><para>Like <option>--manifest</option>, but store the
|
||||
manifest in <replaceable>filename</replaceable>.</para></listitem>
|
||||
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry><term><option>--url-prefix</option> <replaceable>url</replaceable></term>
|
||||
|
||||
<listitem><para>Manifests are expected to contain the absolute
|
||||
URLs of NARs. For generating these URLs, the prefix
|
||||
<replaceable>url</replaceable> is used. It defaults to
|
||||
<uri>file://<replaceable>dest-dir</replaceable></uri>.</para></listitem>
|
||||
|
||||
</varlistentry>
|
||||
|
||||
</variablelist>
|
||||
|
||||
</refsection>
|
||||
|
||||
|
||||
<refsection><title>Examples</title>
|
||||
|
||||
<para>To upload files there typically is some CGI script on the server
|
||||
side. This script should be be protected with a password. The
|
||||
following example uploads the store paths resulting from building the
|
||||
Nix expressions in <filename>foo.nix</filename>, passing appropriate
|
||||
authentication information:
|
||||
|
||||
<screen>
|
||||
$ nix-push \
|
||||
http://foo@bar:server.domain/cgi-bin/upload.pl/cache \
|
||||
http://server.domain/cache \
|
||||
http://foo@bar:server.domain/cgi-bin/upload.pl/MANIFEST \
|
||||
$(nix-instantiate foo.nix)</screen>
|
||||
<para>To add the closure of Thunderbird to a binary cache:
|
||||
|
||||
This will push both sources and binaries (and any build-time
|
||||
dependencies used in the build, such as compilers).</para>
|
||||
|
||||
<para>If we just want to push binaries, not sources and build-time
|
||||
dependencies, we can do:
|
||||
|
||||
<screen>
|
||||
$ nix-push <replaceable>urls</replaceable> $(nix-store -r $(nix-instantiate foo.nix))</screen>
|
||||
|
||||
$ nix-push --dest /tmp/cache $(nix-build -A thunderbird)
|
||||
</screen>
|
||||
|
||||
Assuming that <filename>/tmp/cache</filename> is exported by a web
|
||||
server as <uri>http://example.org/cache</uri>, you can then use this
|
||||
cache on another machine to speed up the installation of Thunderbird:
|
||||
|
||||
<screen>
|
||||
$ nix-build -A thunderbird --option binary-caches http://example.org/cache
|
||||
</screen>
|
||||
|
||||
Alternatively, you could add <literal>binary-caches =
|
||||
http://example.org/cache</literal> to
|
||||
<filename>nix.conf</filename>.</para>
|
||||
|
||||
<para>To also include build-time dependencies (such as source
|
||||
tarballs):
|
||||
|
||||
<screen>
|
||||
$ nix-push --dest /tmp/cache $(nix-instantiate -A thunderbird)
|
||||
</screen>
|
||||
|
||||
</para>
|
||||
|
||||
<para>To generate a manifest suitable for <command>nix-pull</command>:
|
||||
|
||||
<screen>
|
||||
$ nix-push --dest /tmp/cache $(nix-build -A thunderbird) --manifest
|
||||
</screen>
|
||||
|
||||
On another machine you can then do:
|
||||
|
||||
<screen>
|
||||
$ nix-pull http://example.org/cache
|
||||
</screen>
|
||||
|
||||
to cause the binaries to be used by subsequent Nix operations.</para>
|
||||
|
||||
</refsection>
|
||||
|
||||
|
||||
|
||||
<refsection><title>Binary cache format and operation</title>
|
||||
|
||||
<para>A binary cache with URL <replaceable>url</replaceable> only
|
||||
denotes a valid binary cache if the file
|
||||
<uri><replaceable>url</replaceable>/nix-cache-info</uri> exists. If
|
||||
this file does not exist (or cannot be downloaded), the cache is
|
||||
ignored. If it does exist, it must be a text file containing cache
|
||||
properties. Here’s an example:
|
||||
|
||||
<screen>
|
||||
StoreDir: /nix/store
|
||||
WantMassQuery: 1
|
||||
Priority: 10
|
||||
</screen>
|
||||
|
||||
The properties that are currently supported are:
|
||||
|
||||
<variablelist>
|
||||
|
||||
<varlistentry><term><literal>StoreDir</literal></term>
|
||||
|
||||
<listitem><para>The path of the Nix store to which this binary
|
||||
cache applies. Binaries are not relocatable — a binary built for
|
||||
<filename>/nix/store</filename> won’t generally work in
|
||||
<filename>/home/alice/store</filename> — so to prevent binaries
|
||||
from being used in a wrong store, a binary cache is only used if
|
||||
its <literal>StoreDir</literal> matches the local Nix
|
||||
configuration. The default is
|
||||
<filename>/nix/store</filename>.</para></listitem>
|
||||
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry><term><literal>WantMassQuery</literal></term>
|
||||
|
||||
<listitem><para>Query operations such as <command>nix-env
|
||||
-qas</command> can cause thousands of cache queries, and thus
|
||||
thousands of HTTP requests, to determine which packages are
|
||||
available in binary form. While these requests are small, not
|
||||
every server may appreciate a potential onslaught of queries. If
|
||||
<literal>WantMassQuery</literal> is set to <literal>0</literal>
|
||||
(default), “mass queries” such as <command>nix-env -qas</command>
|
||||
will skip this cache. Thus a package may appear not to have a
|
||||
binary substitute. However, the binary will still be used when
|
||||
you actually install the package. If
|
||||
<literal>WantMassQuery</literal> is set to <literal>1</literal>,
|
||||
mass queries will use this cache.</para></listitem>
|
||||
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry><term><literal>Priority</literal></term>
|
||||
|
||||
<listitem><para>Each binary cache has a priority (defaulting to
|
||||
50). Binary caches are checked for binaries in order of ascending
|
||||
priority; thus a higher number denotes a lower priority. The
|
||||
binary cache <uri>http://nixos.org/binary-cache</uri> has priority
|
||||
40.</para></listitem>
|
||||
|
||||
</varlistentry>
|
||||
|
||||
</variablelist>
|
||||
|
||||
</para>
|
||||
|
||||
<para>Every time Nix needs to build some store path
|
||||
<replaceable>p</replaceable>, it will check each configured binary
|
||||
cache to see if it has a NAR file for <replaceable>p</replaceable>,
|
||||
until it finds one. If no cache has a NAR, Nix will fall back to
|
||||
building the path from source (if applicable). To see if a cache with
|
||||
URL <replaceable>url</replaceable> has a binary for
|
||||
<replaceable>p</replaceable>, Nix fetches
|
||||
<replaceable>url/h</replaceable>, where <replaceable>h</replaceable>
|
||||
is the hash part of <replaceable>p</replaceable>. Thus, if we have a
|
||||
cache <uri>http://nixos.org/binary-cache</uri> and we want to obtain
|
||||
the store path
|
||||
<screen>
|
||||
/nix/store/a8922c0h87iilxzzvwn2hmv8x210aqb9-glibc-2.7
|
||||
</screen>
|
||||
then Nix will attempt to fetch
|
||||
<screen>
|
||||
http://nixos.org/binary-cache/a8922c0h87iilxzzvwn2hmv8x210aqb9.narinfo
|
||||
</screen>
|
||||
(Commands such as <command>nix-env -qas</command> will issue an HTTP
|
||||
HEAD request, since it only needs to know if the
|
||||
<filename>.narinfo</filename> file exists.) The
|
||||
<filename>.narinfo</filename> file is a simple text file that looks
|
||||
like this:
|
||||
|
||||
<screen>
|
||||
StorePath: /nix/store/a8922c0h87iilxzzvwn2hmv8x210aqb9-glibc-2.7
|
||||
URL: nar/0zzjpdz46mdn74v09m053yczlz4am038g8r74iy8w43gx8801h70.nar.bz2
|
||||
Compression: bzip2
|
||||
FileHash: sha256:0zzjpdz46mdn74v09m053yczlz4am038g8r74iy8w43gx8801h70
|
||||
FileSize: 24473768
|
||||
NarHash: sha256:0s491y1h9hxj5ghiizlxk7ax6jwbha00zwn7lpyd5xg5bhf60vzg
|
||||
NarSize: 109521136
|
||||
References: 2ma2k0ys8knh4an48n28vigcmc2z8773-linux-headers-2.6.23.16 ...
|
||||
Deriver: 7akyyc87ka32xwmqza9dvyg5pwx3j212-glibc-2.7.drv
|
||||
</screen>
|
||||
|
||||
The fields are as follows:
|
||||
|
||||
<variablelist>
|
||||
|
||||
<varlistentry><term><literal>StorePath</literal></term>
|
||||
|
||||
<listitem><para>The full store path, including the name part
|
||||
(e.g., <literal>glibc-2.7</literal>). It must match the
|
||||
requested store path.</para></listitem>
|
||||
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry><term><literal>URL</literal></term>
|
||||
|
||||
<listitem><para>The URL of the NAR, relative to the binary cache
|
||||
URL.</para></listitem>
|
||||
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry><term><literal>Compression</literal></term>
|
||||
|
||||
<listitem><para>The compression method; either
|
||||
<literal>xz</literal> or
|
||||
<literal>bzip2</literal>.</para></listitem>
|
||||
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry><term><literal>FileHash</literal></term>
|
||||
|
||||
<listitem><para>The SHA-256 hash of the compressed
|
||||
NAR.</para></listitem>
|
||||
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry><term><literal>FileSize</literal></term>
|
||||
|
||||
<listitem><para>The size of the compressed NAR.</para></listitem>
|
||||
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry><term><literal>NarHash</literal></term>
|
||||
|
||||
<listitem><para>The SHA-256 hash of the uncompressed NAR. This is
|
||||
equal to the hash of the store path as returned by
|
||||
<command>nix-store -q --hash
|
||||
<replaceable>p</replaceable></command>.</para></listitem>
|
||||
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry><term><literal>NarSize</literal></term>
|
||||
|
||||
<listitem><para>The size of the uncompressed NAR.</para></listitem>
|
||||
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry><term><literal>References</literal></term>
|
||||
|
||||
<listitem><para>The references of the store path, without the Nix
|
||||
store prefix.</para></listitem>
|
||||
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry><term><literal>Deriver</literal></term>
|
||||
|
||||
<listitem><para>The deriver of the store path, without the Nix
|
||||
store prefix. This field is optional.</para></listitem>
|
||||
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry><term><literal>System</literal></term>
|
||||
|
||||
<listitem><para>The Nix platform type of this binary, if known.
|
||||
This field is optional.</para></listitem>
|
||||
|
||||
</varlistentry>
|
||||
|
||||
</variablelist>
|
||||
|
||||
</para>
|
||||
|
||||
<para>Thus, in our example, after recursively ensuring that the
|
||||
references exist (e.g.,
|
||||
<filename>/nix/store/2ma2k0ys8knh4an48n28vigcmc2z8773-linux-headers-2.6.23.16</filename>),
|
||||
Nix will fetch <screen>
|
||||
http://nixos.org/binary-cache/nar/0zzjpdz46mdn74v09m053yczlz4am038g8r74iy8w43gx8801h70.nar.bz2
|
||||
</screen> and decompress and unpack it to
|
||||
<filename>/nix/store/a8922c0h87iilxzzvwn2hmv8x210aqb9-glibc-2.7</filename>.</para>
|
||||
|
||||
</refsection>
|
||||
|
||||
|
||||
</refentry>
|
||||
|
||||
@@ -174,10 +174,26 @@ the specified store paths. Realisation is a somewhat overloaded term:
|
||||
output. (For non-derivations argument, the argument itself is
|
||||
printed.)</para>
|
||||
|
||||
<para>If the <option>--dry-run</option> option is used, then
|
||||
<command>nix-store</command> will print on standard error a
|
||||
description of what packages would be built or downloaded, and then
|
||||
quit.</para>
|
||||
<para>The following flags are available:</para>
|
||||
|
||||
<variablelist>
|
||||
|
||||
<varlistentry><term><option>--dry-run</option></term>
|
||||
|
||||
<listitem><para>Print on standard error a description of what
|
||||
packages would be built or downloaded, without actually performing
|
||||
the operation.</para></listitem>
|
||||
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry><term><option>--ignore-unknown</option></term>
|
||||
|
||||
<listitem><para>If a non-derivation path does not have a
|
||||
substitute, then silently ignore it.</para></listitem>
|
||||
|
||||
</varlistentry>
|
||||
|
||||
</variablelist>
|
||||
|
||||
</refsection>
|
||||
|
||||
@@ -773,6 +789,7 @@ $ nix-store --add ./foo.c
|
||||
<command>nix-store</command>
|
||||
<arg choice='plain'><option>--verify</option></arg>
|
||||
<arg><option>--check-contents</option></arg>
|
||||
<arg><option>--repair</option></arg>
|
||||
</cmdsynopsis>
|
||||
</refsection>
|
||||
|
||||
@@ -785,7 +802,7 @@ automatically repaired. Inconsistencies are generally the result of
|
||||
the Nix store or database being modified by non-Nix tools, or of bugs
|
||||
in Nix itself.</para>
|
||||
|
||||
<para>There is one option:
|
||||
<para>This operation has the following options:
|
||||
|
||||
<variablelist>
|
||||
|
||||
@@ -800,6 +817,16 @@ in Nix itself.</para>
|
||||
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry><term><option>--repair</option></term>
|
||||
|
||||
<listitem><para>If any valid path is missing from the store, or
|
||||
(if <option>--check-contents</option> is given) the contents of a
|
||||
valid path has been modified, then try to repair the path by
|
||||
redownloading it. See <command>nix-store --repair-path</command>
|
||||
for details.</para></listitem>
|
||||
|
||||
</varlistentry>
|
||||
|
||||
</variablelist>
|
||||
|
||||
</para>
|
||||
@@ -848,6 +875,52 @@ $ nix-store --verify-path $(nix-store -qR $(which svn))
|
||||
</refsection>
|
||||
|
||||
|
||||
<!--######################################################################-->
|
||||
|
||||
<refsection><title>Operation <option>--repair-path</option></title>
|
||||
|
||||
<refsection>
|
||||
<title>Synopsis</title>
|
||||
<cmdsynopsis>
|
||||
<command>nix-store</command>
|
||||
<arg choice='plain'><option>--repair-path</option></arg>
|
||||
<arg choice='plain' rep='repeat'><replaceable>paths</replaceable></arg>
|
||||
</cmdsynopsis>
|
||||
</refsection>
|
||||
|
||||
<refsection><title>Description</title>
|
||||
|
||||
<para>The operation <option>--repair-path</option> attempts to
|
||||
“repair” the specified paths by redownloading them using the available
|
||||
substituters. If no substitutes are available, then repair is not
|
||||
possible.</para>
|
||||
|
||||
<warning><para>During repair, there is a very small time window during
|
||||
which the old path (if it exists) is moved out of the way and replaced
|
||||
with the new path. If repair is interrupted in between, then the
|
||||
system may be left in a broken state (e.g., if the path contains a
|
||||
critical system component like the GNU C Library).</para></warning>
|
||||
|
||||
</refsection>
|
||||
|
||||
<refsection><title>Example</title>
|
||||
|
||||
<screen>
|
||||
$ nix-store --verify-path /nix/store/dj7a81wsm1ijwwpkks3725661h3263p5-glibc-2.13
|
||||
path `/nix/store/dj7a81wsm1ijwwpkks3725661h3263p5-glibc-2.13' was modified!
|
||||
expected hash `2db57715ae90b7e31ff1f2ecb8c12ec1cc43da920efcbe3b22763f36a1861588',
|
||||
got `481c5aa5483ebc97c20457bb8bca24deea56550d3985cda0027f67fe54b808e4'
|
||||
|
||||
$ nix-store --repair-path /nix/store/dj7a81wsm1ijwwpkks3725661h3263p5-glibc-2.13
|
||||
fetching path `/nix/store/d7a81wsm1ijwwpkks3725661h3263p5-glibc-2.13'...
|
||||
…
|
||||
</screen>
|
||||
|
||||
</refsection>
|
||||
|
||||
</refsection>
|
||||
|
||||
|
||||
<!--######################################################################-->
|
||||
|
||||
<refsection xml:id='refsec-nix-store-dump'><title>Operation <option>--dump</option></title>
|
||||
@@ -1246,8 +1319,8 @@ paths.</para>
|
||||
<refsection><title>Example</title>
|
||||
|
||||
<screen>
|
||||
$ nix-store --clear-failed-path /nix/store/000zi5dcla86l92jn1g997jb06sidm7x-perl-PerlMagick-6.59
|
||||
$ nix-store --clear-failed-path *
|
||||
$ nix-store --clear-failed-paths /nix/store/000zi5dcla86l92jn1g997jb06sidm7x-perl-PerlMagick-6.59
|
||||
$ nix-store --clear-failed-paths *
|
||||
</screen>
|
||||
|
||||
</refsection>
|
||||
|
||||
@@ -37,6 +37,11 @@
|
||||
<option>-I</option>
|
||||
<replaceable>path</replaceable>
|
||||
</arg>
|
||||
<arg>
|
||||
<option>--option</option>
|
||||
<replaceable>name</replaceable>
|
||||
<replaceable>value</replaceable>
|
||||
</arg>
|
||||
<sbr />
|
||||
|
||||
</nop>
|
||||
|
||||
@@ -337,20 +337,42 @@
|
||||
|
||||
<listitem><para>Causes Nix to print out a stack trace in case of Nix
|
||||
expression evaluation errors.</para></listitem>
|
||||
|
||||
|
||||
</varlistentry>
|
||||
|
||||
|
||||
<varlistentry><term><option>-I</option> <replaceable>path</replaceable></term>
|
||||
|
||||
<listitem><para>Add a path to the Nix expression search path. See
|
||||
the <envar>NIX_PATH</envar> environment variable for details. Paths
|
||||
added through <option>-I</option> take precedence over
|
||||
<envar>NIX_PATH</envar>.</para></listitem>
|
||||
<listitem><para>Add a path to the Nix expression search path. This
|
||||
option may be given multiple times. See the <envar>NIX_PATH</envar>
|
||||
environment variable for information on the semantics of the Nix
|
||||
search path. Paths added through <option>-I</option> take
|
||||
precedence over <envar>NIX_PATH</envar>.</para></listitem>
|
||||
|
||||
</varlistentry>
|
||||
|
||||
|
||||
<varlistentry><term><option>--option</option> <replaceable>name</replaceable> <replaceable>value</replaceable></term>
|
||||
|
||||
<listitem><para>Set the Nix configuration option
|
||||
<replaceable>name</replaceable> to <replaceable>value</replaceable>.
|
||||
This overrides settings in the Nix configuration file (see
|
||||
<citerefentry><refentrytitle>nix.conf</refentrytitle><manvolnum>5</manvolnum></citerefentry>).</para></listitem>
|
||||
|
||||
</varlistentry>
|
||||
|
||||
|
||||
<varlistentry><term><option>--repair</option></term>
|
||||
|
||||
<listitem><para>Fix corrupted or missing store paths by
|
||||
redownloading or rebuilding them. Note that this is slow because it
|
||||
requires computing a cryptographic hash of the contents of every
|
||||
path in the closure of the build. Also note the warning under
|
||||
<command>nix-store --repair-path</command>.</para></listitem>
|
||||
|
||||
</varlistentry>
|
||||
|
||||
|
||||
</variablelist>
|
||||
|
||||
|
||||
|
||||
@@ -175,21 +175,6 @@ upgrading `coreutils-5.0' to `coreutils-5.2.1'</screen>
|
||||
|
||||
</para>
|
||||
|
||||
<para>If you grow bored of specifying the Nix expressions using
|
||||
<parameter>-f</parameter> all the time, you can set a default
|
||||
location:
|
||||
|
||||
<screen>
|
||||
$ nix-env -I nixpkgs-<replaceable>version</replaceable></screen>
|
||||
|
||||
After this you can just say, for instance, <literal>nix-env -u
|
||||
'*'</literal>.<footnote><para>Setting a default using
|
||||
<parameter>-I</parameter> currently clashes with using Nix channels,
|
||||
since <literal>nix-channel --update</literal> calls <literal>nix-env
|
||||
-I</literal> to set the default to the Nix expressions it downloaded
|
||||
from the channel, replacing whatever default you had
|
||||
set.</para></footnote></para>
|
||||
|
||||
</section>
|
||||
|
||||
|
||||
@@ -458,7 +443,7 @@ URL.</para>
|
||||
<command>nix-channel --add</command>, e.g.,
|
||||
|
||||
<screen>
|
||||
$ nix-channel --add http://nixos.org/releases/nixpkgs/channels/nixpkgs-unstable</screen>
|
||||
$ nix-channel --add http://nixos.org/channels/nixpkgs-unstable</screen>
|
||||
|
||||
subscribes you to a channel that always contains that latest version
|
||||
of the Nix Packages collection. (Instead of
|
||||
@@ -553,7 +538,7 @@ dependencies) to a file, and then unpack that file into another Nix
|
||||
store. For example,
|
||||
|
||||
<screen>
|
||||
$ nix-store --export $(type -p firefox) > firefox.closure</screen>
|
||||
$ nix-store --export $(nix-store -qR $(type -p firefox)) > firefox.closure</screen>
|
||||
|
||||
writes the closure of Firefox to a file. You can then copy this file
|
||||
to another machine and install the closure:
|
||||
@@ -567,7 +552,7 @@ another command, e.g. to copy and install a closure directly to/on
|
||||
another machine:
|
||||
|
||||
<screen>
|
||||
$ nix-store --export $(type -p firefox) | bzip2 | \
|
||||
$ nix-store --export $(nix-store -qR $(type -p firefox)) | bzip2 | \
|
||||
ssh alice@itchy.example.org "bunzip2 | nix-store --import"</screen>
|
||||
|
||||
But note that <command>nix-copy-closure</command> is generally more
|
||||
@@ -598,7 +583,7 @@ $ nix-env -i firefox</screen>
|
||||
and Nix has to build a path that it sees is already present in
|
||||
<filename>/mnt/nix</filename>, then it will just copy from there
|
||||
instead of building it from source.</para>
|
||||
|
||||
|
||||
|
||||
</section>
|
||||
|
||||
|
||||
@@ -41,7 +41,7 @@ file).</para></listitem>
|
||||
<listitem><para>Subscribe to the Nix Packages channel.
|
||||
|
||||
<screen>
|
||||
$ nix-channel --add http://nixos.org/releases/nixpkgs/channels/nixpkgs-unstable</screen>
|
||||
$ nix-channel --add http://nixos.org/channels/nixpkgs-unstable</screen>
|
||||
|
||||
</para></listitem>
|
||||
|
||||
|
||||
@@ -6,6 +6,235 @@
|
||||
|
||||
|
||||
|
||||
<!--==================================================================-->
|
||||
|
||||
<section xml:id="ssec-relnotes-1.5.1"><title>Release 1.5.1 (February 28, 2013)</title>
|
||||
|
||||
<para>The bug fix to the bug fix had a bug itself, of course. But
|
||||
this time it will work for sure!</para>
|
||||
|
||||
</section>
|
||||
|
||||
|
||||
<!--==================================================================-->
|
||||
|
||||
<section xml:id="ssec-relnotes-1.5"><title>Release 1.5 (February 27, 2013)</title>
|
||||
|
||||
<para>This is a brown paper bag release to fix a regression introduced
|
||||
by the hard link security fix in 1.4.</para>
|
||||
|
||||
</section>
|
||||
|
||||
|
||||
<!--==================================================================-->
|
||||
|
||||
<section xml:id="ssec-relnotes-1.4"><title>Release 1.4 (February 26, 2013)</title>
|
||||
|
||||
<para>This release fixes a security bug in multi-user operation. It
|
||||
was possible for derivations to cause the mode of files outside of the
|
||||
Nix store to be changed to 444 (read-only but world-readable) by
|
||||
creating hard links to those files (<link
|
||||
xlink:href="https://github.com/NixOS/nix/commit/5526a282b5b44e9296e61e07d7d2626a79141ac4">details</link>).</para>
|
||||
|
||||
<para>There are also the following improvements:</para>
|
||||
|
||||
<itemizedlist>
|
||||
|
||||
<listitem><para>New built-in function:
|
||||
<function>builtins.hashString</function>.</para></listitem>
|
||||
|
||||
<listitem><para>Build logs are now stored in
|
||||
<filename>/nix/var/log/nix/drvs/<replaceable>XX</replaceable>/</filename>,
|
||||
where <replaceable>XX</replaceable> is the first two characters of
|
||||
the derivation. This is useful on machines that keep a lot of build
|
||||
logs (such as Hydra servers).</para></listitem>
|
||||
|
||||
<listitem><para>The function <function>corepkgs/fetchurl</function>
|
||||
can now make the downloaded file executable. This will allow
|
||||
getting rid of all bootstrap binaries in the Nixpkgs source
|
||||
tree.</para></listitem>
|
||||
|
||||
<listitem><para>Language change: The expression <literal>"${./path}
|
||||
..."</literal> now evaluates to a string instead of a
|
||||
path.</para></listitem>
|
||||
|
||||
</itemizedlist>
|
||||
|
||||
</section>
|
||||
|
||||
|
||||
<!--==================================================================-->
|
||||
|
||||
<section xml:id="ssec-relnotes-1.3"><title>Release 1.3 (January 4, 2013)</title>
|
||||
|
||||
<para>This is primarily a bug fix release. When this version is first
|
||||
run on Linux, it removes any immutable bits from the Nix store and
|
||||
increases the schema version of the Nix store. (The previous release
|
||||
removed support for setting the immutable bit; this release clears any
|
||||
remaining immutable bits to make certain operations more
|
||||
efficient.)</para>
|
||||
|
||||
<para>This release has contributions from Eelco Dolstra and Stuart
|
||||
Pernsteiner.</para>
|
||||
|
||||
</section>
|
||||
|
||||
|
||||
<!--==================================================================-->
|
||||
|
||||
<section xml:id="ssec-relnotes-1.2"><title>Release 1.2 (December 6, 2012)</title>
|
||||
|
||||
<para>This release has the following improvements and changes:</para>
|
||||
|
||||
<itemizedlist>
|
||||
|
||||
<listitem>
|
||||
<para>Nix has a new binary substituter mechanism: the
|
||||
<emphasis>binary cache</emphasis>. A binary cache contains
|
||||
pre-built binaries of Nix packages. Whenever Nix wants to build a
|
||||
missing Nix store path, it will check a set of binary caches to
|
||||
see if any of them has a pre-built binary of that path. The
|
||||
configuration setting <option>binary-caches</option> contains a
|
||||
list of URLs of binary caches. For instance, doing
|
||||
<screen>
|
||||
$ nix-env -i thunderbird --option binary-caches http://nixos.org/binary-cache
|
||||
</screen>
|
||||
will install Thunderbird and its dependencies, using the available
|
||||
pre-built binaries in <uri>http://nixos.org/binary-cache</uri>.
|
||||
The main advantage over the old “manifest”-based method of getting
|
||||
pre-built binaries is that you don’t have to worry about your
|
||||
manifest being in sync with the Nix expressions you’re installing
|
||||
from; i.e., you don’t need to run <command>nix-pull</command> to
|
||||
update your manifest. It’s also more scalable because you don’t
|
||||
need to redownload a giant manifest file every time.
|
||||
</para>
|
||||
|
||||
<para>A Nix channel can provide a binary cache URL that will be
|
||||
used automatically if you subscribe to that channel. If you use
|
||||
the Nixpkgs or NixOS channels
|
||||
(<uri>http://nixos.org/channels</uri>) you automatically get the
|
||||
cache <uri>http://nixos.org/binary-cache</uri>.</para>
|
||||
|
||||
<para>Binary caches are created using <command>nix-push</command>.
|
||||
For details on the operation and format of binary caches, see the
|
||||
<command>nix-push</command> manpage. More details are provided in
|
||||
<link xlink:href="http://lists.science.uu.nl/pipermail/nix-dev/2012-September/009826.html">this
|
||||
nix-dev posting</link>.</para>
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
<para>Multiple output support should now be usable. A derivation
|
||||
can declare that it wants to produce multiple store paths by
|
||||
saying something like
|
||||
<programlisting>
|
||||
outputs = [ "lib" "headers" "doc" ];
|
||||
</programlisting>
|
||||
This will cause Nix to pass the intended store path of each output
|
||||
to the builder through the environment variables
|
||||
<literal>lib</literal>, <literal>headers</literal> and
|
||||
<literal>doc</literal>. Other packages can refer to a specific
|
||||
output by referring to
|
||||
<literal><replaceable>pkg</replaceable>.<replaceable>output</replaceable></literal>,
|
||||
e.g.
|
||||
<programlisting>
|
||||
buildInputs = [ pkg.lib pkg.headers ];
|
||||
</programlisting>
|
||||
If you install a package with multiple outputs using
|
||||
<command>nix-env</command>, each output path will be symlinked
|
||||
into the user environment.</para>
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
<para>Dashes are now valid as part of identifiers and attribute
|
||||
names.</para>
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
<para>The new operation <command>nix-store --repair-path</command>
|
||||
allows corrupted or missing store paths to be repaired by
|
||||
redownloading them. <command>nix-store --verify --check-contents
|
||||
--repair</command> will scan and repair all paths in the Nix
|
||||
store. Similarly, <command>nix-env</command>,
|
||||
<command>nix-build</command>, <command>nix-instantiate</command>
|
||||
and <command>nix-store --realise</command> have a
|
||||
<option>--repair</option> flag to detect and fix bad paths by
|
||||
rebuilding or redownloading them.</para>
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
<para>Nix no longer sets the immutable bit on files in the Nix
|
||||
store. Instead, the recommended way to guard the Nix store
|
||||
against accidental modification on Linux is to make it a read-only
|
||||
bind mount, like this:
|
||||
|
||||
<screen>
|
||||
$ mount --bind /nix/store /nix/store
|
||||
$ mount -o remount,ro,bind /nix/store
|
||||
</screen>
|
||||
|
||||
Nix will automatically make <filename>/nix/store</filename>
|
||||
writable as needed (using a private mount namespace) to allow
|
||||
modifications.</para>
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
<para>Store optimisation (replacing identical files in the store
|
||||
with hard links) can now be done automatically every time a path
|
||||
is added to the store. This is enabled by setting the
|
||||
configuration option <literal>auto-optimise-store</literal> to
|
||||
<literal>true</literal> (disabled by default).</para>
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
<para>Nix now supports <command>xz</command> compression for NARs
|
||||
in addition to <command>bzip2</command>. It compresses about 30%
|
||||
better on typical archives and decompresses about twice as
|
||||
fast.</para>
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
<para>Basic Nix expression evaluation profiling: setting the
|
||||
environment variable <envar>NIX_COUNT_CALLS</envar> to
|
||||
<literal>1</literal> will cause Nix to print how many times each
|
||||
primop or function was executed.</para>
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
<para>New primops: <varname>concatLists</varname>,
|
||||
<varname>elem</varname>, <varname>elemAt</varname> and
|
||||
<varname>filter</varname>.</para>
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
<para>The command <command>nix-copy-closure</command> has a new
|
||||
flag <option>--use-substitutes</option> (<option>-s</option>) to
|
||||
download missing paths on the target machine using the substitute
|
||||
mechanism.</para>
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
<para>The command <command>nix-worker</command> has been renamed
|
||||
to <command>nix-daemon</command>. Support for running the Nix
|
||||
worker in “slave” mode has been removed.</para>
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
<para>The <option>--help</option> flag of every Nix command now
|
||||
invokes <command>man</command>.</para>
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
<para>Chroot builds are now supported on systemd machines.</para>
|
||||
</listitem>
|
||||
|
||||
</itemizedlist>
|
||||
|
||||
<para>This release has contributions from Eelco Dolstra, Florian
|
||||
Friesdorf, Mats Erik Andersson and Shea Levy.</para>
|
||||
|
||||
</section>
|
||||
|
||||
|
||||
<!--==================================================================-->
|
||||
|
||||
<section xml:id="ssec-relnotes-1.1"><title>Release 1.1 (July 18, 2012)</title>
|
||||
@@ -94,6 +323,11 @@
|
||||
<citerefentry><refentrytitle>nix.conf</refentrytitle><manvolnum>5</manvolnum></citerefentry>.</para>
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
<para>When using the Nix daemon, the <option>-s</option> flag in
|
||||
<command>nix-env -qa</command> is now much faster.</para>
|
||||
</listitem>
|
||||
|
||||
</itemizedlist>
|
||||
|
||||
</section>
|
||||
|
||||
@@ -106,7 +106,7 @@ the single Nix expression in that directory
|
||||
here, the entire remainder of the file is the body of the
|
||||
function; when given the required arguments, the body should
|
||||
describe how to build an instance of the Hello package.</para>
|
||||
|
||||
|
||||
</callout>
|
||||
|
||||
<callout arearefs='ex-hello-nix-co-2'>
|
||||
@@ -190,7 +190,7 @@ perl = perl;</programlisting>
|
||||
with the same name happen to be in scope.</para>
|
||||
|
||||
</callout>
|
||||
|
||||
|
||||
</calloutlist>
|
||||
|
||||
</para>
|
||||
@@ -301,7 +301,7 @@ steps:</para>
|
||||
(<literal>make install</literal>).</para>
|
||||
|
||||
</callout>
|
||||
|
||||
|
||||
</calloutlist>
|
||||
|
||||
<para>If you are wondering about the absence of error checking on the
|
||||
@@ -321,7 +321,7 @@ error check.</para>
|
||||
...
|
||||
|
||||
rec { <co xml:id='ex-hello-composition-co-1' />
|
||||
|
||||
|
||||
hello = import ../applications/misc/hello/ex-1 <co xml:id='ex-hello-composition-co-2' /> { <co xml:id='ex-hello-composition-co-3' />
|
||||
inherit fetchurl stdenv perl;
|
||||
};
|
||||
@@ -330,10 +330,10 @@ rec { <co xml:id='ex-hello-composition-co-1' />
|
||||
inherit fetchurl stdenv;
|
||||
};
|
||||
|
||||
fetchurl = import ../build-support/fetchurl {
|
||||
fetchurl = import ../build-support/fetchurl {
|
||||
inherit stdenv; ...
|
||||
};
|
||||
|
||||
|
||||
stdenv = ...;
|
||||
|
||||
}
|
||||
@@ -476,8 +476,8 @@ that the path denoted by <envar>out</envar> is now
|
||||
will see that the path is already valid and finish immediately. If a
|
||||
build fails, either because it returns a non-zero exit code, because
|
||||
Nix or the builder are killed, or because the machine crashes, then
|
||||
the output path will not be registered as valid. If you try to build
|
||||
the derivation again, Nix will remove the output path if it exists
|
||||
the output paths will not be registered as valid. If you try to build
|
||||
the derivation again, Nix will remove the output paths if they exist
|
||||
(e.g., because the builder died half-way through <literal>make
|
||||
install</literal>) and try again. Note that there is no
|
||||
<quote>negative caching</quote>: Nix doesn't remember that a build
|
||||
@@ -543,7 +543,7 @@ genericBuild <co xml:id='ex-hello-builder2-co-3' /></programlisting>
|
||||
<calloutlist>
|
||||
|
||||
<callout arearefs='ex-hello-builder2-co-1'>
|
||||
|
||||
|
||||
<para>The <envar>buildInputs</envar> variable tells
|
||||
<filename>setup</filename> to use the indicated packages as
|
||||
<quote>inputs</quote>. This means that if a package provides a
|
||||
@@ -568,7 +568,7 @@ genericBuild <co xml:id='ex-hello-builder2-co-3' /></programlisting>
|
||||
the file <literal>$stdenv/setup</literal>.</para>
|
||||
|
||||
</callout>
|
||||
|
||||
|
||||
<callout arearefs='ex-hello-builder2-co-3'>
|
||||
|
||||
<para>The final step calls the shell function
|
||||
@@ -580,7 +580,7 @@ genericBuild <co xml:id='ex-hello-builder2-co-3' /></programlisting>
|
||||
see <xref linkend='sec-standard-environment' />.</para>
|
||||
|
||||
</callout>
|
||||
|
||||
|
||||
</calloutlist>
|
||||
|
||||
<para>Discerning readers will note that the
|
||||
@@ -704,7 +704,7 @@ configureFlags = "
|
||||
the third line is indented three spaces. Thus, two spaces are
|
||||
stripped from each line, so the resulting string is
|
||||
|
||||
<programlisting>
|
||||
<programlisting>
|
||||
"This is the first line.\nThis is the second line.\n This is the third line.\n"</programlisting>
|
||||
|
||||
</para>
|
||||
@@ -726,7 +726,7 @@ configureFlags = "
|
||||
linefeed, carriage-return and tab characters can be writted as
|
||||
<literal>''\n</literal>, <literal>''\r</literal>,
|
||||
<literal>''\t</literal>.</para>
|
||||
|
||||
|
||||
<para>Indented strings are primarily useful in that they allow
|
||||
multi-line string literals to follow the indentation of the
|
||||
enclosing Nix expression, and that less escaping is typically
|
||||
@@ -745,7 +745,7 @@ stdenv.mkDerivation {
|
||||
${if enableBar then "cp bar $out/bin" else ""}
|
||||
'';
|
||||
<replaceable>...</replaceable>
|
||||
}
|
||||
}
|
||||
</programlisting>
|
||||
|
||||
</para>
|
||||
@@ -781,7 +781,7 @@ stdenv.mkDerivation {
|
||||
<listitem><para><emphasis>Booleans</emphasis> with values
|
||||
<literal>true</literal> and
|
||||
<literal>false</literal>.</para></listitem>
|
||||
|
||||
|
||||
</itemizedlist>
|
||||
|
||||
</para>
|
||||
@@ -931,7 +931,7 @@ propagate attributes). This can be shortened using the
|
||||
<literal>inherit</literal> keyword. For instance,
|
||||
|
||||
<programlisting>
|
||||
let x = 123; in
|
||||
let x = 123; in
|
||||
{ inherit x;
|
||||
y = 456;
|
||||
}</programlisting>
|
||||
@@ -982,7 +982,7 @@ argument. There are three kinds of patterns:</para>
|
||||
|
||||
<itemizedlist>
|
||||
|
||||
|
||||
|
||||
<listitem><para>If a pattern is a single identifier, then the
|
||||
function matches any argument. Example:
|
||||
|
||||
@@ -1002,7 +1002,7 @@ map (concat "foo") [ "bar" "bla" "abc" ]</programlisting>
|
||||
evaluates to <literal>[ "foobar" "foobla"
|
||||
"fooabc" ]</literal>.</para></listitem>
|
||||
|
||||
|
||||
|
||||
<listitem><para>An <emphasis>attribute set pattern</emphasis> of the
|
||||
form <literal>{ name1, name2, …, nameN }</literal>
|
||||
matches an attribute set containing the listed attributes, and binds
|
||||
@@ -1016,7 +1016,7 @@ map (concat "foo") [ "bar" "bla" "abc" ]</programlisting>
|
||||
<varname>x</varname>, <varname>y</varname> and
|
||||
<varname>z</varname>. No other attributes are allowed. If you want
|
||||
to allow additional arguments, you can use an ellipsis
|
||||
(<literal>...</literal>):
|
||||
(<literal>...</literal>):
|
||||
|
||||
<programlisting>
|
||||
{ x, y, z, ... }: z + y + x</programlisting>
|
||||
@@ -1164,7 +1164,7 @@ used in the Nix expression for Subversion.</para>
|
||||
</calloutlist>
|
||||
|
||||
</simplesect>
|
||||
|
||||
|
||||
|
||||
|
||||
<simplesect><title>With-expressions</title>
|
||||
@@ -1343,7 +1343,7 @@ set, the attributes of which specify the inputs of the build.</para>
|
||||
<listitem><para>There must be an attribute named
|
||||
<varname>name</varname> whose value must be a string. This is used
|
||||
as a symbolic name for the package by <command>nix-env</command>,
|
||||
and it is appended to the hash in the output path of the
|
||||
and it is appended to the output paths of the
|
||||
derivation.</para></listitem>
|
||||
|
||||
<listitem><para>There must be an attribute named
|
||||
@@ -1358,7 +1358,7 @@ set, the attributes of which specify the inputs of the build.</para>
|
||||
|
||||
<itemizedlist>
|
||||
|
||||
<listitem><para>Strings, URIs, and integers are just passed
|
||||
<listitem><para>Strings and integers are just passed
|
||||
verbatim.</para></listitem>
|
||||
|
||||
<listitem><para>A <emphasis>path</emphasis> (e.g.,
|
||||
@@ -1369,8 +1369,8 @@ set, the attributes of which specify the inputs of the build.</para>
|
||||
should reside in the Nix store.</para></listitem>
|
||||
|
||||
<listitem><para>A <emphasis>derivation</emphasis> causes that
|
||||
derivation to be built prior to the present derivation; the
|
||||
output path is put in the environment
|
||||
derivation to be built prior to the present derivation; its
|
||||
default output path is put in the environment
|
||||
variable.</para></listitem>
|
||||
|
||||
<listitem><para>Lists of the previous types are also allowed.
|
||||
@@ -1389,14 +1389,48 @@ set, the attributes of which specify the inputs of the build.</para>
|
||||
specifies command-line arguments to be passed to the builder. It
|
||||
should be a list.</para></listitem>
|
||||
|
||||
<listitem><para>The optional attribute <varname>outputs</varname>
|
||||
specifies a list of symbolic outputs of the derivation. By default,
|
||||
a derivation produces a single output path, denoted as
|
||||
<literal>out</literal>. However, derivations can produce multiple
|
||||
output paths. This is useful because it allows outputs to be
|
||||
downloaded or garbage-collected separately. For instance, imagine a
|
||||
library package that provides a dynamic library, header files, and
|
||||
documentation. A program that links against the library doesn’t
|
||||
need the header files and documentation at runtime, and it doesn’t
|
||||
need the documentation at build time. Thus, the library package
|
||||
could specify:
|
||||
<programlisting>
|
||||
outputs = [ "lib" "headers" "doc" ];
|
||||
</programlisting>
|
||||
This will cause Nix to pass environment variables
|
||||
<literal>lib</literal>, <literal>headers</literal> and
|
||||
<literal>doc</literal> to the builder containing the intended store
|
||||
paths of each output. The builder would typically do something like
|
||||
<programlisting>
|
||||
./configure --libdir=$lib/lib --includedir=$headers/include --docdir=$doc/share/doc
|
||||
</programlisting>
|
||||
for an Autoconf-style package. You can refer to each output of a
|
||||
derivation by selecting it as an attribute, e.g.
|
||||
<programlisting>
|
||||
buildInputs = [ pkg.lib pkg.headers ];
|
||||
</programlisting>
|
||||
The first element of <varname>output</varname> determines the
|
||||
<emphasis>default output</emphasis>. Thus, you could also write
|
||||
<programlisting>
|
||||
buildInputs = [ pkg pkg.headers ];
|
||||
</programlisting>
|
||||
since <literal>pkg</literal> is equivalent to
|
||||
<literal>pkg.lib</literal>.</para></listitem>
|
||||
|
||||
</itemizedlist>
|
||||
|
||||
<para>(Note that <function>mkDerivation</function> in the standard
|
||||
<para>The function <function>mkDerivation</function> in the standard
|
||||
environment is a wrapper around <function>derivation</function> that
|
||||
adds a default value for <varname>system</varname> and always uses
|
||||
Bash as the builder, to which the supplied builder is passed as a
|
||||
command-line argument. See <xref linkend='sec-standard-environment'
|
||||
/>.)</para>
|
||||
/>.</para>
|
||||
|
||||
<para>The builder is executed as follows:
|
||||
|
||||
@@ -1440,17 +1474,19 @@ command-line argument. See <xref linkend='sec-standard-environment'
|
||||
top-level Nix store directory (typically,
|
||||
<filename>/nix/store</filename>).</para></listitem>
|
||||
|
||||
<listitem><para><envar>out</envar> is set to point to the output
|
||||
path of the derivation, which is a subdirectory of the Nix store.
|
||||
The output path is a concatenation of the cryptographic hash of
|
||||
all build inputs, and the <varname>name</varname>
|
||||
attribute.</para></listitem>
|
||||
|
||||
<listitem><para>For each output declared in
|
||||
<varname>outputs</varname>, the corresponding environment variable
|
||||
is set to point to the intended path in the Nix store for that
|
||||
output. Each output path is a concatenation of the cryptographic
|
||||
hash of all build inputs, the <varname>name</varname> attribute
|
||||
and the output name. (The output name is omitted if it’s
|
||||
<literal>out</literal>.)</para></listitem>
|
||||
|
||||
</itemizedlist>
|
||||
|
||||
</para></listitem>
|
||||
|
||||
<listitem><para>If the output path already exists, it is removed.
|
||||
<listitem><para>If an output path already exists, it is removed.
|
||||
Also, locks are acquired to prevent multiple Nix instances from
|
||||
performing the same build at the same time.</para></listitem>
|
||||
|
||||
@@ -1464,14 +1500,11 @@ command-line argument. See <xref linkend='sec-standard-environment'
|
||||
<listitem><para>The temporary directory is removed (unless the
|
||||
<option>-K</option> option was specified).</para></listitem>
|
||||
|
||||
<listitem><para>If the build was successful, Nix scans the output
|
||||
for references to the paths of the inputs. These so-called
|
||||
<emphasis>retained dependencies</emphasis> could be used when the
|
||||
output of the derivation is used (e.g., when it's executed or used
|
||||
as input to another derivation), so if we deploy the derivation, we
|
||||
should copy the retained dependencies as well. The scan is
|
||||
performed by looking for the hash parts of file names of the
|
||||
inputs.</para></listitem>
|
||||
<listitem><para>If the build was successful, Nix scans each output
|
||||
path for references to input paths by looking for the hash parts of
|
||||
the input paths. Since these are potential runtime dependencies,
|
||||
Nix registers them as dependencies of the output
|
||||
paths.</para></listitem>
|
||||
|
||||
<listitem><para>After the build, Nix sets the last-modified
|
||||
timestamp on all files in the build result to 1 (00:00:01 1/1/1970
|
||||
@@ -1497,7 +1530,7 @@ attributes.</para>
|
||||
<variablelist>
|
||||
|
||||
<varlistentry><term><varname>allowedReferences</varname></term>
|
||||
|
||||
|
||||
<listitem><para>The optional attribute
|
||||
<varname>allowedReferences</varname> specifies a list of legal
|
||||
references (dependencies) of the output of the builder. For
|
||||
@@ -1515,7 +1548,7 @@ allowedReferences = [];
|
||||
|
||||
</varlistentry>
|
||||
|
||||
|
||||
|
||||
<varlistentry><term><varname>exportReferencesGraph</varname></term>
|
||||
|
||||
<listitem><para>This attribute allows builders access to the
|
||||
@@ -1626,7 +1659,7 @@ stdenv.mkDerivation {
|
||||
outputHashMode = "flat";
|
||||
outputHashAlgo = "md5";
|
||||
outputHash = md5;
|
||||
|
||||
|
||||
inherit url;
|
||||
}
|
||||
</programlisting>
|
||||
@@ -1655,7 +1688,7 @@ stdenv.mkDerivation {
|
||||
<para>This is the default.</para></listitem>
|
||||
|
||||
</varlistentry>
|
||||
|
||||
|
||||
<varlistentry><term><literal>"recursive"</literal></term>
|
||||
|
||||
<listitem><para>The hash is computed over the NAR archive dump
|
||||
@@ -1676,10 +1709,10 @@ stdenv.mkDerivation {
|
||||
linkend="sec-nix-hash"><command>nix-hash</command> command</link>
|
||||
for information about converting to and from base-32
|
||||
notation.)</para></listitem>
|
||||
|
||||
|
||||
</varlistentry>
|
||||
|
||||
|
||||
|
||||
<varlistentry><term><varname>impureEnvVars</varname></term>
|
||||
|
||||
<listitem><para>This attribute allows you to specify a list of
|
||||
@@ -1706,7 +1739,7 @@ impureEnvVars = [ "http_proxy" "https_proxy" <replaceable>...</replaceable> ];
|
||||
|
||||
</varlistentry>
|
||||
|
||||
|
||||
|
||||
<varlistentry><term><varname>preferLocalBuild</varname></term>
|
||||
|
||||
<listitem><para>If this attribute is set to
|
||||
|
||||
@@ -78,7 +78,7 @@ The hook `nix-mode-hook' is run when Nix mode is started.
|
||||
("\\<isNull\\>" . font-lock-builtin-face)
|
||||
("[a-zA-Z][a-zA-Z0-9\\+-\\.]*:[a-zA-Z0-9%/\\?:@&=\\+\\$,_\\.!~\\*'-]+"
|
||||
. font-lock-constant-face)
|
||||
("\\<\\([a-zA-Z_][a-zA-Z0-9_'\.]*\\)[ \t]*="
|
||||
("\\<\\([a-zA-Z_][a-zA-Z0-9_'\-\.]*\\)[ \t]*="
|
||||
(1 font-lock-variable-name-face nil nil))
|
||||
("<[a-zA-Z0-9._\\+-]+\\(/[a-zA-Z0-9._\\+-]+\\)*>"
|
||||
. font-lock-constant-face)
|
||||
|
||||
@@ -4,7 +4,7 @@ After=syslog.target
|
||||
|
||||
[Service]
|
||||
Type=simple
|
||||
ExecStart=/usr/bin/nix-worker --daemon
|
||||
ExecStart=/usr/bin/nix-daemon
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
+7
-5
@@ -10,17 +10,19 @@ License: LGPLv2+
|
||||
Group: Applications/System
|
||||
%endif
|
||||
URL: http://nixos.org/
|
||||
Source0: %{name}-%{version}.tar.bz2
|
||||
Source0: %{name}-%{version}.tar.gz
|
||||
%if 0%{?el5}
|
||||
BuildRoot: %(mktemp -ud %{_tmppath}/%{name}-%{version}-%{release}-XXXXXX)
|
||||
%endif
|
||||
BuildRequires: perl(DBD::SQLite)
|
||||
BuildRequires: perl(DBI)
|
||||
BuildRequires: perl(WWW::Curl)
|
||||
BuildRequires: perl(ExtUtils::ParseXS)
|
||||
Requires: /usr/bin/perl
|
||||
Requires: curl
|
||||
Requires: perl-DBD-SQLite
|
||||
Requires: bzip2
|
||||
Requires: xz
|
||||
BuildRequires: bzip2-devel
|
||||
BuildRequires: sqlite-devel
|
||||
|
||||
@@ -138,7 +140,7 @@ chmod -x $RPM_BUILD_ROOT%{_sysconfdir}/profile.d/nix.sh
|
||||
%if ! 0%{?rhel}
|
||||
# install systemd service descriptor
|
||||
mkdir -p $RPM_BUILD_ROOT%{_prefix}/lib/systemd/system
|
||||
cp -p misc/systemd/nix-worker.service \
|
||||
cp -p misc/systemd/nix-daemon.service \
|
||||
$RPM_BUILD_ROOT%{_prefix}/lib/systemd/system/
|
||||
%endif
|
||||
|
||||
@@ -172,8 +174,8 @@ chgrp %{nixbld_group} /nix/store
|
||||
chmod 1775 /nix/store
|
||||
%if ! 0%{?rhel}
|
||||
# Enable and start Nix worker
|
||||
systemctl enable nix-worker.service
|
||||
systemctl start nix-worker.service
|
||||
systemctl enable nix-daemon.service
|
||||
systemctl start nix-daemon.service
|
||||
%endif
|
||||
|
||||
%files
|
||||
@@ -185,7 +187,7 @@ systemctl start nix-worker.service
|
||||
%exclude %dir %{perl_vendorarch}/auto/
|
||||
%{_prefix}/libexec/*
|
||||
%if ! 0%{?rhel}
|
||||
%{_prefix}/lib/systemd/system/nix-worker.service
|
||||
%{_prefix}/lib/systemd/system/nix-daemon.service
|
||||
%endif
|
||||
%{_datadir}/emacs/site-lisp/nix-mode.el
|
||||
%{_datadir}/nix
|
||||
|
||||
+2
-1
@@ -1,4 +1,4 @@
|
||||
PERL_MODULES = lib/Nix/Store.pm lib/Nix/Manifest.pm lib/Nix/GeneratePatches.pm lib/Nix/SSH.pm lib/Nix/CopyClosure.pm lib/Nix/Config.pm.in
|
||||
PERL_MODULES = lib/Nix/Store.pm lib/Nix/Manifest.pm lib/Nix/GeneratePatches.pm lib/Nix/SSH.pm lib/Nix/CopyClosure.pm lib/Nix/Config.pm.in lib/Nix/Utils.pm
|
||||
|
||||
all: $(PERL_MODULES:.in=)
|
||||
|
||||
@@ -26,6 +26,7 @@ AM_CXXFLAGS = \
|
||||
-D_FILE_OFFSET_BITS=64
|
||||
|
||||
lib/Nix/Store.cc: lib/Nix/Store.xs
|
||||
$(INSTALL) -d lib/Nix
|
||||
xsubpp $^ -output $@
|
||||
|
||||
else
|
||||
|
||||
@@ -1,27 +1,39 @@
|
||||
package Nix::Config;
|
||||
|
||||
$version = "@version@";
|
||||
|
||||
$binDir = $ENV{"NIX_BIN_DIR"} || "@bindir@";
|
||||
$libexecDir = $ENV{"NIX_LIBEXEC_DIR"} || "@libexecdir@";
|
||||
$stateDir = $ENV{"NIX_STATE_DIR"} || "@localstatedir@/nix";
|
||||
$manifestDir = $ENV{"NIX_MANIFESTS_DIR"} || "@localstatedir@/nix/manifests";
|
||||
$logDir = $ENV{"NIX_LOG_DIR"} || "@localstatedir@/log/nix";
|
||||
$confDir = $ENV{"NIX_CONF_DIR"} || "@sysconfdir@/nix";
|
||||
$storeDir = $ENV{"NIX_STORE_DIR"} || "@storedir@";
|
||||
|
||||
$bzip2 = $ENV{"NIX_BZIP2"} || "@bzip2@";
|
||||
$bzip2 = "@bzip2@";
|
||||
$xz = "@xz@";
|
||||
$curl = "@curl@";
|
||||
|
||||
$useBindings = "@perlbindings@" eq "yes";
|
||||
|
||||
%config = ();
|
||||
|
||||
sub readConfig {
|
||||
my %config;
|
||||
my $config = "@sysconfdir@/nix/nix.conf";
|
||||
if (defined $ENV{'_NIX_OPTIONS'}) {
|
||||
foreach my $s (split '\n', $ENV{'_NIX_OPTIONS'}) {
|
||||
my ($n, $v) = split '=', $s, 2;
|
||||
$config{$n} = $v;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
my $config = "$confDir/nix.conf";
|
||||
return unless -f $config;
|
||||
|
||||
|
||||
open CONFIG, "<$config" or die "cannot open `$config'";
|
||||
while (<CONFIG>) {
|
||||
/^\s*([\w|-]+)\s*=\s*(.*)$/ or next;
|
||||
$config{$1} = $2;
|
||||
print "|$1| -> |$2|\n";
|
||||
}
|
||||
close CONFIG;
|
||||
}
|
||||
|
||||
@@ -6,7 +6,8 @@ use Nix::Store;
|
||||
|
||||
|
||||
sub copyTo {
|
||||
my ($sshHost, $sshOpts, $storePaths, $compressor, $decompressor, $includeOutputs, $dryRun, $sign, $progressViewer) = @_;
|
||||
my ($sshHost, $sshOpts, $storePaths, $compressor, $decompressor,
|
||||
$includeOutputs, $dryRun, $sign, $progressViewer, $useSubstitutes) = @_;
|
||||
|
||||
$compressor = "$compressor |" if $compressor ne "";
|
||||
$decompressor = "$decompressor |" if $decompressor ne "";
|
||||
@@ -16,6 +17,12 @@ sub copyTo {
|
||||
my @closure = reverse(topoSortPaths(computeFSClosure(0, $includeOutputs,
|
||||
map { followLinksToStorePath $_ } @{$storePaths})));
|
||||
|
||||
# Optionally use substitutes on the remote host.
|
||||
if (!$dryRun && $useSubstitutes) {
|
||||
system "ssh $sshHost @{$sshOpts} nix-store -r --ignore-unknown @closure";
|
||||
# Ignore exit status because this is just an optimisation.
|
||||
}
|
||||
|
||||
# Ask the remote host which paths are invalid. Because of limits
|
||||
# to the command line length, do this in chunks. Eventually,
|
||||
# we'll want to use ‘--from-stdin’, but we can't rely on the
|
||||
|
||||
+89
-27
@@ -9,12 +9,12 @@ use Fcntl ':flock';
|
||||
use Nix::Config;
|
||||
|
||||
our @ISA = qw(Exporter);
|
||||
our @EXPORT = qw(readManifest writeManifest updateManifestDB addPatch);
|
||||
our @EXPORT = qw(readManifest writeManifest updateManifestDB addPatch deleteOldManifests parseNARInfo);
|
||||
|
||||
|
||||
sub addNAR {
|
||||
my ($narFiles, $storePath, $info) = @_;
|
||||
|
||||
|
||||
$$narFiles{$storePath} = []
|
||||
unless defined $$narFiles{$storePath};
|
||||
|
||||
@@ -24,7 +24,7 @@ sub addNAR {
|
||||
foreach my $narFile (@{$narFileList}) {
|
||||
$found = 1 if $narFile->{url} eq $info->{url};
|
||||
}
|
||||
|
||||
|
||||
push @{$narFileList}, $info if !$found;
|
||||
}
|
||||
|
||||
@@ -43,7 +43,7 @@ sub addPatch {
|
||||
$patch2->{url} eq $patch->{url} &&
|
||||
$patch2->{basePath} eq $patch->{basePath};
|
||||
}
|
||||
|
||||
|
||||
push @{$patchList}, $patch if !$found;
|
||||
|
||||
return !$found;
|
||||
@@ -68,7 +68,7 @@ sub readManifest_ {
|
||||
my $manifestVersion = 2;
|
||||
|
||||
my ($storePath, $url, $hash, $size, $basePath, $baseHash, $patchType);
|
||||
my ($narHash, $narSize, $references, $deriver, $copyFrom, $system);
|
||||
my ($narHash, $narSize, $references, $deriver, $copyFrom, $system, $compressionType);
|
||||
|
||||
while (<MANIFEST>) {
|
||||
chomp;
|
||||
@@ -93,10 +93,11 @@ sub readManifest_ {
|
||||
undef $system;
|
||||
$references = "";
|
||||
$deriver = "";
|
||||
}
|
||||
$compressionType = "bzip2";
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
|
||||
if (/^\}$/) {
|
||||
$inside = 0;
|
||||
|
||||
@@ -107,6 +108,7 @@ sub readManifest_ {
|
||||
, references => $references
|
||||
, deriver => $deriver
|
||||
, system => $system
|
||||
, compressionType => $compressionType
|
||||
});
|
||||
}
|
||||
|
||||
@@ -120,11 +122,12 @@ sub readManifest_ {
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
elsif (/^\s*StorePath:\s*(\/\S+)\s*$/) { $storePath = $1; }
|
||||
elsif (/^\s*CopyFrom:\s*(\/\S+)\s*$/) { $copyFrom = $1; }
|
||||
elsif (/^\s*Hash:\s*(\S+)\s*$/) { $hash = $1; }
|
||||
elsif (/^\s*URL:\s*(\S+)\s*$/) { $url = $1; }
|
||||
elsif (/^\s*Compression:\s*(\S+)\s*$/) { $compressionType = $1; }
|
||||
elsif (/^\s*Size:\s*(\d+)\s*$/) { $size = $1; }
|
||||
elsif (/^\s*BasePath:\s*(\/\S+)\s*$/) { $basePath = $1; }
|
||||
elsif (/^\s*BaseHash:\s*(\S+)\s*$/) { $baseHash = $1; }
|
||||
@@ -172,6 +175,7 @@ sub writeManifest {
|
||||
print MANIFEST "{\n";
|
||||
print MANIFEST " StorePath: $storePath\n";
|
||||
print MANIFEST " NarURL: $narFile->{url}\n";
|
||||
print MANIFEST " Compression: $narFile->{compressionType}\n";
|
||||
print MANIFEST " Hash: $narFile->{hash}\n" if defined $narFile->{hash};
|
||||
print MANIFEST " Size: $narFile->{size}\n" if defined $narFile->{size};
|
||||
print MANIFEST " NarHash: $narFile->{narHash}\n";
|
||||
@@ -184,7 +188,7 @@ sub writeManifest {
|
||||
print MANIFEST "}\n";
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
foreach my $storePath (sort (keys %{$patches})) {
|
||||
my $patchList = $$patches{$storePath};
|
||||
foreach my $patch (@{$patchList}) {
|
||||
@@ -201,8 +205,8 @@ sub writeManifest {
|
||||
print MANIFEST "}\n";
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
close MANIFEST;
|
||||
|
||||
rename("$manifest.tmp", $manifest)
|
||||
@@ -211,11 +215,11 @@ sub writeManifest {
|
||||
|
||||
# Create a bzipped manifest.
|
||||
unless (defined $noCompress) {
|
||||
system("$Nix::Config::bzip2 < $manifest > $manifest.bz2.tmp") == 0
|
||||
or die "cannot compress manifest";
|
||||
system("$Nix::Config::bzip2 < $manifest > $manifest.bz2.tmp") == 0
|
||||
or die "cannot compress manifest";
|
||||
|
||||
rename("$manifest.bz2.tmp", "$manifest.bz2")
|
||||
or die "cannot rename $manifest.bz2.tmp: $!";
|
||||
rename("$manifest.bz2.tmp", "$manifest.bz2")
|
||||
or die "cannot rename $manifest.bz2.tmp: $!";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -224,8 +228,9 @@ sub updateManifestDB {
|
||||
my $manifestDir = $Nix::Config::manifestDir;
|
||||
|
||||
mkpath($manifestDir);
|
||||
|
||||
my $dbPath = "$manifestDir/cache.sqlite";
|
||||
|
||||
unlink "$manifestDir/cache.sqlite"; # remove obsolete cache
|
||||
my $dbPath = "$manifestDir/cache-v2.sqlite";
|
||||
|
||||
# Open/create the database.
|
||||
our $dbh = DBI->connect("dbi:SQLite:dbname=$dbPath", "", "")
|
||||
@@ -245,13 +250,14 @@ sub updateManifestDB {
|
||||
timestamp integer not null
|
||||
);
|
||||
EOF
|
||||
|
||||
|
||||
$dbh->do(<<EOF);
|
||||
create table if not exists NARs (
|
||||
id integer primary key autoincrement not null,
|
||||
manifest integer not null,
|
||||
storePath text not null,
|
||||
url text not null,
|
||||
compressionType text not null,
|
||||
hash text,
|
||||
size integer,
|
||||
narHash text,
|
||||
@@ -292,8 +298,8 @@ EOF
|
||||
flock(MAINLOCK, LOCK_EX) or die;
|
||||
|
||||
our $insertNAR = $dbh->prepare(
|
||||
"insert into NARs(manifest, storePath, url, hash, size, narHash, " .
|
||||
"narSize, refs, deriver, system) values (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)") or die;
|
||||
"insert into NARs(manifest, storePath, url, compressionType, hash, size, narHash, " .
|
||||
"narSize, refs, deriver, system) values (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)") or die;
|
||||
|
||||
our $insertPatch = $dbh->prepare(
|
||||
"insert into Patches(manifest, storePath, basePath, baseHash, url, hash, " .
|
||||
@@ -304,7 +310,7 @@ EOF
|
||||
# Read each manifest in $manifestDir and add it to the database,
|
||||
# unless we've already done so on a previous run.
|
||||
my %seen;
|
||||
|
||||
|
||||
for my $manifestLink (glob "$manifestDir/*.nixmanifest") {
|
||||
my $manifest = Cwd::abs_path($manifestLink);
|
||||
next unless -f $manifest;
|
||||
@@ -316,9 +322,9 @@ EOF
|
||||
{}, $manifest, $timestamp)} == 1;
|
||||
|
||||
print STDERR "caching $manifest...\n";
|
||||
|
||||
|
||||
$dbh->do("delete from Manifests where path = ?", {}, $manifest);
|
||||
|
||||
|
||||
$dbh->do("insert into Manifests(path, timestamp) values (?, ?)",
|
||||
{}, $manifest, $timestamp);
|
||||
|
||||
@@ -327,11 +333,11 @@ EOF
|
||||
sub addNARToDB {
|
||||
my ($storePath, $narFile) = @_;
|
||||
$insertNAR->execute(
|
||||
$id, $storePath, $narFile->{url}, $narFile->{hash}, $narFile->{size},
|
||||
$narFile->{narHash}, $narFile->{narSize}, $narFile->{references},
|
||||
$id, $storePath, $narFile->{url}, $narFile->{compressionType}, $narFile->{hash},
|
||||
$narFile->{size}, $narFile->{narHash}, $narFile->{narSize}, $narFile->{references},
|
||||
$narFile->{deriver}, $narFile->{system});
|
||||
};
|
||||
|
||||
|
||||
sub addPatchToDB {
|
||||
my ($storePath, $patch) = @_;
|
||||
$insertPatch->execute(
|
||||
@@ -341,7 +347,7 @@ EOF
|
||||
};
|
||||
|
||||
my $version = readManifest_($manifest, \&addNARToDB, \&addPatchToDB);
|
||||
|
||||
|
||||
if ($version < 3) {
|
||||
die "you have an old-style or corrupt manifest `$manifestLink'; please delete it\n";
|
||||
}
|
||||
@@ -364,4 +370,60 @@ EOF
|
||||
}
|
||||
|
||||
|
||||
|
||||
# Delete all old manifests downloaded from a given URL.
|
||||
sub deleteOldManifests {
|
||||
my ($url, $curUrlFile) = @_;
|
||||
for my $urlFile (glob "$Nix::Config::manifestDir/*.url") {
|
||||
next if defined $curUrlFile && $urlFile eq $curUrlFile;
|
||||
open URL, "<$urlFile" or die;
|
||||
my $url2 = <URL>;
|
||||
chomp $url2;
|
||||
close URL;
|
||||
next unless $url eq $url2;
|
||||
my $base = $urlFile; $base =~ s/.url$//;
|
||||
unlink "${base}.url";
|
||||
unlink "${base}.nixmanifest";
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
# Parse a NAR info file.
|
||||
sub parseNARInfo {
|
||||
my ($storePath, $content) = @_;
|
||||
|
||||
my ($storePath2, $url, $fileHash, $fileSize, $narHash, $narSize, $deriver, $system);
|
||||
my $compression = "bzip2";
|
||||
my @refs;
|
||||
|
||||
foreach my $line (split "\n", $content) {
|
||||
return undef unless $line =~ /^(.*): (.*)$/;
|
||||
if ($1 eq "StorePath") { $storePath2 = $2; }
|
||||
elsif ($1 eq "URL") { $url = $2; }
|
||||
elsif ($1 eq "Compression") { $compression = $2; }
|
||||
elsif ($1 eq "FileHash") { $fileHash = $2; }
|
||||
elsif ($1 eq "FileSize") { $fileSize = int($2); }
|
||||
elsif ($1 eq "NarHash") { $narHash = $2; }
|
||||
elsif ($1 eq "NarSize") { $narSize = int($2); }
|
||||
elsif ($1 eq "References") { @refs = split / /, $2; }
|
||||
elsif ($1 eq "Deriver") { $deriver = $2; }
|
||||
elsif ($1 eq "System") { $system = $2; }
|
||||
}
|
||||
|
||||
return undef if $storePath ne $storePath2 || !defined $url || !defined $narHash;
|
||||
|
||||
return
|
||||
{ url => $url
|
||||
, compression => $compression
|
||||
, fileHash => $fileHash
|
||||
, fileSize => $fileSize
|
||||
, narHash => $narHash
|
||||
, narSize => $narSize
|
||||
, refs => [ @refs ]
|
||||
, deriver => $deriver
|
||||
, system => $system
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
return 1;
|
||||
|
||||
+13
-10
@@ -15,11 +15,11 @@
|
||||
using namespace nix;
|
||||
|
||||
|
||||
void doInit()
|
||||
void doInit()
|
||||
{
|
||||
if (!store) {
|
||||
try {
|
||||
setDefaultsFromEnvironment();
|
||||
settings.processEnvironment();
|
||||
store = openStore();
|
||||
} catch (Error & e) {
|
||||
croak(e.what());
|
||||
@@ -237,32 +237,35 @@ SV * derivationFromPath(char * drvPath)
|
||||
doInit();
|
||||
Derivation drv = derivationFromPath(*store, drvPath);
|
||||
hash = newHV();
|
||||
|
||||
/* TODO: handle drv.outputs */
|
||||
|
||||
|
||||
HV * outputs = newHV();
|
||||
for (DerivationOutputs::iterator i = drv.outputs.begin(); i != drv.outputs.end(); ++i)
|
||||
hv_store(outputs, i->first.c_str(), i->first.size(), newSVpv(i->second.path.c_str(), 0), 0);
|
||||
hv_stores(hash, "outputs", newRV((SV *) outputs));
|
||||
|
||||
AV * inputDrvs = newAV();
|
||||
for (DerivationInputs::iterator i = drv.inputDrvs.begin(); i != drv.inputDrvs.end(); ++i)
|
||||
av_push(inputDrvs, newSVpv(i->first.c_str(), 0)); // !!! ignores i->second
|
||||
hv_stores(hash, "inputDrvs", newRV((SV *) inputDrvs));
|
||||
|
||||
|
||||
AV * inputSrcs = newAV();
|
||||
for (PathSet::iterator i = drv.inputSrcs.begin(); i != drv.inputSrcs.end(); ++i)
|
||||
av_push(inputSrcs, newSVpv(i->c_str(), 0));
|
||||
hv_stores(hash, "inputSrcs", newRV((SV *) inputSrcs));
|
||||
|
||||
|
||||
hv_stores(hash, "platform", newSVpv(drv.platform.c_str(), 0));
|
||||
hv_stores(hash, "builder", newSVpv(drv.builder.c_str(), 0));
|
||||
|
||||
|
||||
AV * args = newAV();
|
||||
for (Strings::iterator i = drv.args.begin(); i != drv.args.end(); ++i)
|
||||
av_push(args, newSVpv(i->c_str(), 0));
|
||||
hv_stores(hash, "args", newRV((SV *) args));
|
||||
|
||||
|
||||
HV * env = newHV();
|
||||
for (StringPairs::iterator i = drv.env.begin(); i != drv.env.end(); ++i)
|
||||
hv_store(env, i->first.c_str(), i->first.size(), newSVpv(i->second.c_str(), 0), 0);
|
||||
hv_stores(hash, "env", newRV((SV *) env));
|
||||
|
||||
|
||||
RETVAL = newRV_noinc((SV *)hash);
|
||||
} catch (Error & e) {
|
||||
croak(e.what());
|
||||
|
||||
@@ -0,0 +1,38 @@
|
||||
package Nix::Utils;
|
||||
|
||||
our @ISA = qw(Exporter);
|
||||
our @EXPORT = qw(checkURL uniq writeFile readFile);
|
||||
|
||||
$urlRE = "(?: [a-zA-Z][a-zA-Z0-9\+\-\.]*\:[a-zA-Z0-9\%\/\?\:\@\&\=\+\$\,\-\_\.\!\~\*]+ )";
|
||||
|
||||
sub checkURL {
|
||||
my ($url) = @_;
|
||||
die "invalid URL ‘$url’\n" unless $url =~ /^ $urlRE $ /x;
|
||||
}
|
||||
|
||||
sub uniq {
|
||||
my %seen;
|
||||
my @res;
|
||||
foreach my $name (@_) {
|
||||
next if $seen{$name};
|
||||
$seen{$name} = 1;
|
||||
push @res, $name;
|
||||
}
|
||||
return @res;
|
||||
}
|
||||
|
||||
sub writeFile {
|
||||
my ($fn, $s) = @_;
|
||||
open TMP, ">$fn" or die "cannot create file `$fn': $!";
|
||||
print TMP "$s" or die;
|
||||
close TMP or die;
|
||||
}
|
||||
|
||||
sub readFile {
|
||||
local $/ = undef;
|
||||
my ($fn) = @_;
|
||||
open TMP, "<$fn" or die "cannot open file `$fn': $!";
|
||||
my $s = <TMP>;
|
||||
close TMP or die;
|
||||
return $s;
|
||||
}
|
||||
+31
-12
@@ -29,6 +29,7 @@ let
|
||||
--with-xml-flags=--nonet
|
||||
--with-dbi=${perlPackages.DBI}/${perl.libPrefix}
|
||||
--with-dbd-sqlite=${perlPackages.DBDSQLite}/${perl.libPrefix}
|
||||
--with-www-curl=${perlPackages.WWWCurl}/${perl.libPrefix}
|
||||
'';
|
||||
|
||||
postUnpack = ''
|
||||
@@ -41,9 +42,18 @@ let
|
||||
export VARTEXFONTS=$TMPDIR/texfonts
|
||||
'';
|
||||
|
||||
distPhase =
|
||||
''
|
||||
runHook preDist
|
||||
make dist-gzip
|
||||
make dist-xz
|
||||
mkdir -p $out/tarballs
|
||||
cp *.tar.* $out/tarballs
|
||||
'';
|
||||
|
||||
preDist = ''
|
||||
make -C doc/manual install prefix=$out
|
||||
|
||||
|
||||
make -C doc/manual manual.pdf prefix=$out
|
||||
cp doc/manual/manual.pdf $out/manual.pdf
|
||||
|
||||
@@ -54,7 +64,7 @@ let
|
||||
# to Windows and Macs, so there should be no Linux binaries
|
||||
# in the closure).
|
||||
nuke-refs $out/manual.pdf
|
||||
|
||||
|
||||
echo "doc manual $out/share/doc/nix/manual" >> $out/nix-support/hydra-build-products
|
||||
echo "doc-pdf manual $out/manual.pdf" >> $out/nix-support/hydra-build-products
|
||||
echo "doc release-notes $out/share/doc/nix/release-notes" >> $out/nix-support/hydra-build-products
|
||||
@@ -77,11 +87,17 @@ let
|
||||
--disable-init-state
|
||||
--with-dbi=${perlPackages.DBI}/${perl.libPrefix}
|
||||
--with-dbd-sqlite=${perlPackages.DBDSQLite}/${perl.libPrefix}
|
||||
--with-www-curl=${perlPackages.WWWCurl}/${perl.libPrefix}
|
||||
--enable-gc
|
||||
--sysconfdir=/etc
|
||||
'';
|
||||
|
||||
enableParallelBuilding = true;
|
||||
|
||||
makeFlags = "profiledir=$(out)/etc/profile.d";
|
||||
|
||||
installFlags = "sysconfdir=$(out)/etc";
|
||||
|
||||
doInstallCheck = true;
|
||||
};
|
||||
|
||||
@@ -134,12 +150,13 @@ let
|
||||
--disable-init-state
|
||||
--with-dbi=${perlPackages.DBI}/${perl.libPrefix}
|
||||
--with-dbd-sqlite=${perlPackages.DBDSQLite}/${perl.libPrefix}
|
||||
--with-www-curl=${perlPackages.WWWCurl}/${perl.libPrefix}
|
||||
'';
|
||||
|
||||
dontInstall = false;
|
||||
|
||||
doInstallCheck = true;
|
||||
|
||||
|
||||
lcovFilter = [ "*/boost/*" "*-tab.*" ];
|
||||
|
||||
# We call `dot', and even though we just use it to
|
||||
@@ -148,16 +165,16 @@ let
|
||||
FONTCONFIG_FILE = texFunctions.fontsConf;
|
||||
};
|
||||
|
||||
|
||||
|
||||
rpm_fedora13i386 = makeRPM_i686 (diskImageFuns: diskImageFuns.fedora13i386) 50;
|
||||
rpm_fedora13x86_64 = makeRPM_x86_64 (diskImageFunsFun: diskImageFunsFun.fedora13x86_64) 50;
|
||||
rpm_fedora16i386 = makeRPM_i686 (diskImageFuns: diskImageFuns.fedora16i386) 50;
|
||||
rpm_fedora16x86_64 = makeRPM_x86_64 (diskImageFunsFun: diskImageFunsFun.fedora16x86_64) 50;
|
||||
|
||||
|
||||
|
||||
deb_debian60i386 = makeDeb_i686 (diskImageFuns: diskImageFuns.debian60i386) 50;
|
||||
deb_debian60x86_64 = makeDeb_x86_64 (diskImageFunsFun: diskImageFunsFun.debian60x86_64) 50;
|
||||
|
||||
|
||||
deb_ubuntu1004i386 = makeDeb_i686 (diskImageFuns: diskImageFuns.ubuntu1004i386) 50;
|
||||
deb_ubuntu1004x86_64 = makeDeb_x86_64 (diskImageFuns: diskImageFuns.ubuntu1004x86_64) 50;
|
||||
deb_ubuntu1010i386 = makeDeb_i686 (diskImageFuns: diskImageFuns.ubuntu1010i386) 50;
|
||||
@@ -166,6 +183,8 @@ let
|
||||
deb_ubuntu1110x86_64 = makeDeb_x86_64 (diskImageFuns: diskImageFuns.ubuntu1110x86_64) 60;
|
||||
deb_ubuntu1204i386 = makeDeb_i686 (diskImageFuns: diskImageFuns.ubuntu1204i386) 60;
|
||||
deb_ubuntu1204x86_64 = makeDeb_x86_64 (diskImageFuns: diskImageFuns.ubuntu1204x86_64) 60;
|
||||
deb_ubuntu1210i386 = makeDeb_i686 (diskImageFuns: diskImageFuns.ubuntu1210i386) 70;
|
||||
deb_ubuntu1210x86_64 = makeDeb_x86_64 (diskImageFuns: diskImageFuns.ubuntu1210x86_64) 70;
|
||||
|
||||
|
||||
# System tests.
|
||||
@@ -183,16 +202,16 @@ let
|
||||
makeRPM_i686 = makeRPM "i686-linux";
|
||||
makeRPM_x86_64 = makeRPM "x86_64-linux";
|
||||
|
||||
makeRPM =
|
||||
makeRPM =
|
||||
system: diskImageFun: prio:
|
||||
|
||||
with import nixpkgs { inherit system; };
|
||||
|
||||
releaseTools.rpmBuild rec {
|
||||
name = "nix-rpm-${diskImage.name}";
|
||||
name = "nix-rpm";
|
||||
src = jobs.tarball;
|
||||
diskImage = (diskImageFun vmTools.diskImageFuns)
|
||||
{ extraPackages = [ "perl-DBD-SQLite" "perl-devel" "sqlite" "sqlite-devel" "bzip2-devel" "emacs" ]; };
|
||||
{ extraPackages = [ "perl-DBD-SQLite" "perl-devel" "sqlite" "sqlite-devel" "bzip2-devel" "emacs" "perl-WWW-Curl" ]; };
|
||||
memSize = 1024;
|
||||
meta.schedulingPriority = prio;
|
||||
postRPMInstall = "cd /tmp/rpmout/BUILD/nix-* && make installcheck";
|
||||
@@ -201,7 +220,7 @@ let
|
||||
|
||||
makeDeb_i686 = makeDeb "i686-linux";
|
||||
makeDeb_x86_64 = makeDeb "x86_64-linux";
|
||||
|
||||
|
||||
makeDeb =
|
||||
system: diskImageFun: prio:
|
||||
|
||||
@@ -211,11 +230,11 @@ let
|
||||
name = "nix-deb";
|
||||
src = jobs.tarball;
|
||||
diskImage = (diskImageFun vmTools.diskImageFuns)
|
||||
{ extraPackages = [ "libdbd-sqlite3-perl" "libsqlite3-dev" "libbz2-dev" ]; };
|
||||
{ extraPackages = [ "libdbd-sqlite3-perl" "libsqlite3-dev" "libbz2-dev" "libwww-curl-perl" ]; };
|
||||
memSize = 1024;
|
||||
meta.schedulingPriority = prio;
|
||||
configureFlags = "--sysconfdir=/etc";
|
||||
debRequires = [ "curl" "libdbd-sqlite3-perl" "libsqlite3-0" "libbz2-1.0" ];
|
||||
debRequires = [ "curl" "libdbd-sqlite3-perl" "libsqlite3-0" "libbz2-1.0" "bzip2" "xz-utils" "libwww-curl-perl" ];
|
||||
doInstallCheck = true;
|
||||
};
|
||||
|
||||
|
||||
+6
-6
@@ -7,17 +7,16 @@ noinst_SCRIPTS = nix-profile.sh \
|
||||
find-runtime-roots.pl build-remote.pl nix-reduce-build \
|
||||
copy-from-other-stores.pl nix-http-export.cgi
|
||||
|
||||
nix-pull nix-push: download-using-manifests.pl
|
||||
profiledir = $(sysconfdir)/profile.d
|
||||
|
||||
install-exec-local: download-using-manifests.pl copy-from-other-stores.pl find-runtime-roots.pl
|
||||
$(INSTALL) -d $(DESTDIR)$(sysconfdir)/profile.d
|
||||
$(INSTALL_DATA) nix-profile.sh $(DESTDIR)$(sysconfdir)/profile.d/nix.sh
|
||||
install-exec-local: download-using-manifests.pl copy-from-other-stores.pl download-from-binary-cache.pl find-runtime-roots.pl
|
||||
$(INSTALL) -d $(DESTDIR)$(profiledir)
|
||||
$(INSTALL_DATA) nix-profile.sh $(DESTDIR)$(profiledir)/nix.sh
|
||||
$(INSTALL) -d $(DESTDIR)$(libexecdir)/nix
|
||||
$(INSTALL_PROGRAM) find-runtime-roots.pl $(DESTDIR)$(libexecdir)/nix
|
||||
$(INSTALL_PROGRAM) build-remote.pl $(DESTDIR)$(libexecdir)/nix
|
||||
$(INSTALL) -d $(DESTDIR)$(libexecdir)/nix/substituters
|
||||
$(INSTALL_PROGRAM) download-using-manifests.pl $(DESTDIR)$(libexecdir)/nix/substituters
|
||||
$(INSTALL_PROGRAM) copy-from-other-stores.pl $(DESTDIR)$(libexecdir)/nix/substituters
|
||||
$(INSTALL_PROGRAM) download-using-manifests.pl copy-from-other-stores.pl download-from-binary-cache.pl $(DESTDIR)$(libexecdir)/nix/substituters
|
||||
$(INSTALL) -d $(DESTDIR)$(sysconfdir)/nix
|
||||
|
||||
include ../substitute.mk
|
||||
@@ -29,6 +28,7 @@ EXTRA_DIST = nix-collect-garbage.in \
|
||||
nix-build.in \
|
||||
download-using-manifests.pl.in \
|
||||
copy-from-other-stores.pl.in \
|
||||
download-from-binary-cache.pl.in \
|
||||
nix-copy-closure.in \
|
||||
find-runtime-roots.pl.in \
|
||||
build-remote.pl.in \
|
||||
|
||||
+17
-14
@@ -6,6 +6,7 @@ use IO::Handle;
|
||||
use Nix::Config;
|
||||
use Nix::SSH qw/sshOpts openSSHConnection/;
|
||||
use Nix::CopyClosure;
|
||||
use Nix::Store;
|
||||
no warnings('once');
|
||||
|
||||
|
||||
@@ -18,7 +19,7 @@ no warnings('once');
|
||||
# less than the maximum load for that machine, we try to get an
|
||||
# exclusive lock on $currentLoad/$machine-$slot (without blocking).
|
||||
# If we get such a lock, we send "accept" to the caller. Otherwise,
|
||||
# we send "postpone" and exit.
|
||||
# we send "postpone" and exit.
|
||||
# - We release the exclusive lock on $currentLoad/main-lock.
|
||||
# - We perform the build on $neededSystem.
|
||||
# - We release the exclusive lock on $currentLoad/$machine-$slot.
|
||||
@@ -45,8 +46,7 @@ sub all { $_ || return 0 for @_; 1 }
|
||||
# Initialisation.
|
||||
my $loadIncreased = 0;
|
||||
|
||||
my ($localSystem, $maxSilentTime, $printBuildTrace) = @ARGV;
|
||||
$maxSilentTime = 0 unless defined $maxSilentTime;
|
||||
my ($localSystem, $maxSilentTime, $printBuildTrace, $buildTimeout) = @ARGV;
|
||||
|
||||
my $currentLoad = $ENV{"NIX_CURRENT_LOAD"};
|
||||
my $conf = $ENV{"NIX_REMOTE_SYSTEMS"};
|
||||
@@ -102,14 +102,14 @@ REQ: while (1) {
|
||||
sendReply "decline";
|
||||
next;
|
||||
}
|
||||
|
||||
|
||||
# Acquire the exclusive lock on $currentLoad/main-lock.
|
||||
mkdir $currentLoad, 0777 or die unless -d $currentLoad;
|
||||
my $mainLock = "$currentLoad/main-lock";
|
||||
sysopen MAINLOCK, "$mainLock", O_RDWR|O_CREAT, 0600 or die;
|
||||
flock(MAINLOCK, LOCK_EX) or die;
|
||||
|
||||
|
||||
|
||||
|
||||
while (1) {
|
||||
# Find all machine that can execute this build, i.e., that
|
||||
# support builds for the given platform and features, and are
|
||||
@@ -141,7 +141,7 @@ REQ: while (1) {
|
||||
close $slotLock;
|
||||
$slot++;
|
||||
}
|
||||
|
||||
|
||||
push @available, { machine => $cur, load => $load, free => $free }
|
||||
if $load < $cur->{maxJobs};
|
||||
}
|
||||
@@ -160,7 +160,7 @@ REQ: while (1) {
|
||||
if ($rightType && !$canBuildLocally) {
|
||||
sendReply "postpone";
|
||||
} else {
|
||||
sendReply "decline";
|
||||
sendReply "decline";
|
||||
}
|
||||
close MAINLOCK;
|
||||
next REQ;
|
||||
@@ -182,9 +182,9 @@ REQ: while (1) {
|
||||
|
||||
|
||||
# Select the best available machine and lock a free slot.
|
||||
my $selected = $available[0];
|
||||
my $selected = $available[0];
|
||||
my $machine = $selected->{machine};
|
||||
|
||||
|
||||
$slotLock = openSlotLock($machine, $selected->{free});
|
||||
flock($slotLock, LOCK_EX | LOCK_NB) or die;
|
||||
utime undef, undef, $slotLock;
|
||||
@@ -196,7 +196,7 @@ REQ: while (1) {
|
||||
@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;
|
||||
}
|
||||
@@ -257,7 +257,10 @@ close UPLOADLOCK;
|
||||
|
||||
|
||||
# Perform the build.
|
||||
my $buildFlags = "--max-silent-time $maxSilentTime --fallback --add-root $rootsDir/\$PPID.out --option verbosity 0";
|
||||
my $buildFlags =
|
||||
"--max-silent-time $maxSilentTime --option build-timeout $buildTimeout"
|
||||
. " --fallback --add-root $rootsDir/\$PPID.out --quiet"
|
||||
. " --option build-keep-log false";
|
||||
|
||||
# We let the remote side kill its process group when the connection is
|
||||
# closed unexpectedly. This is necessary to ensure that no processes
|
||||
@@ -286,10 +289,10 @@ if (system("exec ssh $hostName @sshOpts '(read; kill -INT -\$\$) <&0 & nix-store
|
||||
foreach my $output (@outputs) {
|
||||
my $maybeSignRemote = "";
|
||||
$maybeSignRemote = "--sign" if $UID != 0;
|
||||
|
||||
next if isValidPath($output);
|
||||
system("exec ssh $hostName @sshOpts 'nix-store --export $maybeSignRemote $output'" .
|
||||
"| NIX_HELD_LOCKS=$output @bindir@/nix-store --import > /dev/null") == 0
|
||||
or die "cannot copy $output from $hostName: $?";
|
||||
or die "cannot copy $output from $hostName: $?";
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -36,42 +36,45 @@ sub findStorePath {
|
||||
if ($ARGV[0] eq "--query") {
|
||||
|
||||
while (<STDIN>) {
|
||||
my $cmd = $_; chomp $cmd;
|
||||
chomp;
|
||||
my ($cmd, @args) = split " ", $_;
|
||||
|
||||
if ($cmd eq "have") {
|
||||
my $storePath = <STDIN>; chomp $storePath;
|
||||
print STDOUT (defined findStorePath($storePath) ? "1\n" : "0\n");
|
||||
foreach my $storePath (@args) {
|
||||
print "$storePath\n" if defined findStorePath($storePath);
|
||||
}
|
||||
print "\n";
|
||||
}
|
||||
|
||||
elsif ($cmd eq "info") {
|
||||
my $storePath = <STDIN>; chomp $storePath;
|
||||
my ($store, $sourcePath) = findStorePath($storePath);
|
||||
if (!defined $store) {
|
||||
print "0\n";
|
||||
next; # not an error
|
||||
foreach my $storePath (@args) {
|
||||
my ($store, $sourcePath) = findStorePath($storePath);
|
||||
next unless defined $store;
|
||||
|
||||
$ENV{"NIX_DB_DIR"} = "$store/var/nix/db";
|
||||
|
||||
my $deriver = `@bindir@/nix-store --query --deriver $storePath`;
|
||||
die "cannot query deriver of `$storePath'" if $? != 0;
|
||||
chomp $deriver;
|
||||
$deriver = "" if $deriver eq "unknown-deriver";
|
||||
|
||||
my @references = split "\n",
|
||||
`@bindir@/nix-store --query --references $storePath`;
|
||||
die "cannot query references of `$storePath'" if $? != 0;
|
||||
|
||||
my $narSize = `@bindir@/nix-store --query --size $storePath`;
|
||||
die "cannot query size of `$storePath'" if $? != 0;
|
||||
chomp $narSize;
|
||||
|
||||
print "$storePath\n";
|
||||
print "$deriver\n";
|
||||
print scalar @references, "\n";
|
||||
print "$_\n" foreach @references;
|
||||
print "$narSize\n";
|
||||
print "$narSize\n";
|
||||
}
|
||||
print "1\n";
|
||||
|
||||
$ENV{"NIX_DB_DIR"} = "$store/var/nix/db";
|
||||
|
||||
my $deriver = `@bindir@/nix-store --query --deriver $storePath`;
|
||||
die "cannot query deriver of `$storePath'" if $? != 0;
|
||||
chomp $deriver;
|
||||
$deriver = "" if $deriver eq "unknown-deriver";
|
||||
|
||||
my @references = split "\n",
|
||||
`@bindir@/nix-store --query --references $storePath`;
|
||||
die "cannot query references of `$storePath'" if $? != 0;
|
||||
|
||||
my $narSize = `@bindir@/nix-store --query --size $storePath`;
|
||||
die "cannot query size of `$storePath'" if $? != 0;
|
||||
chomp $narSize;
|
||||
|
||||
print "$deriver\n";
|
||||
print scalar @references, "\n";
|
||||
print "$_\n" foreach @references;
|
||||
print "$narSize\n";
|
||||
print "$narSize\n";
|
||||
print "\n";
|
||||
}
|
||||
|
||||
else { die "unknown command `$cmd'"; }
|
||||
@@ -80,13 +83,15 @@ if ($ARGV[0] eq "--query") {
|
||||
|
||||
|
||||
elsif ($ARGV[0] eq "--substitute") {
|
||||
die unless scalar @ARGV == 2;
|
||||
die unless scalar @ARGV == 3;
|
||||
my $storePath = $ARGV[1];
|
||||
my $destPath = $ARGV[2];
|
||||
my ($store, $sourcePath) = findStorePath $storePath;
|
||||
die unless $store;
|
||||
print "\n*** Copying `$storePath' from `$sourcePath'\n\n";
|
||||
system("$binDir/nix-store --dump $sourcePath | $binDir/nix-store --restore $storePath") == 0
|
||||
print STDERR "\n*** Copying `$storePath' from `$sourcePath'\n\n";
|
||||
system("$binDir/nix-store --dump $sourcePath | $binDir/nix-store --restore $destPath") == 0
|
||||
or die "cannot copy `$sourcePath' to `$storePath'";
|
||||
print "\n"; # no hash to verify
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,552 @@
|
||||
#! @perl@ -w @perlFlags@
|
||||
|
||||
use DBI;
|
||||
use File::Basename;
|
||||
use IO::Select;
|
||||
use Nix::Config;
|
||||
use Nix::Store;
|
||||
use Nix::Utils;
|
||||
use Nix::Manifest;
|
||||
use WWW::Curl::Easy;
|
||||
use WWW::Curl::Multi;
|
||||
use strict;
|
||||
|
||||
|
||||
Nix::Config::readConfig;
|
||||
|
||||
my @caches;
|
||||
my $gotCaches = 0;
|
||||
|
||||
my $maxParallelRequests = int($Nix::Config::config{"binary-caches-parallel-connections"} // 150);
|
||||
$maxParallelRequests = 1 if $maxParallelRequests < 1;
|
||||
|
||||
my $ttlNegative = 24 * 3600; # when to purge negative lookups from the database
|
||||
my $ttlNegativeUse = 3600; # how long negative lookups are valid for non-"have" lookups
|
||||
my $didExpiration = 0;
|
||||
|
||||
my $debug = ($ENV{"NIX_DEBUG_SUBST"} // "") eq 1;
|
||||
open(STDERR, ">>/dev/tty") if $debug;
|
||||
|
||||
my ($dbh, $queryCache, $insertNAR, $queryNAR, $insertNARExistence, $queryNARExistence, $expireNARExistence);
|
||||
|
||||
my $curlm = WWW::Curl::Multi->new;
|
||||
my $activeRequests = 0;
|
||||
my $curlIdCount = 1;
|
||||
my %requests;
|
||||
my %scheduled;
|
||||
my $caBundle = $ENV{"CURL_CA_BUNDLE"} // $ENV{"OPENSSL_X509_CERT_FILE"};
|
||||
|
||||
my $userName = getpwuid($<) or die "cannot figure out user name";
|
||||
|
||||
|
||||
sub addRequest {
|
||||
my ($storePath, $url, $head) = @_;
|
||||
|
||||
my $curl = WWW::Curl::Easy->new;
|
||||
my $curlId = $curlIdCount++;
|
||||
$requests{$curlId} = { storePath => $storePath, url => $url, handle => $curl, content => "", type => $head ? "HEAD" : "GET" };
|
||||
|
||||
$curl->setopt(CURLOPT_PRIVATE, $curlId);
|
||||
$curl->setopt(CURLOPT_URL, $url);
|
||||
open (my $fh, ">", \$requests{$curlId}->{content});
|
||||
$curl->setopt(CURLOPT_WRITEDATA, $fh);
|
||||
$curl->setopt(CURLOPT_FOLLOWLOCATION, 1);
|
||||
$curl->setopt(CURLOPT_CAINFO, $caBundle) if defined $caBundle;
|
||||
$curl->setopt(CURLOPT_USERAGENT, "Nix/$Nix::Config::version");
|
||||
$curl->setopt(CURLOPT_NOBODY, 1) if $head;
|
||||
$curl->setopt(CURLOPT_FAILONERROR, 1);
|
||||
|
||||
if ($activeRequests >= $maxParallelRequests) {
|
||||
$scheduled{$curlId} = 1;
|
||||
} else {
|
||||
$curlm->add_handle($curl);
|
||||
$activeRequests++;
|
||||
}
|
||||
|
||||
return $requests{$curlId};
|
||||
}
|
||||
|
||||
|
||||
sub processRequests {
|
||||
while ($activeRequests) {
|
||||
my ($rfds, $wfds, $efds) = $curlm->fdset();
|
||||
#print STDERR "R = @{$rfds}, W = @{$wfds}, E = @{$efds}\n";
|
||||
|
||||
# Sleep until we can read or write some data.
|
||||
if (scalar @{$rfds} + scalar @{$wfds} + scalar @{$efds} > 0) {
|
||||
IO::Select->select(IO::Select->new(@{$rfds}), IO::Select->new(@{$wfds}), IO::Select->new(@{$efds}), 0.1);
|
||||
}
|
||||
|
||||
if ($curlm->perform() != $activeRequests) {
|
||||
while (my ($id, $result) = $curlm->info_read) {
|
||||
if ($id) {
|
||||
my $request = $requests{$id} or die;
|
||||
my $handle = $request->{handle};
|
||||
$request->{result} = $result;
|
||||
$request->{httpStatus} = $handle->getinfo(CURLINFO_RESPONSE_CODE);
|
||||
|
||||
print STDERR "$request->{type} on $request->{url} [$request->{result}, $request->{httpStatus}]\n" if $debug;
|
||||
|
||||
$activeRequests--;
|
||||
delete $request->{handle};
|
||||
|
||||
if (scalar(keys %scheduled) > 0) {
|
||||
my $id2 = (keys %scheduled)[0];
|
||||
$curlm->add_handle($requests{$id2}->{handle});
|
||||
$activeRequests++;
|
||||
delete $scheduled{$id2};
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
sub initCache {
|
||||
my $dbPath = "$Nix::Config::stateDir/binary-cache-v2.sqlite";
|
||||
|
||||
unlink "$Nix::Config::stateDir/binary-cache-v1.sqlite";
|
||||
|
||||
# Open/create the database.
|
||||
$dbh = DBI->connect("dbi:SQLite:dbname=$dbPath", "", "")
|
||||
or die "cannot open database `$dbPath'";
|
||||
$dbh->{RaiseError} = 1;
|
||||
$dbh->{PrintError} = 0;
|
||||
|
||||
$dbh->sqlite_busy_timeout(60 * 60 * 1000);
|
||||
|
||||
$dbh->do("pragma synchronous = off"); # we can always reproduce the cache
|
||||
$dbh->do("pragma journal_mode = truncate");
|
||||
|
||||
# Initialise the database schema, if necessary.
|
||||
$dbh->do(<<EOF);
|
||||
create table if not exists BinaryCaches (
|
||||
id integer primary key autoincrement not null,
|
||||
url text unique not null,
|
||||
timestamp integer not null,
|
||||
storeDir text not null,
|
||||
wantMassQuery integer not null,
|
||||
priority integer not null
|
||||
);
|
||||
EOF
|
||||
|
||||
$dbh->do(<<EOF);
|
||||
create table if not exists NARs (
|
||||
cache integer not null,
|
||||
storePath text not null,
|
||||
url text not null,
|
||||
compression text not null,
|
||||
fileHash text,
|
||||
fileSize integer,
|
||||
narHash text,
|
||||
narSize integer,
|
||||
refs text,
|
||||
deriver text,
|
||||
system text,
|
||||
timestamp integer not null,
|
||||
primary key (cache, storePath),
|
||||
foreign key (cache) references BinaryCaches(id) on delete cascade
|
||||
);
|
||||
EOF
|
||||
|
||||
$dbh->do(<<EOF);
|
||||
create table if not exists NARExistence (
|
||||
cache integer not null,
|
||||
storePath text not null,
|
||||
exist integer not null,
|
||||
timestamp integer not null,
|
||||
primary key (cache, storePath),
|
||||
foreign key (cache) references BinaryCaches(id) on delete cascade
|
||||
);
|
||||
EOF
|
||||
|
||||
$dbh->do("create index if not exists NARExistenceByExistTimestamp on NARExistence (exist, timestamp)");
|
||||
|
||||
$queryCache = $dbh->prepare("select id, storeDir, wantMassQuery, priority from BinaryCaches where url = ?") or die;
|
||||
|
||||
$insertNAR = $dbh->prepare(
|
||||
"insert or replace into NARs(cache, storePath, url, compression, fileHash, fileSize, narHash, " .
|
||||
"narSize, refs, deriver, system, timestamp) values (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)") or die;
|
||||
|
||||
$queryNAR = $dbh->prepare("select * from NARs where cache = ? and storePath = ?") or die;
|
||||
|
||||
$insertNARExistence = $dbh->prepare(
|
||||
"insert or replace into NARExistence(cache, storePath, exist, timestamp) values (?, ?, ?, ?)") or die;
|
||||
|
||||
$queryNARExistence = $dbh->prepare("select exist, timestamp from NARExistence where cache = ? and storePath = ?") or die;
|
||||
|
||||
$expireNARExistence = $dbh->prepare("delete from NARExistence where exist = ? and timestamp < ?") or die;
|
||||
}
|
||||
|
||||
|
||||
sub getAvailableCaches {
|
||||
return if $gotCaches;
|
||||
$gotCaches = 1;
|
||||
|
||||
return if
|
||||
($Nix::Config::config{"use-binary-caches"} // "true") eq "false" ||
|
||||
($Nix::Config::config{"untrusted-use-binary-caches"} // "true") eq "false";
|
||||
|
||||
sub strToList {
|
||||
my ($s) = @_;
|
||||
return map { s/\/+$//; $_ } split(/ /, $s);
|
||||
}
|
||||
|
||||
my @urls = strToList($Nix::Config::config{"binary-caches"} //
|
||||
($Nix::Config::storeDir eq "/nix/store" ? "http://nixos.org/binary-cache" : ""));
|
||||
|
||||
my $urlsFiles = $Nix::Config::config{"binary-cache-files"}
|
||||
// "$Nix::Config::stateDir/profiles/per-user/$userName/channels/binary-caches/*";
|
||||
foreach my $urlFile (glob $urlsFiles) {
|
||||
next unless -f $urlFile;
|
||||
open FILE, "<$urlFile" or die "cannot open ‘$urlFile’\n";
|
||||
my $url = <FILE>; chomp $url;
|
||||
close FILE;
|
||||
push @urls, strToList($url);
|
||||
}
|
||||
|
||||
# Allow Nix daemon users to override the binary caches to a subset
|
||||
# of those listed in the config file. Note that ‘untrusted-*’
|
||||
# denotes options passed by the client.
|
||||
if (defined $Nix::Config::config{"untrusted-binary-caches"}) {
|
||||
my @untrustedUrls = strToList $Nix::Config::config{"untrusted-binary-caches"};
|
||||
my @trustedUrls = uniq(@urls, strToList($Nix::Config::config{"trusted-binary-caches"} // ""));
|
||||
@urls = ();
|
||||
foreach my $url (@untrustedUrls) {
|
||||
die "binary cache ‘$url’ is not trusted (please add it to ‘trusted-binary-caches’ [@trustedUrls] in $Nix::Config::confDir/nix.conf)\n"
|
||||
unless scalar(grep { $url eq $_ } @trustedUrls) > 0;
|
||||
push @urls, $url;
|
||||
}
|
||||
}
|
||||
|
||||
foreach my $url (uniq @urls) {
|
||||
|
||||
# FIXME: not atomic.
|
||||
$queryCache->execute($url);
|
||||
my $res = $queryCache->fetchrow_hashref();
|
||||
if (defined $res) {
|
||||
next if $res->{storeDir} ne $Nix::Config::storeDir;
|
||||
push @caches, { id => $res->{id}, url => $url, wantMassQuery => $res->{wantMassQuery}, priority => $res->{priority} };
|
||||
next;
|
||||
}
|
||||
|
||||
# Get the cache info file.
|
||||
my $request = addRequest(undef, $url . "/nix-cache-info");
|
||||
processRequests;
|
||||
|
||||
if ($request->{result} != 0) {
|
||||
print STDERR "could not download ‘$request->{url}’ (" .
|
||||
($request->{result} != 0 ? "Curl error $request->{result}" : "HTTP status $request->{httpStatus}") . ")\n";
|
||||
next;
|
||||
}
|
||||
|
||||
my $storeDir = "/nix/store";
|
||||
my $wantMassQuery = 0;
|
||||
my $priority = 50;
|
||||
foreach my $line (split "\n", $request->{content}) {
|
||||
unless ($line =~ /^(.*): (.*)$/) {
|
||||
print STDERR "bad cache info file ‘$request->{url}’\n";
|
||||
return undef;
|
||||
}
|
||||
if ($1 eq "StoreDir") { $storeDir = $2; }
|
||||
elsif ($1 eq "WantMassQuery") { $wantMassQuery = int($2); }
|
||||
elsif ($1 eq "Priority") { $priority = int($2); }
|
||||
}
|
||||
|
||||
$dbh->do("insert into BinaryCaches(url, timestamp, storeDir, wantMassQuery, priority) values (?, ?, ?, ?, ?)",
|
||||
{}, $url, time(), $storeDir, $wantMassQuery, $priority);
|
||||
my $id = $dbh->last_insert_id("", "", "", "");
|
||||
next if $storeDir ne $Nix::Config::storeDir;
|
||||
push @caches, { id => $id, url => $url, wantMassQuery => $wantMassQuery, priority => $priority };
|
||||
}
|
||||
|
||||
@caches = sort { $a->{priority} <=> $b->{priority} } @caches;
|
||||
|
||||
expireNegative();
|
||||
}
|
||||
|
||||
|
||||
sub processNARInfo {
|
||||
my ($storePath, $cache, $request) = @_;
|
||||
|
||||
if ($request->{result} != 0) {
|
||||
if ($request->{result} != 37 && $request->{httpStatus} != 404) {
|
||||
print STDERR "could not download ‘$request->{url}’ (" .
|
||||
($request->{result} != 0 ? "Curl error $request->{result}" : "HTTP status $request->{httpStatus}") . ")\n";
|
||||
} else {
|
||||
$insertNARExistence->execute($cache->{id}, basename($storePath), 0, time())
|
||||
unless $request->{url} =~ /^file:/;
|
||||
}
|
||||
return undef;
|
||||
}
|
||||
|
||||
my $narInfo = parseNARInfo($storePath, $request->{content});
|
||||
return undef unless defined $narInfo;
|
||||
|
||||
# Cache the result.
|
||||
$insertNAR->execute(
|
||||
$cache->{id}, basename($storePath), $narInfo->{url}, $narInfo->{compression},
|
||||
$narInfo->{fileHash}, $narInfo->{fileSize}, $narInfo->{narHash}, $narInfo->{narSize},
|
||||
join(" ", @{$narInfo->{refs}}), $narInfo->{deriver}, $narInfo->{system}, time())
|
||||
unless $request->{url} =~ /^file:/;
|
||||
|
||||
return $narInfo;
|
||||
}
|
||||
|
||||
|
||||
sub getCachedInfoFrom {
|
||||
my ($storePath, $cache) = @_;
|
||||
|
||||
$queryNAR->execute($cache->{id}, basename($storePath));
|
||||
my $res = $queryNAR->fetchrow_hashref();
|
||||
return undef unless defined $res;
|
||||
|
||||
return
|
||||
{ url => $res->{url}
|
||||
, compression => $res->{compression}
|
||||
, fileHash => $res->{fileHash}
|
||||
, fileSize => $res->{fileSize}
|
||||
, narHash => $res->{narHash}
|
||||
, narSize => $res->{narSize}
|
||||
, refs => [ split " ", $res->{refs} ]
|
||||
, deriver => $res->{deriver}
|
||||
} if defined $res;
|
||||
}
|
||||
|
||||
|
||||
sub negativeHit {
|
||||
my ($storePath, $cache) = @_;
|
||||
$queryNARExistence->execute($cache->{id}, basename($storePath));
|
||||
my $res = $queryNARExistence->fetchrow_hashref();
|
||||
return defined $res && $res->{exist} == 0 && time() - $res->{timestamp} < $ttlNegativeUse;
|
||||
}
|
||||
|
||||
|
||||
sub positiveHit {
|
||||
my ($storePath, $cache) = @_;
|
||||
return 1 if defined getCachedInfoFrom($storePath, $cache);
|
||||
$queryNARExistence->execute($cache->{id}, basename($storePath));
|
||||
my $res = $queryNARExistence->fetchrow_hashref();
|
||||
return defined $res && $res->{exist} == 1;
|
||||
}
|
||||
|
||||
|
||||
sub expireNegative {
|
||||
return if $didExpiration;
|
||||
$didExpiration = 1;
|
||||
my $time = time();
|
||||
# Round up to the next multiple of the TTL to ensure that we do
|
||||
# expiration only once per time interval. E.g. if $ttlNegative ==
|
||||
# 3600, we expire entries at most once per hour. This is
|
||||
# presumably faster than expiring a few entries per request (and
|
||||
# thus doing a transaction).
|
||||
my $limit = (int($time / $ttlNegative) - 1) * $ttlNegative;
|
||||
$expireNARExistence->execute($limit, 0);
|
||||
print STDERR "expired ", $expireNARExistence->rows, " negative entries\n" if $debug;
|
||||
}
|
||||
|
||||
|
||||
sub printInfo {
|
||||
my ($storePath, $info) = @_;
|
||||
print "$storePath\n";
|
||||
print $info->{deriver} ? "$Nix::Config::storeDir/$info->{deriver}" : "", "\n";
|
||||
print scalar @{$info->{refs}}, "\n";
|
||||
print "$Nix::Config::storeDir/$_\n" foreach @{$info->{refs}};
|
||||
print $info->{fileSize} || 0, "\n";
|
||||
print $info->{narSize} || 0, "\n";
|
||||
}
|
||||
|
||||
|
||||
sub infoUrl {
|
||||
my ($binaryCacheUrl, $storePath) = @_;
|
||||
my $pathHash = substr(basename($storePath), 0, 32);
|
||||
my $infoUrl = "$binaryCacheUrl/$pathHash.narinfo";
|
||||
}
|
||||
|
||||
|
||||
sub printInfoParallel {
|
||||
my @paths = @_;
|
||||
|
||||
# First print all paths for which we have cached info.
|
||||
my @left;
|
||||
foreach my $storePath (@paths) {
|
||||
my $found = 0;
|
||||
foreach my $cache (@caches) {
|
||||
my $info = getCachedInfoFrom($storePath, $cache);
|
||||
if (defined $info) {
|
||||
printInfo($storePath, $info);
|
||||
$found = 1;
|
||||
last;
|
||||
}
|
||||
}
|
||||
push @left, $storePath if !$found;
|
||||
}
|
||||
|
||||
return if scalar @left == 0;
|
||||
|
||||
foreach my $cache (@caches) {
|
||||
|
||||
my @left2;
|
||||
%requests = ();
|
||||
foreach my $storePath (@left) {
|
||||
if (negativeHit($storePath, $cache)) {
|
||||
push @left2, $storePath;
|
||||
next;
|
||||
}
|
||||
addRequest($storePath, infoUrl($cache->{url}, $storePath));
|
||||
}
|
||||
|
||||
processRequests;
|
||||
|
||||
foreach my $request (values %requests) {
|
||||
my $info = processNARInfo($request->{storePath}, $cache, $request);
|
||||
if (defined $info) {
|
||||
printInfo($request->{storePath}, $info);
|
||||
} else {
|
||||
push @left2, $request->{storePath};
|
||||
}
|
||||
}
|
||||
|
||||
@left = @left2;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
sub printSubstitutablePaths {
|
||||
my @paths = @_;
|
||||
|
||||
# First look for paths that have cached info.
|
||||
my @left;
|
||||
foreach my $storePath (@paths) {
|
||||
my $found = 0;
|
||||
foreach my $cache (@caches) {
|
||||
next unless $cache->{wantMassQuery};
|
||||
if (positiveHit($storePath, $cache)) {
|
||||
print "$storePath\n";
|
||||
$found = 1;
|
||||
last;
|
||||
}
|
||||
}
|
||||
push @left, $storePath if !$found;
|
||||
}
|
||||
|
||||
return if scalar @left == 0;
|
||||
|
||||
# For remaining paths, do HEAD requests.
|
||||
foreach my $cache (@caches) {
|
||||
next unless $cache->{wantMassQuery};
|
||||
my @left2;
|
||||
%requests = ();
|
||||
foreach my $storePath (@left) {
|
||||
if (negativeHit($storePath, $cache)) {
|
||||
push @left2, $storePath;
|
||||
next;
|
||||
}
|
||||
addRequest($storePath, infoUrl($cache->{url}, $storePath), 1);
|
||||
}
|
||||
|
||||
processRequests;
|
||||
|
||||
foreach my $request (values %requests) {
|
||||
if ($request->{result} != 0) {
|
||||
if ($request->{result} != 37 && $request->{httpStatus} != 404) {
|
||||
print STDERR "could not check ‘$request->{url}’ (" .
|
||||
($request->{result} != 0 ? "Curl error $request->{result}" : "HTTP status $request->{httpStatus}") . ")\n";
|
||||
} else {
|
||||
$insertNARExistence->execute($cache->{id}, basename($request->{storePath}), 0, time())
|
||||
unless $request->{url} =~ /^file:/;
|
||||
}
|
||||
push @left2, $request->{storePath};
|
||||
} else {
|
||||
$insertNARExistence->execute($cache->{id}, basename($request->{storePath}), 1, time())
|
||||
unless $request->{url} =~ /^file:/;
|
||||
print "$request->{storePath}\n";
|
||||
}
|
||||
}
|
||||
|
||||
@left = @left2;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
sub downloadBinary {
|
||||
my ($storePath, $destPath) = @_;
|
||||
|
||||
foreach my $cache (@caches) {
|
||||
my $info = getCachedInfoFrom($storePath, $cache);
|
||||
|
||||
unless (defined $info) {
|
||||
next if negativeHit($storePath, $cache);
|
||||
my $request = addRequest($storePath, infoUrl($cache->{url}, $storePath));
|
||||
processRequests;
|
||||
$info = processNARInfo($storePath, $cache, $request);
|
||||
}
|
||||
|
||||
next unless defined $info;
|
||||
|
||||
my $decompressor;
|
||||
if ($info->{compression} eq "bzip2") { $decompressor = "$Nix::Config::bzip2 -d"; }
|
||||
elsif ($info->{compression} eq "xz") { $decompressor = "$Nix::Config::xz -d"; }
|
||||
else {
|
||||
print STDERR "unknown compression method ‘$info->{compression}’\n";
|
||||
next;
|
||||
}
|
||||
my $url = "$cache->{url}/$info->{url}"; # FIXME: handle non-relative URLs
|
||||
print STDERR "\n*** Downloading ‘$url’ to ‘$storePath’...\n";
|
||||
checkURL $url;
|
||||
if (system("$Nix::Config::curl --fail --location --insecure '$url' | $decompressor | $Nix::Config::binDir/nix-store --restore $destPath") != 0) {
|
||||
warn "download of `$url' failed" . ($! ? ": $!" : "") . "\n";
|
||||
next;
|
||||
}
|
||||
|
||||
# Tell Nix about the expected hash so it can verify it.
|
||||
print "$info->{narHash}\n";
|
||||
|
||||
print STDERR "\n";
|
||||
return;
|
||||
}
|
||||
|
||||
print STDERR "could not download ‘$storePath’ from any binary cache\n";
|
||||
}
|
||||
|
||||
|
||||
initCache();
|
||||
|
||||
|
||||
if ($ARGV[0] eq "--query") {
|
||||
|
||||
while (<STDIN>) {
|
||||
getAvailableCaches;
|
||||
chomp;
|
||||
my ($cmd, @args) = split " ", $_;
|
||||
|
||||
if ($cmd eq "have") {
|
||||
print STDERR "checking binary caches for existence of @args\n" if $debug;
|
||||
printSubstitutablePaths(@args);
|
||||
print "\n";
|
||||
}
|
||||
|
||||
elsif ($cmd eq "info") {
|
||||
print STDERR "checking binary caches for info on @args\n" if $debug;
|
||||
printInfoParallel(@args);
|
||||
print "\n";
|
||||
}
|
||||
|
||||
else { die "unknown command `$cmd'"; }
|
||||
|
||||
flush STDOUT;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
elsif ($ARGV[0] eq "--substitute") {
|
||||
my $storePath = $ARGV[1] or die;
|
||||
my $destPath = $ARGV[2] or die;
|
||||
getAvailableCaches;
|
||||
downloadBinary($storePath, $destPath);
|
||||
}
|
||||
|
||||
else {
|
||||
die;
|
||||
}
|
||||
@@ -4,6 +4,7 @@ use strict;
|
||||
use Nix::Config;
|
||||
use Nix::Manifest;
|
||||
use Nix::Store;
|
||||
use Nix::Utils;
|
||||
use POSIX qw(strftime);
|
||||
use File::Temp qw(tempdir);
|
||||
|
||||
@@ -15,6 +16,9 @@ my $logFile = "$Nix::Config::logDir/downloads";
|
||||
# estimating the expected download size.
|
||||
my $fast = 1;
|
||||
|
||||
# ‘--insecure’ is fine because Nix verifies the hash of the result.
|
||||
my $curl = "$Nix::Config::curl --fail --location --insecure";
|
||||
|
||||
|
||||
# Open the manifest cache and update it if necessary.
|
||||
my $dbh = updateManifestDB();
|
||||
@@ -38,7 +42,7 @@ sub parseHash {
|
||||
# given path.
|
||||
sub computeSmallestDownload {
|
||||
my $targetPath = shift;
|
||||
|
||||
|
||||
# Build a graph of all store paths that might contribute to the
|
||||
# construction of $targetPath, and the special node "start". The
|
||||
# edges are either patch operations, or downloads of full NAR
|
||||
@@ -93,7 +97,7 @@ sub computeSmallestDownload {
|
||||
my $patchList = $dbh->selectall_arrayref(
|
||||
"select * from Patches where storePath = ?",
|
||||
{ Slice => {} }, $u);
|
||||
|
||||
|
||||
foreach my $patch (@{$patchList}) {
|
||||
if (isValidPath($patch->{basePath})) {
|
||||
my ($baseHashAlgo, $baseHash) = parseHash $patch->{baseHash};
|
||||
@@ -106,7 +110,7 @@ sub computeSmallestDownload {
|
||||
$hash =~ s/.*://;
|
||||
$hashCache->{$baseHashAlgo}->{$patch->{basePath}} = $hash;
|
||||
}
|
||||
|
||||
|
||||
next if $hash ne $baseHash;
|
||||
}
|
||||
push @queue, $patch->{basePath};
|
||||
@@ -117,7 +121,7 @@ sub computeSmallestDownload {
|
||||
my $narFileList = $dbh->selectall_arrayref(
|
||||
"select * from NARs where storePath = ?",
|
||||
{ Slice => {} }, $u);
|
||||
|
||||
|
||||
foreach my $narFile (@{$narFileList}) {
|
||||
# !!! how to handle files whose size is not known in advance?
|
||||
# For now, assume some arbitrary size (1 GB).
|
||||
@@ -173,58 +177,56 @@ sub computeSmallestDownload {
|
||||
if ($ARGV[0] eq "--query") {
|
||||
|
||||
while (<STDIN>) {
|
||||
my $cmd = $_; chomp $cmd;
|
||||
chomp;
|
||||
my ($cmd, @args) = split " ", $_;
|
||||
|
||||
if ($cmd eq "have") {
|
||||
my $storePath = <STDIN>; chomp $storePath;
|
||||
print STDOUT (
|
||||
scalar @{$dbh->selectcol_arrayref("select 1 from NARs where storePath = ?", {}, $storePath)} > 0
|
||||
? "1\n" : "0\n");
|
||||
foreach my $storePath (@args) {
|
||||
print "$storePath\n" if scalar @{$dbh->selectcol_arrayref("select 1 from NARs where storePath = ?", {}, $storePath)} > 0;
|
||||
}
|
||||
print "\n";
|
||||
}
|
||||
|
||||
elsif ($cmd eq "info") {
|
||||
my $storePath = <STDIN>; chomp $storePath;
|
||||
foreach my $storePath (@args) {
|
||||
|
||||
my $infos = $dbh->selectall_arrayref(
|
||||
"select * from NARs where storePath = ?",
|
||||
{ Slice => {} }, $storePath);
|
||||
|
||||
my $info;
|
||||
if (scalar @{$infos} > 0) {
|
||||
$info = @{$infos}[0];
|
||||
}
|
||||
else {
|
||||
print "0\n";
|
||||
next; # not an error
|
||||
}
|
||||
my $infos = $dbh->selectall_arrayref(
|
||||
"select * from NARs where storePath = ?",
|
||||
{ Slice => {} }, $storePath);
|
||||
|
||||
print "1\n";
|
||||
print "$info->{deriver}\n";
|
||||
my @references = split " ", $info->{refs};
|
||||
print scalar @references, "\n";
|
||||
print "$_\n" foreach @references;
|
||||
next unless scalar @{$infos} > 0;
|
||||
my $info = @{$infos}[0];
|
||||
|
||||
my @path = computeSmallestDownload $storePath;
|
||||
print "$storePath\n";
|
||||
print "$info->{deriver}\n";
|
||||
my @references = split " ", $info->{refs};
|
||||
print scalar @references, "\n";
|
||||
print "$_\n" foreach @references;
|
||||
|
||||
my $downloadSize = 0;
|
||||
while (scalar @path > 0) {
|
||||
my $edge = pop @path;
|
||||
my $u = $edge->{start};
|
||||
my $v = $edge->{end};
|
||||
if ($edge->{type} eq "patch") {
|
||||
$downloadSize += $edge->{info}->{size} || 0;
|
||||
}
|
||||
elsif ($edge->{type} eq "narfile") {
|
||||
$downloadSize += $edge->{info}->{size} || 0;
|
||||
my @path = computeSmallestDownload $storePath;
|
||||
|
||||
my $downloadSize = 0;
|
||||
while (scalar @path > 0) {
|
||||
my $edge = pop @path;
|
||||
my $u = $edge->{start};
|
||||
my $v = $edge->{end};
|
||||
if ($edge->{type} eq "patch") {
|
||||
$downloadSize += $edge->{info}->{size} || 0;
|
||||
}
|
||||
elsif ($edge->{type} eq "narfile") {
|
||||
$downloadSize += $edge->{info}->{size} || 0;
|
||||
}
|
||||
}
|
||||
|
||||
print "$downloadSize\n";
|
||||
|
||||
my $narSize = $info->{narSize} || 0;
|
||||
print "$narSize\n";
|
||||
}
|
||||
|
||||
print "$downloadSize\n";
|
||||
|
||||
my $narSize = $info->{narSize} || 0;
|
||||
print "$narSize\n";
|
||||
print "\n";
|
||||
}
|
||||
|
||||
|
||||
else { die "unknown command `$cmd'"; }
|
||||
}
|
||||
|
||||
@@ -236,8 +238,9 @@ elsif ($ARGV[0] ne "--substitute") {
|
||||
}
|
||||
|
||||
|
||||
die unless scalar @ARGV == 2;
|
||||
die unless scalar @ARGV == 3;
|
||||
my $targetPath = $ARGV[1];
|
||||
my $destPath = $ARGV[2];
|
||||
$fast = 0;
|
||||
|
||||
|
||||
@@ -273,16 +276,6 @@ $dbh->disconnect;
|
||||
my $curStep = 1;
|
||||
my $maxStep = scalar @path;
|
||||
|
||||
sub downloadFile {
|
||||
my $url = shift;
|
||||
$ENV{"PRINT_PATH"} = 1;
|
||||
$ENV{"QUIET"} = 1;
|
||||
my ($hash, $path) = `$Nix::Config::binDir/nix-prefetch-url '$url'`;
|
||||
die "download of `$url' failed" . ($! ? ": $!" : "") . "\n" unless $? == 0;
|
||||
chomp $path;
|
||||
return $path;
|
||||
}
|
||||
|
||||
my $finalNarHash;
|
||||
|
||||
while (scalar @path > 0) {
|
||||
@@ -314,13 +307,16 @@ while (scalar @path > 0) {
|
||||
|
||||
# Download the patch.
|
||||
print STDERR " downloading patch...\n";
|
||||
my $patchPath = downloadFile "$patch->{url}";
|
||||
my $patchPath = "$tmpDir/patch";
|
||||
checkURL $patch->{url};
|
||||
system("$curl '$patch->{url}' -o $patchPath") == 0
|
||||
or die "cannot download patch `$patch->{url}'\n";
|
||||
|
||||
# Apply the patch to the NAR archive produced in step 1 (for
|
||||
# the already present path) or a later step (for patch sequences).
|
||||
print STDERR " applying patch...\n";
|
||||
system("$Nix::Config::libexecDir/bspatch $tmpNar $tmpNar2 $patchPath") == 0
|
||||
or die "cannot apply patch `$patchPath' to $tmpNar";
|
||||
or die "cannot apply patch `$patchPath' to $tmpNar\n";
|
||||
|
||||
if ($curStep < $maxStep) {
|
||||
# The archive will be used as the base of the next patch.
|
||||
@@ -329,8 +325,8 @@ while (scalar @path > 0) {
|
||||
# This was the last patch. Unpack the final NAR archive
|
||||
# into the target path.
|
||||
print STDERR " unpacking patched archive...\n";
|
||||
system("$Nix::Config::binDir/nix-store --restore $v < $tmpNar2") == 0
|
||||
or die "cannot unpack $tmpNar2 into `$v'";
|
||||
system("$Nix::Config::binDir/nix-store --restore $destPath < $tmpNar2") == 0
|
||||
or die "cannot unpack $tmpNar2 to `$v'\n";
|
||||
}
|
||||
|
||||
$finalNarHash = $patch->{narHash};
|
||||
@@ -338,24 +334,26 @@ while (scalar @path > 0) {
|
||||
|
||||
elsif ($edge->{type} eq "narfile") {
|
||||
my $narFile = $edge->{info};
|
||||
print STDERR "downloading `$narFile->{url}' into `$v'\n";
|
||||
print STDERR "downloading `$narFile->{url}' to `$v'\n";
|
||||
|
||||
my $size = $narFile->{size} || -1;
|
||||
print LOGFILE "$$ narfile $narFile->{url} $size $v\n";
|
||||
|
||||
# Download the archive.
|
||||
print STDERR " downloading archive...\n";
|
||||
my $narFilePath = downloadFile "$narFile->{url}";
|
||||
|
||||
checkURL $narFile->{url};
|
||||
|
||||
my $decompressor =
|
||||
$narFile->{compressionType} eq "bzip2" ? "$Nix::Config::bzip2 -d" :
|
||||
$narFile->{compressionType} eq "xz" ? "$Nix::Config::xz -d" :
|
||||
die "unknown compression type `$narFile->{compressionType}'";
|
||||
|
||||
if ($curStep < $maxStep) {
|
||||
# The archive will be used a base to a patch.
|
||||
system("$Nix::Config::bzip2 -d < '$narFilePath' > $tmpNar") == 0
|
||||
or die "cannot unpack `$narFilePath' into `$v'";
|
||||
system("$curl '$narFile->{url}' | $decompressor > $tmpNar") == 0
|
||||
or die "cannot download and unpack `$narFile->{url}' to `$v'\n";
|
||||
} else {
|
||||
# Unpack the archive into the target path.
|
||||
print STDERR " unpacking archive...\n";
|
||||
system("$Nix::Config::bzip2 -d < '$narFilePath' | $Nix::Config::binDir/nix-store --restore '$v'") == 0
|
||||
or die "cannot unpack `$narFilePath' into `$v'";
|
||||
# Unpack the archive to the target path.
|
||||
system("$curl '$narFile->{url}' | $decompressor | $Nix::Config::binDir/nix-store --restore '$destPath'") == 0
|
||||
or die "cannot download and unpack `$narFile->{url}' to `$v'\n";
|
||||
}
|
||||
|
||||
$finalNarHash = $narFile->{narHash};
|
||||
@@ -365,21 +363,10 @@ while (scalar @path > 0) {
|
||||
}
|
||||
|
||||
|
||||
# Make sure that the hash declared in the manifest matches what we
|
||||
# downloaded and unpacked.
|
||||
|
||||
if (defined $finalNarHash) {
|
||||
my ($hashAlgo, $hash) = parseHash $finalNarHash;
|
||||
|
||||
# The hash in the manifest can be either in base-16 or base-32.
|
||||
# Handle both.
|
||||
my $hash2 = hashPath($hashAlgo, $hashAlgo eq "sha256" && length($hash) != 64, $targetPath);
|
||||
|
||||
die "hash mismatch in downloaded path $targetPath; expected $hash, got $hash2\n"
|
||||
if $hash ne $hash2;
|
||||
} else {
|
||||
die "cannot check integrity of the downloaded path since its hash is not known\n";
|
||||
}
|
||||
# Tell Nix about the expected hash so it can verify it.
|
||||
die "cannot check integrity of the downloaded path since its hash is not known\n"
|
||||
unless defined $finalNarHash;
|
||||
print "$finalNarHash\n";
|
||||
|
||||
|
||||
print STDERR "\n";
|
||||
|
||||
+34
-29
@@ -33,29 +33,12 @@ for (my $n = 0; $n < scalar @ARGV; $n++) {
|
||||
my $arg = $ARGV[$n];
|
||||
|
||||
if ($arg eq "--help") {
|
||||
print STDERR <<EOF;
|
||||
Usage: nix-build [OPTION]... [FILE]...
|
||||
exec "man nix-build" or die;
|
||||
}
|
||||
|
||||
`nix-build' builds the given Nix expressions (which
|
||||
default to ./default.nix if none are given). A symlink called
|
||||
`result' is placed in the current directory.
|
||||
|
||||
Flags:
|
||||
--add-drv-link: create a symlink `derivation' to the store derivation
|
||||
--drv-link NAME: create symlink NAME instead of `derivation'
|
||||
--no-out-link: do not create the `result' symlink
|
||||
--out-link / -o NAME: create symlink NAME instead of `result'
|
||||
--attr / -A ATTR: select a specific attribute from the Nix expression
|
||||
|
||||
--run-env: build dependencies of the specified derivation, then start a
|
||||
shell with the environment of the derivation
|
||||
--command: command to run with `--run-env'
|
||||
--exclude: regexp specifying dependencies to be excluded by `--run-env'
|
||||
|
||||
Any additional flags are passed to `nix-store'.
|
||||
EOF
|
||||
elsif ($arg eq "--version") {
|
||||
print "nix-build (Nix) $Nix::Config::version\n";
|
||||
exit 0;
|
||||
# '` hack
|
||||
}
|
||||
|
||||
elsif ($arg eq "--add-drv-link") {
|
||||
@@ -114,26 +97,30 @@ EOF
|
||||
push @buildArgs, "--dry-run";
|
||||
$dryRun = 1;
|
||||
}
|
||||
|
||||
|
||||
elsif ($arg eq "--show-trace") {
|
||||
push @instArgs, $arg;
|
||||
}
|
||||
|
||||
|
||||
elsif ($arg eq "-") {
|
||||
@exprs = ("-");
|
||||
}
|
||||
|
||||
elsif ($arg eq "--verbose" or substr($arg, 0, 2) eq "-v") {
|
||||
push @buildArgs, $arg;
|
||||
push @instArgs, $arg;
|
||||
$verbose = 1;
|
||||
}
|
||||
|
||||
elsif ($arg eq "--quiet") {
|
||||
|
||||
elsif ($arg eq "--quiet" || $arg eq "--repair") {
|
||||
push @buildArgs, $arg;
|
||||
push @instArgs, $arg;
|
||||
}
|
||||
|
||||
|
||||
elsif ($arg eq "--run-env") {
|
||||
$runEnv = 1;
|
||||
}
|
||||
|
||||
|
||||
elsif ($arg eq "--command") {
|
||||
$n++;
|
||||
die "$0: `$arg' requires an argument\n" unless $n < scalar @ARGV;
|
||||
@@ -194,15 +181,33 @@ foreach my $expr (@exprs) {
|
||||
die;
|
||||
}
|
||||
|
||||
# Ugly hackery to make "nix-build -A foo.all" produce symlinks
|
||||
# ./result, ./result-dev, and so on, rather than ./result,
|
||||
# ./result-2-dev, and so on. This combines multiple derivation
|
||||
# paths into one "/nix/store/drv-path!out1,out2,..." argument.
|
||||
my $prevDrvPath = "";
|
||||
my @drvPaths2;
|
||||
foreach my $drvPath (@drvPaths) {
|
||||
my $target = readlink $drvPath or die "cannot read symlink `$drvPath'";
|
||||
my $p = $drvPath; my $output = "out";
|
||||
if ($drvPath =~ /(.*)!(.*)/) {
|
||||
$p = $1; $output = $2;
|
||||
} else {
|
||||
$p = $drvPath;
|
||||
}
|
||||
my $target = readlink $p or die "cannot read symlink `$p'";
|
||||
print STDERR "derivation is $target\n" if $verbose;
|
||||
if ($target eq $prevDrvPath) {
|
||||
push @drvPaths2, (pop @drvPaths2) . "," . $output;
|
||||
} else {
|
||||
push @drvPaths2, $target . "!" . $output;
|
||||
$prevDrvPath = $target;
|
||||
}
|
||||
}
|
||||
|
||||
# Build.
|
||||
my @outPaths;
|
||||
$pid = open(OUTPATHS, "-|") || exec "$Nix::Config::binDir/nix-store", "--add-root", $outLink, "--indirect", "-r",
|
||||
@buildArgs, @drvPaths;
|
||||
@buildArgs, @drvPaths2;
|
||||
while (<OUTPATHS>) {chomp; push @outPaths, $_;}
|
||||
if (!close OUTPATHS) {
|
||||
die "nix-store killed by signal " . ($? & 127) . "\n" if ($? & 127);
|
||||
|
||||
+46
-37
@@ -4,6 +4,9 @@ use strict;
|
||||
use File::Basename;
|
||||
use File::Path qw(mkpath);
|
||||
use Nix::Config;
|
||||
use Nix::Manifest;
|
||||
|
||||
Nix::Config::readConfig;
|
||||
|
||||
my $manifestDir = $Nix::Config::manifestDir;
|
||||
|
||||
@@ -22,7 +25,7 @@ my $nixDefExpr = "$home/.nix-defexpr";
|
||||
my $userName = getpwuid($<) or die "cannot figure out user name";
|
||||
my $profile = "$Nix::Config::stateDir/profiles/per-user/$userName/channels";
|
||||
mkpath(dirname $profile, 0, 0755);
|
||||
|
||||
|
||||
my %channels;
|
||||
|
||||
|
||||
@@ -65,11 +68,13 @@ sub addChannel {
|
||||
sub removeChannel {
|
||||
my ($name) = @_;
|
||||
readChannels;
|
||||
my $url = $channels{$name};
|
||||
deleteOldManifests($url . "/MANIFEST", undef) if defined $url;
|
||||
delete $channels{$name};
|
||||
writeChannels;
|
||||
|
||||
system("$Nix::Config::binDir/nix-env --profile '$profile' -e '$name'") == 0
|
||||
or die "cannot remove channel `$name'";
|
||||
or die "cannot remove channel `$name'\n";
|
||||
}
|
||||
|
||||
|
||||
@@ -77,20 +82,14 @@ sub removeChannel {
|
||||
# channels.
|
||||
sub update {
|
||||
my @channelNames = @_;
|
||||
|
||||
|
||||
readChannels;
|
||||
|
||||
# Create the manifests directory if it doesn't exist.
|
||||
mkdir $manifestDir, 0755 unless -e $manifestDir;
|
||||
|
||||
# Do we have write permission to the manifests directory?
|
||||
die "$0: you do not have write permission to `$manifestDir'!\n" unless -W $manifestDir;
|
||||
|
||||
# Download each channel.
|
||||
my $exprs = "";
|
||||
foreach my $name (keys %channels) {
|
||||
next if scalar @channelNames > 0 && ! grep { $_ eq $name } @{channelNames};
|
||||
|
||||
|
||||
my $url = $channels{$name};
|
||||
my $origUrl = "$url/MANIFEST";
|
||||
|
||||
@@ -101,17 +100,34 @@ sub update {
|
||||
die "$0: unable to check `$url'\n" if $? != 0;
|
||||
$headers =~ s/\r//g;
|
||||
$url = $1 if $headers =~ /^Location:\s*(.*)\s*$/m;
|
||||
|
||||
# Pull the channel manifest.
|
||||
$ENV{'NIX_ORIG_URL'} = $origUrl;
|
||||
system("$Nix::Config::binDir/nix-pull", "--skip-wrong-store", "$url/MANIFEST") == 0
|
||||
or die "cannot pull manifest from `$url'\n";
|
||||
|
||||
# Check if the channel advertises a binary cache.
|
||||
my $binaryCacheURL = `$Nix::Config::curl --silent '$url'/binary-cache-url`;
|
||||
my $extraAttrs = "";
|
||||
my $getManifest = ($Nix::Config::config{"force-manifest"} // "false") eq "true";
|
||||
if ($? == 0 && $binaryCacheURL ne "") {
|
||||
$extraAttrs .= "binaryCacheURL = \"$binaryCacheURL\"; ";
|
||||
deleteOldManifests($origUrl, undef);
|
||||
} else {
|
||||
$getManifest = 1;
|
||||
}
|
||||
|
||||
if ($getManifest) {
|
||||
# No binary cache, so pull the channel manifest.
|
||||
mkdir $manifestDir, 0755 unless -e $manifestDir;
|
||||
die "$0: you do not have write permission to `$manifestDir'!\n" unless -W $manifestDir;
|
||||
$ENV{'NIX_ORIG_URL'} = $origUrl;
|
||||
system("$Nix::Config::binDir/nix-pull", "--skip-wrong-store", "$url/MANIFEST") == 0
|
||||
or die "cannot pull manifest from `$url'\n";
|
||||
}
|
||||
|
||||
# Download the channel tarball.
|
||||
my $fullURL = "$url/nixexprs.tar.bz2";
|
||||
my $fullURL = "$url/nixexprs.tar.xz";
|
||||
system("$Nix::Config::curl --fail --silent --head '$fullURL' > /dev/null") == 0 or
|
||||
$fullURL = "$url/nixexprs.tar.bz2";
|
||||
print STDERR "downloading Nix expressions from `$fullURL'...\n";
|
||||
my ($hash, $path) = `PRINT_PATH=1 QUIET=1 $Nix::Config::binDir/nix-prefetch-url '$fullURL'`;
|
||||
die "cannot fetch `$fullURL'" if $? != 0;
|
||||
die "cannot fetch `$fullURL'\n" if $? != 0;
|
||||
chomp $path;
|
||||
|
||||
# If the URL contains a version number, append it to the name
|
||||
@@ -120,7 +136,7 @@ sub update {
|
||||
my $cname = $name;
|
||||
$cname .= $1 if basename($url) =~ /(-\d.*)$/;
|
||||
|
||||
$exprs .= "'f: f { name = \"$cname\"; channelName = \"$name\"; src = builtins.storePath \"$path\"; }' ";
|
||||
$exprs .= "'f: f { name = \"$cname\"; channelName = \"$name\"; src = builtins.storePath \"$path\"; $extraAttrs }' ";
|
||||
}
|
||||
|
||||
# Unpack the channel tarballs into the Nix store and install them
|
||||
@@ -139,26 +155,14 @@ sub update {
|
||||
}
|
||||
|
||||
|
||||
sub usageError {
|
||||
print STDERR <<EOF;
|
||||
Usage:
|
||||
nix-channel --add URL [CHANNEL-NAME]
|
||||
nix-channel --remove CHANNEL-NAME
|
||||
nix-channel --list
|
||||
nix-channel --update [CHANNEL-NAME...]
|
||||
EOF
|
||||
exit 1;
|
||||
}
|
||||
|
||||
|
||||
usageError if scalar @ARGV == 0;
|
||||
die "$0: argument expected\n" if scalar @ARGV == 0;
|
||||
|
||||
|
||||
while (scalar @ARGV) {
|
||||
my $arg = shift @ARGV;
|
||||
|
||||
if ($arg eq "--add") {
|
||||
usageError if scalar @ARGV < 1 || scalar @ARGV > 2;
|
||||
die "$0: `--add' requires one or two arguments\n" if scalar @ARGV < 1 || scalar @ARGV > 2;
|
||||
my $url = shift @ARGV;
|
||||
my $name = shift @ARGV;
|
||||
unless (defined $name) {
|
||||
@@ -171,13 +175,13 @@ while (scalar @ARGV) {
|
||||
}
|
||||
|
||||
if ($arg eq "--remove") {
|
||||
usageError if scalar @ARGV != 1;
|
||||
die "$0: `--remove' requires one argument\n" if scalar @ARGV != 1;
|
||||
removeChannel(shift @ARGV);
|
||||
last;
|
||||
}
|
||||
|
||||
if ($arg eq "--list") {
|
||||
usageError if scalar @ARGV != 0;
|
||||
die "$0: `--list' requires one argument\n" if scalar @ARGV != 0;
|
||||
readChannels;
|
||||
foreach my $name (keys %channels) {
|
||||
print "$name $channels{$name}\n";
|
||||
@@ -189,12 +193,17 @@ while (scalar @ARGV) {
|
||||
update(@ARGV);
|
||||
last;
|
||||
}
|
||||
|
||||
|
||||
elsif ($arg eq "--help") {
|
||||
usageError;
|
||||
exec "man nix-channel" or die;
|
||||
}
|
||||
|
||||
elsif ($arg eq "--version") {
|
||||
print "nix-channel (Nix) $Nix::Config::version\n";
|
||||
exit 0;
|
||||
}
|
||||
|
||||
else {
|
||||
die "unknown argument `$arg'; try `--help'";
|
||||
die "unknown argument `$arg'; try `--help'\n";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,10 +9,15 @@ my $profilesDir = "@localstatedir@/nix/profiles";
|
||||
# Process the command line arguments.
|
||||
my @args = ();
|
||||
my $removeOld = 0;
|
||||
my $dryRun = 0;
|
||||
|
||||
for my $arg (@ARGV) {
|
||||
if ($arg eq "--delete-old" || $arg eq "-d") {
|
||||
$removeOld = 1;
|
||||
} elsif ($arg eq "--dry-run") {
|
||||
$dryRun = 1;
|
||||
} elsif ($arg eq "--help") {
|
||||
exec "man nix-collect-garbage" or die;
|
||||
} else {
|
||||
push @args, $arg;
|
||||
}
|
||||
@@ -35,13 +40,13 @@ sub removeOldGenerations {
|
||||
$name = $dir . "/" . $name;
|
||||
if (-l $name && (readlink($name) =~ /link/)) {
|
||||
print STDERR "removing old generations of profile $name\n";
|
||||
system("$Nix::Config::binDir/nix-env", "-p", $name, "--delete-generations", "old");
|
||||
system("$Nix::Config::binDir/nix-env", "-p", $name, "--delete-generations", "old", $dryRun ? "--dry-run" : ());
|
||||
}
|
||||
elsif (! -l $name && -d $name) {
|
||||
removeOldGenerations $name;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
closedir $dh or die;
|
||||
}
|
||||
|
||||
@@ -49,4 +54,4 @@ removeOldGenerations $profilesDir if $removeOld;
|
||||
|
||||
|
||||
# Run the actual garbage collector.
|
||||
exec "$Nix::Config::binDir/nix-store", "--gc", @args;
|
||||
exec "$Nix::Config::binDir/nix-store", "--gc", @args unless $dryRun;
|
||||
|
||||
+19
-13
@@ -17,19 +17,14 @@ EOF
|
||||
|
||||
# Get the target host.
|
||||
my $sshHost;
|
||||
|
||||
my $sign = 0;
|
||||
|
||||
my $compressor = "";
|
||||
my $decompressor = "";
|
||||
|
||||
my $progressViewer = "";
|
||||
|
||||
my $toMode = 1;
|
||||
|
||||
my $includeOutputs = 0;
|
||||
|
||||
my $dryRun = 0;
|
||||
my $useSubstitutes = 0;
|
||||
|
||||
|
||||
# !!! Copied from nix-pack-closure, should put this in a module.
|
||||
@@ -37,8 +32,11 @@ my @storePaths = ();
|
||||
|
||||
while (@ARGV) {
|
||||
my $arg = shift @ARGV;
|
||||
|
||||
if ($arg eq "--sign") {
|
||||
|
||||
if ($arg eq "--help") {
|
||||
exec "man nix-copy-closure" or die;
|
||||
}
|
||||
elsif ($arg eq "--sign") {
|
||||
$sign = 1;
|
||||
}
|
||||
elsif ($arg eq "--gzip") {
|
||||
@@ -68,6 +66,9 @@ while (@ARGV) {
|
||||
elsif ($arg eq "--dry-run") {
|
||||
$dryRun = 1;
|
||||
}
|
||||
elsif ($arg eq "--use-substitutes" || $arg eq "-s") {
|
||||
$useSubstitutes = 1;
|
||||
}
|
||||
elsif (!defined $sshHost) {
|
||||
$sshHost = $arg;
|
||||
}
|
||||
@@ -81,7 +82,9 @@ openSSHConnection $sshHost or die "$0: unable to start SSH\n";
|
||||
|
||||
|
||||
if ($toMode) { # Copy TO the remote machine.
|
||||
Nix::CopyClosure::copyTo($sshHost, [ @sshOpts ], [ @storePaths ], $compressor, $decompressor, $includeOutputs, $dryRun, $sign, $progressViewer);
|
||||
Nix::CopyClosure::copyTo(
|
||||
$sshHost, [ @sshOpts ], [ @storePaths ], $compressor, $decompressor,
|
||||
$includeOutputs, $dryRun, $sign, $progressViewer, $useSubstitutes);
|
||||
}
|
||||
|
||||
else { # Copy FROM the remote machine.
|
||||
@@ -92,7 +95,7 @@ else { # Copy FROM the remote machine.
|
||||
my $extraOpts = $includeOutputs ? "--include-outputs" : "";
|
||||
my $pid = open(READ,
|
||||
"set -f; ssh @sshOpts $sshHost nix-store --query --requisites $extraOpts @storePaths|") or die;
|
||||
|
||||
|
||||
while (<READ>) {
|
||||
chomp;
|
||||
die "bad: $_" unless /^\//;
|
||||
@@ -104,10 +107,13 @@ else { # Copy FROM the remote machine.
|
||||
# Export the store paths on the remote machine and import them locally.
|
||||
if (scalar @missing > 0) {
|
||||
print STDERR "copying ", scalar @missing, " missing paths from ‘$sshHost’...\n";
|
||||
$compressor = "| $compressor" if $compressor ne "";
|
||||
$decompressor = "$decompressor |" if $decompressor ne "";
|
||||
$progressViewer = "$progressViewer |" if $progressViewer ne "";
|
||||
unless ($dryRun) {
|
||||
if ($useSubstitutes) {
|
||||
system "$Nix::Config::binDir/nix-store -r --ignore-unknown @missing";
|
||||
}
|
||||
$compressor = "| $compressor" if $compressor ne "";
|
||||
$decompressor = "$decompressor |" if $decompressor ne "";
|
||||
$progressViewer = "$progressViewer |" if $progressViewer ne "";
|
||||
my $extraOpts = $sign ? "--sign" : "";
|
||||
system("set -f; ssh $sshHost @sshOpts 'nix-store --export $extraOpts @missing $compressor' | $progressViewer $decompressor $Nix::Config::binDir/nix-store --import > /dev/null") == 0
|
||||
or die "copying store paths from remote machine `$sshHost' failed: $?";
|
||||
|
||||
@@ -3,27 +3,11 @@
|
||||
use strict;
|
||||
use File::Temp qw(tempdir);
|
||||
use Nix::Config;
|
||||
|
||||
|
||||
sub usageError {
|
||||
print STDERR <<EOF;
|
||||
Usage: nix-install-package (FILE | --url URL)
|
||||
|
||||
Install a Nix Package (.nixpkg) either directly from FILE or by
|
||||
downloading it from URL.
|
||||
|
||||
Flags:
|
||||
--profile / -p LINK: install into the specified profile
|
||||
--non-interactive: don't run inside a new terminal
|
||||
EOF
|
||||
; # '
|
||||
exit 1;
|
||||
}
|
||||
use Nix::Utils;
|
||||
|
||||
|
||||
# Parse the command line arguments.
|
||||
my @args = @ARGV;
|
||||
usageError if scalar @args == 0;
|
||||
|
||||
my $source;
|
||||
my $fromURL = 0;
|
||||
@@ -33,14 +17,14 @@ my $interactive = 1;
|
||||
while (scalar @args) {
|
||||
my $arg = shift @args;
|
||||
if ($arg eq "--help") {
|
||||
usageError;
|
||||
exec "man nix-install-package" or die;
|
||||
}
|
||||
elsif ($arg eq "--url") {
|
||||
$fromURL = 1;
|
||||
}
|
||||
elsif ($arg eq "--profile" || $arg eq "-p") {
|
||||
my $profile = shift @args;
|
||||
usageError if !defined $profile;
|
||||
die "$0: `--profile' requires an argument\n" if !defined $profile;
|
||||
push @extraNixEnvArgs, "-p", $profile;
|
||||
}
|
||||
elsif ($arg eq "--non-interactive") {
|
||||
@@ -51,7 +35,7 @@ while (scalar @args) {
|
||||
}
|
||||
}
|
||||
|
||||
usageError unless defined $source;
|
||||
die "$0: please specify a .nixpkg file or URL\n" unless defined $source;
|
||||
|
||||
|
||||
# Re-execute in a terminal, if necessary, so that if we're executed
|
||||
@@ -72,7 +56,7 @@ my $tmpDir = tempdir("nix-install-package.XXXXXX", CLEANUP => 1, TMPDIR => 1)
|
||||
|
||||
sub barf {
|
||||
my $msg = shift;
|
||||
print "$msg\n";
|
||||
print "\nInstallation failed: $msg\n";
|
||||
<STDIN> if $interactive;
|
||||
exit 1;
|
||||
}
|
||||
@@ -92,7 +76,6 @@ open PKGFILE, "<$pkgFile" or barf "cannot open `$pkgFile': $!";
|
||||
my $contents = <PKGFILE>;
|
||||
close PKGFILE;
|
||||
|
||||
my $urlRE = "(?: [a-zA-Z][a-zA-Z0-9\+\-\.]*\:[a-zA-Z0-9\%\/\?\:\@\&\=\+\$\,\-\_\.\!\~\*\']+ )";
|
||||
my $nameRE = "(?: [A-Za-z0-9\+\-\.\_\?\=]+ )"; # see checkStoreName()
|
||||
my $systemRE = "(?: [A-Za-z0-9\+\-\_]+ )";
|
||||
my $pathRE = "(?: \/ [\/A-Za-z0-9\+\-\.\_\?\=]* )";
|
||||
@@ -101,7 +84,7 @@ my $pathRE = "(?: \/ [\/A-Za-z0-9\+\-\.\_\?\=]* )";
|
||||
# store path. We'll let nix-env do that.
|
||||
|
||||
$contents =~
|
||||
/ ^ \s* (\S+) \s+ ($urlRE) \s+ ($nameRE) \s+ ($systemRE) \s+ ($pathRE) \s+ ($pathRE) /x
|
||||
/ ^ \s* (\S+) \s+ ($Nix::Utils::urlRE) \s+ ($nameRE) \s+ ($systemRE) \s+ ($pathRE) \s+ ($pathRE) ( \s+ ($Nix::Utils::urlRE) )? /x
|
||||
or barf "invalid package contents";
|
||||
my $version = $1;
|
||||
my $manifestURL = $2;
|
||||
@@ -109,6 +92,7 @@ my $drvName = $3;
|
||||
my $system = $4;
|
||||
my $drvPath = $5;
|
||||
my $outPath = $6;
|
||||
my $binaryCacheURL = $8;
|
||||
|
||||
barf "invalid package version `$version'" unless $version eq "NIXPKG1";
|
||||
|
||||
@@ -122,17 +106,25 @@ if ($interactive) {
|
||||
}
|
||||
|
||||
|
||||
# Store the manifest in the temporary directory so that we don't
|
||||
# pollute /nix/var/nix/manifests. This also requires that we don't
|
||||
# use the Nix daemon (because otherwise download-using-manifests won't
|
||||
# see our NIX_MANIFESTS_DIRS environment variable).
|
||||
$ENV{NIX_MANIFESTS_DIR} = $tmpDir;
|
||||
$ENV{NIX_REMOTE} = "";
|
||||
if (defined $binaryCacheURL) {
|
||||
|
||||
push @extraNixEnvArgs, "--option", "binary-caches", $binaryCacheURL;
|
||||
|
||||
print "\nPulling manifests...\n";
|
||||
system("$Nix::Config::binDir/nix-pull", $manifestURL) == 0
|
||||
or barf "nix-pull failed: $?";
|
||||
} else {
|
||||
|
||||
# Store the manifest in the temporary directory so that we don't
|
||||
# pollute /nix/var/nix/manifests. This also requires that we
|
||||
# don't use the Nix daemon (because otherwise
|
||||
# download-using-manifests won't see our NIX_MANIFESTS_DIRS
|
||||
# environment variable).
|
||||
$ENV{NIX_MANIFESTS_DIR} = $tmpDir;
|
||||
$ENV{NIX_REMOTE} = "";
|
||||
|
||||
print "\nPulling manifests...\n";
|
||||
system("$Nix::Config::binDir/nix-pull", $manifestURL) == 0
|
||||
or barf "nix-pull failed: $?";
|
||||
|
||||
}
|
||||
|
||||
|
||||
print "\nInstalling package...\n";
|
||||
|
||||
+21
-19
@@ -6,12 +6,30 @@ use File::Temp qw(tempdir);
|
||||
use File::stat;
|
||||
use Nix::Store;
|
||||
use Nix::Config;
|
||||
use Nix::Utils;
|
||||
|
||||
my $url = shift;
|
||||
my $expHash = shift;
|
||||
my $hashType = $ENV{'NIX_HASH_ALGO'} || "sha256";
|
||||
my $hashType = $ENV{'NIX_HASH_ALGO'} || "sha256"; # obsolete
|
||||
my $cacheDir = $ENV{'NIX_DOWNLOAD_CACHE'};
|
||||
|
||||
my @args;
|
||||
my $arg;
|
||||
while ($arg = shift) {
|
||||
if ($arg eq "--help") {
|
||||
exec "man nix-prefetch-url" or die;
|
||||
} elsif ($arg eq "--type") {
|
||||
$hashType = shift;
|
||||
die "$0: `$arg' requires an argument\n" unless defined $hashType;
|
||||
} elsif (substr($arg, 0, 1) eq "-") {
|
||||
die "$0: unknown flag `$arg'\n";
|
||||
} else {
|
||||
push @args, $arg;
|
||||
}
|
||||
}
|
||||
|
||||
my $url = $args[0];
|
||||
my $expHash = $args[1];
|
||||
|
||||
|
||||
if (!defined $url || $url eq "") {
|
||||
print STDERR <<EOF
|
||||
Usage: nix-prefetch-url URL [EXPECTED-HASH]
|
||||
@@ -20,22 +38,6 @@ EOF
|
||||
exit 1;
|
||||
}
|
||||
|
||||
sub writeFile {
|
||||
my ($fn, $s) = @_;
|
||||
open TMP, ">$fn" or die;
|
||||
print TMP "$s" or die;
|
||||
close TMP or die;
|
||||
}
|
||||
|
||||
sub readFile {
|
||||
local $/ = undef;
|
||||
my ($fn) = @_;
|
||||
open TMP, "<$fn" or die;
|
||||
my $s = <TMP>;
|
||||
close TMP or die;
|
||||
return $s;
|
||||
}
|
||||
|
||||
my $tmpDir = tempdir("nix-prefetch-url.XXXXXX", CLEANUP => 1, TMPDIR => 1)
|
||||
or die "cannot create a temporary directory";
|
||||
|
||||
|
||||
+11
-44
@@ -1,50 +1,17 @@
|
||||
export NIX_USER_PROFILE_DIR=@localstatedir@/nix/profiles/per-user/$USER
|
||||
export NIX_PROFILES="@localstatedir@/nix/profiles/default $HOME/.nix-profile"
|
||||
if test -n "$HOME"; then
|
||||
NIX_LINK="$HOME/.nix-profile"
|
||||
|
||||
# Set up the per-user profile.
|
||||
mkdir -m 0755 -p $NIX_USER_PROFILE_DIR
|
||||
if test "$(stat --printf '%u' $NIX_USER_PROFILE_DIR)" != "$(id -u)"; then
|
||||
echo "WARNING: bad ownership on $NIX_USER_PROFILE_DIR" >&2
|
||||
fi
|
||||
|
||||
if ! test -L $HOME/.nix-profile; then
|
||||
echo "creating $HOME/.nix-profile" >&2
|
||||
if test "$USER" != root; then
|
||||
@coreutils@/ln -s $NIX_USER_PROFILE_DIR/profile $HOME/.nix-profile
|
||||
else
|
||||
# Root installs in the system-wide profile by default.
|
||||
@coreutils@/ln -s /nix/var/nix/profiles/default $HOME/.nix-profile
|
||||
# Set the default profile.
|
||||
if ! [ -L "$NIX_LINK" ]; then
|
||||
echo "creating $NIX_LINK" >&2
|
||||
_NIX_DEF_LINK=@localstatedir@/nix/profiles/default
|
||||
@coreutils@/ln -s "$_NIX_DEF_LINK" "$NIX_LINK"
|
||||
fi
|
||||
fi
|
||||
|
||||
export PATH="$HOME/.nix-profile/bin:$PATH"
|
||||
|
||||
# Subscribe the root user to the NixOS channel by default.
|
||||
if [ "$USER" = root -a ! -e $HOME/.nix-channels ]; then
|
||||
echo "http://nixos.org/releases/nixos/channels/nixos-unstable nixos" > $HOME/.nix-channels
|
||||
fi
|
||||
|
||||
# Create the per-user garbage collector roots directory.
|
||||
NIX_USER_GCROOTS_DIR=@localstatedir@/nix/gcroots/per-user/$USER
|
||||
mkdir -m 0755 -p $NIX_USER_GCROOTS_DIR
|
||||
if test "$(stat --printf '%u' $NIX_USER_GCROOTS_DIR)" != "$(id -u)"; then
|
||||
echo "WARNING: bad ownership on $NIX_USER_GCROOTS_DIR" >&2
|
||||
fi
|
||||
|
||||
# Set up a default Nix expression from which to install stuff.
|
||||
if [ ! -e $HOME/.nix-defexpr -o -L $HOME/.nix-defexpr ]; then
|
||||
echo "creating $HOME/.nix-defexpr" >&2
|
||||
rm -f $HOME/.nix-defexpr
|
||||
mkdir $HOME/.nix-defexpr
|
||||
if [ "$USER" != root ]; then
|
||||
@coreutils@/ln -s @localstatedir@/nix/profiles/per-user/root/channels $HOME/.nix-defexpr/channels_root
|
||||
# Subscribe the root user to the Nixpkgs channel by default.
|
||||
if [ ! -e $HOME/.nix-channels ]; then
|
||||
echo "http://nixos.org/channels/nixpkgs-unstable nixpkgs" > $HOME/.nix-channels
|
||||
fi
|
||||
fi
|
||||
|
||||
# Set up secure multi-user builds: non-root users build through the
|
||||
# Nix daemon.
|
||||
if test "$USER" != root; then
|
||||
export NIX_REMOTE=daemon
|
||||
else
|
||||
unset NIX_REMOTE
|
||||
export PATH=$NIX_LINK/bin:$PATH
|
||||
fi
|
||||
|
||||
+6
-15
@@ -76,31 +76,22 @@ sub processURL {
|
||||
open URL, ">$urlFile" or die "cannot create `$urlFile'";
|
||||
print URL $origUrl;
|
||||
close URL;
|
||||
|
||||
|
||||
my $finalPath = "$manifestDir/$baseName-$hash.nixmanifest";
|
||||
|
||||
unlink $finalPath if -e $finalPath;
|
||||
|
||||
|
||||
symlink("$manifest", "$finalPath")
|
||||
or die "cannot link `$finalPath to `$manifest'";
|
||||
|
||||
# Delete all old manifests downloaded from this URL.
|
||||
for my $urlFile2 (glob "$manifestDir/*.url") {
|
||||
next if $urlFile eq $urlFile2;
|
||||
open URL, "<$urlFile2" or die;
|
||||
my $url2 = <URL>;
|
||||
chomp $url2;
|
||||
close URL;
|
||||
next unless $origUrl eq $url2;
|
||||
my $base = $urlFile2; $base =~ s/.url$//;
|
||||
unlink "${base}.url";
|
||||
unlink "${base}.nixmanifest";
|
||||
}
|
||||
deleteOldManifests($origUrl, $urlFile);
|
||||
}
|
||||
|
||||
while (@ARGV) {
|
||||
my $url = shift @ARGV;
|
||||
if ($url eq "--skip-wrong-store") {
|
||||
if ($url eq "--help") {
|
||||
exec "man nix-pull" or die;
|
||||
} elsif ($url eq "--skip-wrong-store") {
|
||||
# No-op, no longer supported.
|
||||
} else {
|
||||
processURL $url;
|
||||
|
||||
+187
-195
@@ -1,85 +1,83 @@
|
||||
#! @perl@ -w @perlFlags@
|
||||
|
||||
use strict;
|
||||
use File::Basename;
|
||||
use File::Temp qw(tempdir);
|
||||
use File::Path qw(mkpath);
|
||||
use File::stat;
|
||||
use File::Copy;
|
||||
use Nix::Config;
|
||||
use Nix::Store;
|
||||
use Nix::Manifest;
|
||||
|
||||
my $hashAlgo = "sha256";
|
||||
use Nix::Utils;
|
||||
|
||||
my $tmpDir = tempdir("nix-push.XXXXXX", CLEANUP => 1, TMPDIR => 1)
|
||||
or die "cannot create a temporary directory";
|
||||
|
||||
my $nixExpr = "$tmpDir/create-nars.nix";
|
||||
my $manifest = "$tmpDir/MANIFEST";
|
||||
|
||||
my $curl = "$Nix::Config::curl --fail --silent";
|
||||
my $extraCurlFlags = ${ENV{'CURL_FLAGS'}};
|
||||
$curl = "$curl $extraCurlFlags" if defined $extraCurlFlags;
|
||||
|
||||
|
||||
# Parse the command line.
|
||||
my $localCopy;
|
||||
my $localArchivesDir;
|
||||
my $localManifestFile;
|
||||
my $compressionType = "xz";
|
||||
my $force = 0;
|
||||
my $destDir;
|
||||
my $writeManifest = 0;
|
||||
my $manifestPath;
|
||||
my $archivesURL;
|
||||
my $link = 0;
|
||||
my @roots;
|
||||
|
||||
my $targetArchivesUrl;
|
||||
for (my $n = 0; $n < scalar @ARGV; $n++) {
|
||||
my $arg = $ARGV[$n];
|
||||
|
||||
my $archivesPutURL;
|
||||
my $archivesGetURL;
|
||||
my $manifestPutURL;
|
||||
|
||||
sub showSyntax {
|
||||
print STDERR <<EOF
|
||||
Usage: nix-push --copy ARCHIVES_DIR MANIFEST_FILE PATHS...
|
||||
or: nix-push ARCHIVES_PUT_URL ARCHIVES_GET_URL MANIFEST_PUT_URL PATHS...
|
||||
|
||||
`nix-push' copies or uploads the closure of PATHS to the given
|
||||
destination.
|
||||
EOF
|
||||
; # `
|
||||
exit 1;
|
||||
}
|
||||
|
||||
showSyntax if scalar @ARGV < 1;
|
||||
|
||||
if ($ARGV[0] eq "--copy") {
|
||||
showSyntax if scalar @ARGV < 3;
|
||||
$localCopy = 1;
|
||||
shift @ARGV;
|
||||
$localArchivesDir = shift @ARGV;
|
||||
$localManifestFile = shift @ARGV;
|
||||
if ($ARGV[0] eq "--target") {
|
||||
shift @ARGV;
|
||||
$targetArchivesUrl = shift @ARGV;
|
||||
}
|
||||
else {
|
||||
$targetArchivesUrl = "file://$localArchivesDir";
|
||||
if ($arg eq "--help") {
|
||||
exec "man nix-push" or die;
|
||||
} elsif ($arg eq "--bzip2") {
|
||||
$compressionType = "bzip2";
|
||||
} elsif ($arg eq "--force") {
|
||||
$force = 1;
|
||||
} elsif ($arg eq "--dest") {
|
||||
$n++;
|
||||
die "$0: `$arg' requires an argument\n" unless $n < scalar @ARGV;
|
||||
$destDir = $ARGV[$n];
|
||||
mkpath($destDir, 0, 0755);
|
||||
} elsif ($arg eq "--manifest") {
|
||||
$writeManifest = 1;
|
||||
} elsif ($arg eq "--manifest-path") {
|
||||
$n++;
|
||||
die "$0: `$arg' requires an argument\n" unless $n < scalar @ARGV;
|
||||
$manifestPath = $ARGV[$n];
|
||||
$writeManifest = 1;
|
||||
mkpath(dirname($manifestPath), 0, 0755);
|
||||
} elsif ($arg eq "--url-prefix") {
|
||||
$n++;
|
||||
die "$0: `$arg' requires an argument\n" unless $n < scalar @ARGV;
|
||||
$archivesURL = $ARGV[$n];
|
||||
} elsif ($arg eq "--link") {
|
||||
$link = 1;
|
||||
} elsif (substr($arg, 0, 1) eq "-") {
|
||||
die "$0: unknown flag `$arg'\n";
|
||||
} else {
|
||||
push @roots, $arg;
|
||||
}
|
||||
}
|
||||
else {
|
||||
showSyntax if scalar @ARGV < 3;
|
||||
$localCopy = 0;
|
||||
$archivesPutURL = shift @ARGV;
|
||||
$archivesGetURL = shift @ARGV;
|
||||
$manifestPutURL = shift @ARGV;
|
||||
}
|
||||
|
||||
die "$0: please specify a destination directory\n" if !defined $destDir;
|
||||
|
||||
$archivesURL = "file://$destDir" unless defined $archivesURL;
|
||||
|
||||
|
||||
# From the given store paths, determine the set of requisite store
|
||||
# paths, i.e, the paths required to realise them.
|
||||
my %storePaths;
|
||||
|
||||
foreach my $path (@ARGV) {
|
||||
die unless $path =~ /^\//;
|
||||
|
||||
# Get all paths referenced by the normalisation of the given
|
||||
foreach my $path (@roots) {
|
||||
# Get all paths referenced by the normalisation of the given
|
||||
# Nix expression.
|
||||
my $pid = open(READ,
|
||||
"$Nix::Config::binDir/nix-store --query --requisites --force-realise " .
|
||||
"--include-outputs '$path'|") or die;
|
||||
|
||||
|
||||
while (<READ>) {
|
||||
chomp;
|
||||
die "bad: $_" unless /^\//;
|
||||
@@ -92,19 +90,50 @@ foreach my $path (@ARGV) {
|
||||
my @storePaths = keys %storePaths;
|
||||
|
||||
|
||||
# For each path, create a Nix expression that turns the path into
|
||||
# a Nix archive.
|
||||
# Don't create archives for files that are already in the binary cache.
|
||||
my @storePaths2;
|
||||
my %narFiles;
|
||||
foreach my $storePath (@storePaths) {
|
||||
my $pathHash = substr(basename($storePath), 0, 32);
|
||||
my $narInfoFile = "$destDir/$pathHash.narinfo";
|
||||
if (-e $narInfoFile) {
|
||||
my $narInfo = parseNARInfo($storePath, readFile($narInfoFile));
|
||||
my $narFile = "$destDir/$narInfo->{url}";
|
||||
if (-e $narFile) {
|
||||
print STDERR "skipping existing $storePath\n";
|
||||
# Add the NAR info to $narFiles if we're writing a
|
||||
# manifest.
|
||||
$narFiles{$storePath} = [
|
||||
{ url => ("$archivesURL/" . basename $narInfo->{url})
|
||||
, hash => $narInfo->{fileHash}
|
||||
, size => $narInfo->{fileSize}
|
||||
, compressionType => $narInfo->{compression}
|
||||
, narHash => $narInfo->{narHash}
|
||||
, narSize => $narInfo->{narSize}
|
||||
, references => join(" ", map { "$Nix::Config::storeDir/$_" } @{$narInfo->{refs}})
|
||||
, deriver => $narInfo->{deriver} ? "$Nix::Config::storeDir/$narInfo->{deriver}" : undef
|
||||
}
|
||||
] if $writeManifest;
|
||||
next;
|
||||
}
|
||||
}
|
||||
push @storePaths2, $storePath;
|
||||
}
|
||||
|
||||
|
||||
# Create a list of Nix derivations that turn each path into a Nix
|
||||
# archive.
|
||||
open NIX, ">$nixExpr";
|
||||
print NIX "[";
|
||||
|
||||
foreach my $storePath (@storePaths) {
|
||||
foreach my $storePath (@storePaths2) {
|
||||
die unless ($storePath =~ /\/[0-9a-z]{32}[^\"\\\$]*$/);
|
||||
|
||||
# Construct a Nix expression that creates a Nix archive.
|
||||
my $nixexpr =
|
||||
my $nixexpr =
|
||||
"(import <nix/nar.nix> " .
|
||||
"{ storePath = builtins.storePath \"$storePath\"; hashAlgo = \"$hashAlgo\"; }) ";
|
||||
|
||||
"{ storePath = builtins.storePath \"$storePath\"; hashAlgo = \"sha256\"; compressionType = \"$compressionType\"; }) ";
|
||||
|
||||
print NIX $nixexpr;
|
||||
}
|
||||
|
||||
@@ -112,172 +141,135 @@ print NIX "]";
|
||||
close NIX;
|
||||
|
||||
|
||||
# Instantiate store derivations from the Nix expression.
|
||||
my @storeExprs;
|
||||
print STDERR "instantiating store derivations...\n";
|
||||
my $pid = open(READ, "$Nix::Config::binDir/nix-instantiate $nixExpr|")
|
||||
or die "cannot run nix-instantiate";
|
||||
# Build the Nix expression.
|
||||
print STDERR "building compressed archives...\n";
|
||||
my @narPaths;
|
||||
my $pid = open(READ, "$Nix::Config::binDir/nix-build $nixExpr -o $tmpDir/result |")
|
||||
or die "cannot run nix-build";
|
||||
while (<READ>) {
|
||||
chomp;
|
||||
die unless /^\//;
|
||||
push @storeExprs, $_;
|
||||
push @narPaths, $_;
|
||||
}
|
||||
close READ or die "nix-instantiate failed: $?";
|
||||
close READ or die "nix-build failed: $?";
|
||||
|
||||
|
||||
# Build the derivations.
|
||||
print STDERR "creating archives...\n";
|
||||
|
||||
my @narPaths;
|
||||
|
||||
my @tmp = @storeExprs;
|
||||
while (scalar @tmp > 0) {
|
||||
my $n = scalar @tmp;
|
||||
if ($n > 256) { $n = 256 };
|
||||
my @tmp2 = @tmp[0..$n - 1];
|
||||
@tmp = @tmp[$n..scalar @tmp - 1];
|
||||
|
||||
my $pid = open(READ, "$Nix::Config::binDir/nix-store --realise @tmp2|")
|
||||
or die "cannot run nix-store";
|
||||
while (<READ>) {
|
||||
chomp;
|
||||
die unless (/^\//);
|
||||
push @narPaths, "$_";
|
||||
}
|
||||
close READ or die "nix-store failed: $?";
|
||||
# Write the cache info file.
|
||||
my $cacheInfoFile = "$destDir/nix-cache-info";
|
||||
if (! -e $cacheInfoFile) {
|
||||
open FILE, ">$cacheInfoFile" or die "cannot create $cacheInfoFile: $!";
|
||||
print FILE "StoreDir: $Nix::Config::storeDir\n";
|
||||
print FILE "WantMassQuery: 0\n"; # by default, don't hit this cache for "nix-env -qas"
|
||||
close FILE;
|
||||
}
|
||||
|
||||
|
||||
# Create the manifest.
|
||||
print STDERR "creating manifest...\n";
|
||||
# Copy the archives and the corresponding NAR info files.
|
||||
print STDERR "copying archives...\n";
|
||||
|
||||
my %narFiles;
|
||||
my %patches;
|
||||
my $totalNarSize = 0;
|
||||
my $totalCompressedSize = 0;
|
||||
|
||||
my @narArchives;
|
||||
for (my $n = 0; $n < scalar @storePaths; $n++) {
|
||||
my $storePath = $storePaths[$n];
|
||||
for (my $n = 0; $n < scalar @storePaths2; $n++) {
|
||||
my $storePath = $storePaths2[$n];
|
||||
my $narDir = $narPaths[$n];
|
||||
|
||||
$storePath =~ /\/([^\/]*)$/;
|
||||
my $basename = $1;
|
||||
defined $basename or die;
|
||||
my $baseName = basename $storePath;
|
||||
|
||||
open HASH, "$narDir/narbz2-hash" or die "cannot open narbz2-hash";
|
||||
my $narbz2Hash = <HASH>;
|
||||
chomp $narbz2Hash;
|
||||
$narbz2Hash =~ /^[0-9a-z]+$/ or die "invalid hash";
|
||||
close HASH;
|
||||
|
||||
my $narName = "$narbz2Hash.nar.bz2";
|
||||
|
||||
my $narFile = "$narDir/$narName";
|
||||
(-f $narFile) or die "narfile for $storePath not found";
|
||||
push @narArchives, $narFile;
|
||||
|
||||
my $narbz2Size = stat($narFile)->size;
|
||||
|
||||
my $references = `$Nix::Config::binDir/nix-store --query --references '$storePath'`;
|
||||
die "cannot query references for `$storePath'" if $? != 0;
|
||||
$references = join(" ", split(" ", $references));
|
||||
|
||||
my $deriver = `$Nix::Config::binDir/nix-store --query --deriver '$storePath'`;
|
||||
die "cannot query deriver for `$storePath'" if $? != 0;
|
||||
chomp $deriver;
|
||||
$deriver = "" if $deriver eq "unknown-deriver";
|
||||
|
||||
my $narHash = `$Nix::Config::binDir/nix-store --query --hash '$storePath'`;
|
||||
die "cannot query hash for `$storePath'" if $? != 0;
|
||||
chomp $narHash;
|
||||
# Get info about the store path.
|
||||
my ($deriver, $narHash, $time, $narSize, $refs) = queryPathInfo($storePath, 1);
|
||||
|
||||
# In some exceptional cases (such as VM tests that use the Nix
|
||||
# store of the host), the database doesn't contain the hash. So
|
||||
# compute it.
|
||||
if ($narHash =~ /^sha256:0*$/) {
|
||||
$narHash = `$Nix::Config::binDir/nix-hash --type sha256 --base32 '$storePath'`;
|
||||
die "cannot hash `$storePath'" if $? != 0;
|
||||
my $nar = "$tmpDir/nar";
|
||||
system("$Nix::Config::binDir/nix-store --dump $storePath > $nar") == 0
|
||||
or die "cannot dump $storePath\n";
|
||||
$narHash = `$Nix::Config::binDir/nix-hash --type sha256 --base32 --flat $nar`;
|
||||
die "cannot hash `$nar'" if $? != 0;
|
||||
chomp $narHash;
|
||||
$narHash = "sha256:$narHash";
|
||||
$narSize = stat("$nar")->size;
|
||||
unlink $nar or die;
|
||||
}
|
||||
|
||||
my $narSize = `$Nix::Config::binDir/nix-store --query --size '$storePath'`;
|
||||
die "cannot query size for `$storePath'" if $? != 0;
|
||||
chomp $narSize;
|
||||
$totalNarSize += $narSize;
|
||||
|
||||
my $url;
|
||||
if ($localCopy) {
|
||||
$url = "$targetArchivesUrl/$narName";
|
||||
} else {
|
||||
$url = "$archivesGetURL/$narName";
|
||||
# Get info about the compressed NAR.
|
||||
open HASH, "$narDir/nar-compressed-hash" or die "cannot open nar-compressed-hash";
|
||||
my $compressedHash = <HASH>;
|
||||
chomp $compressedHash;
|
||||
$compressedHash =~ /^[0-9a-z]+$/ or die "invalid hash";
|
||||
close HASH;
|
||||
|
||||
my $narName = "$compressedHash.nar." . ($compressionType eq "xz" ? "xz" : "bz2");
|
||||
|
||||
my $narFile = "$narDir/$narName";
|
||||
(-f $narFile) or die "NAR file for $storePath not found";
|
||||
|
||||
my $compressedSize = stat($narFile)->size;
|
||||
$totalCompressedSize += $compressedSize;
|
||||
|
||||
printf STDERR "%s [%.2f MiB, %.1f%%]\n", $storePath,
|
||||
$compressedSize / (1024 * 1024), $compressedSize / $narSize * 100;
|
||||
|
||||
# Copy the compressed NAR.
|
||||
my $dst = "$destDir/$narName";
|
||||
if (! -f $dst) {
|
||||
my $tmp = "$destDir/.tmp.$$.$narName";
|
||||
if ($link) {
|
||||
link($narFile, $tmp) or die "cannot link $tmp to $narFile: $!\n";
|
||||
} else {
|
||||
copy($narFile, $tmp) or die "cannot copy $narFile to $tmp: $!\n";
|
||||
}
|
||||
rename($tmp, $dst) or die "cannot rename $tmp to $dst: $!\n";
|
||||
}
|
||||
|
||||
# Write the info file.
|
||||
my $info;
|
||||
$info .= "StorePath: $storePath\n";
|
||||
$info .= "URL: $narName\n";
|
||||
$info .= "Compression: $compressionType\n";
|
||||
$info .= "FileHash: sha256:$compressedHash\n";
|
||||
$info .= "FileSize: $compressedSize\n";
|
||||
$info .= "NarHash: $narHash\n";
|
||||
$info .= "NarSize: $narSize\n";
|
||||
$info .= "References: " . join(" ", map { basename $_ } @{$refs}) . "\n";
|
||||
if (defined $deriver) {
|
||||
$info .= "Deriver: " . basename $deriver . "\n";
|
||||
if (isValidPath($deriver)) {
|
||||
my $drv = derivationFromPath($deriver);
|
||||
$info .= "System: $drv->{platform}\n";
|
||||
}
|
||||
}
|
||||
|
||||
my $pathHash = substr(basename($storePath), 0, 32);
|
||||
|
||||
$dst = "$destDir/$pathHash.narinfo";
|
||||
if ($force || ! -f $dst) {
|
||||
my $tmp = "$destDir/.tmp.$$.$pathHash.narinfo";
|
||||
open INFO, ">$tmp" or die;
|
||||
print INFO "$info" or die;
|
||||
close INFO or die;
|
||||
rename($tmp, $dst) or die "cannot rename $tmp to $dst: $!\n";
|
||||
}
|
||||
|
||||
$narFiles{$storePath} = [
|
||||
{ url => $url
|
||||
, hash => "$hashAlgo:$narbz2Hash"
|
||||
, size => $narbz2Size
|
||||
{ url => "$archivesURL/$narName"
|
||||
, hash => "sha256:$compressedHash"
|
||||
, size => $compressedSize
|
||||
, compressionType => $compressionType
|
||||
, narHash => "$narHash"
|
||||
, narSize => $narSize
|
||||
, references => $references
|
||||
, references => join(" ", @{$refs})
|
||||
, deriver => $deriver
|
||||
}
|
||||
];
|
||||
] if $writeManifest;
|
||||
}
|
||||
|
||||
writeManifest $manifest, \%narFiles, \%patches;
|
||||
printf STDERR "total compressed size %.2f MiB, %.1f%%\n",
|
||||
$totalCompressedSize / (1024 * 1024), $totalCompressedSize / ($totalNarSize || 1) * 100;
|
||||
|
||||
|
||||
sub copyFile {
|
||||
my $src = shift;
|
||||
my $dst = shift;
|
||||
my $tmp = "$dst.tmp.$$";
|
||||
system("@coreutils@/cp", $src, $tmp) == 0 or die "cannot copy file";
|
||||
rename($tmp, $dst) or die "cannot rename file: $!";
|
||||
}
|
||||
|
||||
|
||||
# Upload/copy the archives.
|
||||
print STDERR "uploading/copying archives...\n";
|
||||
|
||||
sub archiveExists {
|
||||
my $name = shift;
|
||||
print STDERR " HEAD on $archivesGetURL/$name\n";
|
||||
return system("$curl --head $archivesGetURL/$name > /dev/null") == 0;
|
||||
}
|
||||
|
||||
foreach my $narArchive (@narArchives) {
|
||||
|
||||
$narArchive =~ /\/([^\/]*)$/;
|
||||
my $basename = $1;
|
||||
|
||||
if ($localCopy) {
|
||||
# Since nix-push creates $dst atomically, if it exists we
|
||||
# don't have to copy again.
|
||||
my $dst = "$localArchivesDir/$basename";
|
||||
if (! -f "$localArchivesDir/$basename") {
|
||||
print STDERR " $narArchive\n";
|
||||
copyFile $narArchive, $dst;
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (!archiveExists("$basename")) {
|
||||
print STDERR " $narArchive\n";
|
||||
system("$curl --show-error --upload-file " .
|
||||
"'$narArchive' '$archivesPutURL/$basename' > /dev/null") == 0 or
|
||||
die "curl failed on $narArchive: $?";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
# Upload the manifest.
|
||||
print STDERR "uploading manifest...\n";
|
||||
if ($localCopy) {
|
||||
copyFile $manifest, $localManifestFile;
|
||||
copyFile "$manifest.bz2", "$localManifestFile.bz2";
|
||||
} else {
|
||||
system("$curl --show-error --upload-file " .
|
||||
"'$manifest' '$manifestPutURL' > /dev/null") == 0 or
|
||||
die "curl failed on $manifest: $?";
|
||||
system("$curl --show-error --upload-file " .
|
||||
"'$manifest'.bz2 '$manifestPutURL'.bz2 > /dev/null") == 0 or
|
||||
die "curl failed on $manifest: $?";
|
||||
}
|
||||
# Optionally write a manifest.
|
||||
writeManifest($manifestPath // "$destDir/MANIFEST", \%narFiles, \()) if $writeManifest;
|
||||
|
||||
+2
-2
@@ -1,3 +1,3 @@
|
||||
SUBDIRS = bin2c boost libutil libstore libmain nix-store nix-hash \
|
||||
libexpr nix-instantiate nix-env nix-worker nix-setuid-helper \
|
||||
SUBDIRS = boost libutil libstore libmain nix-store nix-hash \
|
||||
libexpr nix-instantiate nix-env nix-daemon nix-setuid-helper \
|
||||
nix-log2xml bsdiff-4.3
|
||||
|
||||
@@ -1,6 +0,0 @@
|
||||
noinst_PROGRAMS = bin2c
|
||||
|
||||
bin2c_SOURCES = bin2c.c
|
||||
|
||||
bin2c$(EXEEXT): bin2c.c
|
||||
$(CC_FOR_BUILD) $(CFLAGS_FOR_BUILD) -o bin2c bin2c.c
|
||||
@@ -1,23 +0,0 @@
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <stdarg.h>
|
||||
|
||||
void print(const char *format, ...)
|
||||
{
|
||||
va_list ap;
|
||||
va_start(ap, format);
|
||||
if (vprintf(format, ap) < 0) abort();
|
||||
va_end(ap);
|
||||
}
|
||||
|
||||
int main(int argc, char * * argv)
|
||||
{
|
||||
int c;
|
||||
if (argc != 2) abort();
|
||||
print("static unsigned char %s[] = { ", argv[1]);
|
||||
while ((c = getchar()) != EOF) {
|
||||
print("0x%02x, ", (unsigned char) c);
|
||||
}
|
||||
print("0 };\n");
|
||||
return 0;
|
||||
}
|
||||
@@ -10,7 +10,7 @@ namespace nix {
|
||||
void findAlongAttrPath(EvalState & state, const string & attrPath,
|
||||
Bindings & autoArgs, Expr * e, Value & v)
|
||||
{
|
||||
Strings tokens = tokenizeString(attrPath, ".");
|
||||
Strings tokens = tokenizeString<Strings>(attrPath, ".");
|
||||
|
||||
Error attrError =
|
||||
Error(format("attribute selection path `%1%' does not match expression") % attrPath);
|
||||
|
||||
+89
-21
@@ -138,12 +138,18 @@ EvalState::EvalState()
|
||||
, sName(symbols.create("name"))
|
||||
, sSystem(symbols.create("system"))
|
||||
, sOverrides(symbols.create("__overrides"))
|
||||
, sOutputs(symbols.create("outputs"))
|
||||
, sOutputName(symbols.create("outputName"))
|
||||
, sIgnoreNulls(symbols.create("__ignoreNulls"))
|
||||
, baseEnv(allocEnv(128))
|
||||
, baseEnvDispl(0)
|
||||
, staticBaseEnv(false, 0)
|
||||
, repair(false)
|
||||
{
|
||||
nrEnvs = nrValuesInEnvs = nrValues = nrListElems = 0;
|
||||
nrAttrsets = nrOpUpdates = nrOpUpdateValuesCopied = 0;
|
||||
nrListConcats = nrPrimOpCalls = nrFunctionCalls = 0;
|
||||
countCalls = getEnv("NIX_COUNT_CALLS", "0") != "0";
|
||||
|
||||
#if HAVE_BOEHMGC
|
||||
static bool gcInitialised = true;
|
||||
@@ -177,9 +183,9 @@ EvalState::EvalState()
|
||||
|
||||
/* Initialise the Nix expression search path. */
|
||||
searchPathInsertionPoint = searchPath.end();
|
||||
Strings paths = tokenizeString(getEnv("NIX_PATH", ""), ":");
|
||||
Strings paths = tokenizeString<Strings>(getEnv("NIX_PATH", ""), ":");
|
||||
foreach (Strings::iterator, i, paths) addToSearchPath(*i);
|
||||
addToSearchPath("nix=" + nixDataDir + "/nix/corepkgs");
|
||||
addToSearchPath("nix=" + settings.nixDataDir + "/nix/corepkgs");
|
||||
searchPathInsertionPoint = searchPath.begin();
|
||||
|
||||
createBaseEnv();
|
||||
@@ -300,8 +306,10 @@ inline Value * EvalState::lookupVar(Env * env, const VarRef & var)
|
||||
if (var.fromWith) {
|
||||
while (1) {
|
||||
Bindings::iterator j = env->values[0]->attrs->find(var.name);
|
||||
if (j != env->values[0]->attrs->end())
|
||||
if (j != env->values[0]->attrs->end()) {
|
||||
if (countCalls && j->pos) attrSelects[*j->pos]++;
|
||||
return j->value;
|
||||
}
|
||||
if (env->prevWith == 0)
|
||||
throwEvalError("undefined variable `%1%'", var.name);
|
||||
for (unsigned int l = env->prevWith; l; --l, env = env->up) ;
|
||||
@@ -344,7 +352,7 @@ void EvalState::mkList(Value & v, unsigned int length)
|
||||
{
|
||||
v.type = tList;
|
||||
v.list.length = length;
|
||||
v.list.elems = (Value * *) GC_MALLOC(length * sizeof(Value *));
|
||||
v.list.elems = length ? (Value * *) GC_MALLOC(length * sizeof(Value *)) : 0;
|
||||
nrListElems += length;
|
||||
}
|
||||
|
||||
@@ -619,8 +627,10 @@ void ExprSelect::eval(EvalState & state, Env & env, Value & v)
|
||||
}
|
||||
vAttrs = j->value;
|
||||
pos = j->pos;
|
||||
if (state.countCalls && pos) state.attrSelects[*pos]++;
|
||||
}
|
||||
|
||||
|
||||
state.forceValue(*vAttrs);
|
||||
|
||||
} catch (Error & e) {
|
||||
@@ -700,6 +710,8 @@ void EvalState::callFunction(Value & fun, Value & arg, Value & v)
|
||||
vArgs[n--] = arg->primOpApp.right;
|
||||
|
||||
/* And call the primop. */
|
||||
nrPrimOpCalls++;
|
||||
if (countCalls) primOpCalls[primOp->primOp->name]++;
|
||||
try {
|
||||
primOp->primOp->fun(*this, vArgs, v);
|
||||
} catch (Error & e) {
|
||||
@@ -716,7 +728,7 @@ void EvalState::callFunction(Value & fun, Value & arg, Value & v)
|
||||
}
|
||||
|
||||
if (fun.type != tLambda)
|
||||
throwTypeError("attempt to call something which is neither a function nor a primop (built-in operation) but %1%",
|
||||
throwTypeError("attempt to call something which is not a function but %1%",
|
||||
showType(fun));
|
||||
|
||||
unsigned int size =
|
||||
@@ -760,6 +772,9 @@ void EvalState::callFunction(Value & fun, Value & arg, Value & v)
|
||||
throwTypeError("function at %1% called with unexpected argument", fun.lambda.fun->pos);
|
||||
}
|
||||
|
||||
nrFunctionCalls++;
|
||||
if (countCalls) functionCalls[fun.lambda.fun->pos]++;
|
||||
|
||||
try {
|
||||
fun.lambda.fun->body->eval(*this, env2, v);
|
||||
} catch (Error & e) {
|
||||
@@ -778,20 +793,20 @@ void EvalState::autoCallFunction(Bindings & args, Value & fun, Value & res)
|
||||
return;
|
||||
}
|
||||
|
||||
Value actualArgs;
|
||||
mkAttrs(actualArgs, fun.lambda.fun->formals->formals.size());
|
||||
Value * actualArgs = allocValue();
|
||||
mkAttrs(*actualArgs, fun.lambda.fun->formals->formals.size());
|
||||
|
||||
foreach (Formals::Formals_::iterator, i, fun.lambda.fun->formals->formals) {
|
||||
Bindings::iterator j = args.find(i->name);
|
||||
if (j != args.end())
|
||||
actualArgs.attrs->push_back(*j);
|
||||
actualArgs->attrs->push_back(*j);
|
||||
else if (!i->def)
|
||||
throwTypeError("cannot auto-call a function that has an argument without a default value (`%1%')", i->name);
|
||||
}
|
||||
|
||||
actualArgs.attrs->sort();
|
||||
actualArgs->attrs->sort();
|
||||
|
||||
callFunction(fun, actualArgs, res);
|
||||
callFunction(fun, *actualArgs, res);
|
||||
}
|
||||
|
||||
|
||||
@@ -902,14 +917,36 @@ void ExprOpUpdate::eval(EvalState & state, Env & env, Value & v)
|
||||
void ExprOpConcatLists::eval(EvalState & state, Env & env, Value & v)
|
||||
{
|
||||
Value v1; e1->eval(state, env, v1);
|
||||
state.forceList(v1);
|
||||
Value v2; e2->eval(state, env, v2);
|
||||
state.forceList(v2);
|
||||
state.mkList(v, v1.list.length + v2.list.length);
|
||||
for (unsigned int n = 0; n < v1.list.length; ++n)
|
||||
v.list.elems[n] = v1.list.elems[n];
|
||||
for (unsigned int n = 0; n < v2.list.length; ++n)
|
||||
v.list.elems[n + v1.list.length] = v2.list.elems[n];
|
||||
Value * lists[2] = { &v1, &v2 };
|
||||
state.concatLists(v, 2, lists);
|
||||
}
|
||||
|
||||
|
||||
void EvalState::concatLists(Value & v, unsigned int nrLists, Value * * lists)
|
||||
{
|
||||
nrListConcats++;
|
||||
|
||||
Value * nonEmpty = 0;
|
||||
unsigned int len = 0;
|
||||
for (unsigned int n = 0; n < nrLists; ++n) {
|
||||
forceList(*lists[n]);
|
||||
unsigned int l = lists[n]->list.length;
|
||||
len += l;
|
||||
if (l) nonEmpty = lists[n];
|
||||
}
|
||||
|
||||
if (nonEmpty && len == nonEmpty->list.length) {
|
||||
v = *nonEmpty;
|
||||
return;
|
||||
}
|
||||
|
||||
mkList(v, len);
|
||||
for (unsigned int n = 0, pos = 0; n < nrLists; ++n) {
|
||||
unsigned int l = lists[n]->list.length;
|
||||
memcpy(v.list.elems + pos, lists[n]->list.elems, l * sizeof(Value *));
|
||||
pos += l;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -929,10 +966,10 @@ void ExprConcatStrings::eval(EvalState & state, Env & env, Value & v)
|
||||
since paths are copied when they are used in a derivation),
|
||||
and none of the strings are allowed to have contexts. */
|
||||
if (first) {
|
||||
isPath = vStr.type == tPath;
|
||||
isPath = !forceString && vStr.type == tPath;
|
||||
first = false;
|
||||
}
|
||||
|
||||
|
||||
s << state.coerceToString(vStr, context, false, !isPath);
|
||||
}
|
||||
|
||||
@@ -1058,9 +1095,9 @@ string EvalState::coerceToString(Value & v, PathSet & context,
|
||||
if (srcToStore[path] != "")
|
||||
dstPath = srcToStore[path];
|
||||
else {
|
||||
dstPath = readOnlyMode
|
||||
dstPath = settings.readOnlyMode
|
||||
? computeStorePathForPath(path).first
|
||||
: store->addToStore(path);
|
||||
: store->addToStore(path, true, htSHA256, defaultPathFilter, repair);
|
||||
srcToStore[path] = dstPath;
|
||||
printMsg(lvlChatty, format("copied source `%1%' -> `%2%'")
|
||||
% path % dstPath);
|
||||
@@ -1207,6 +1244,7 @@ void EvalState::printStats()
|
||||
% nrEnvs % (nrEnvs * sizeof(Env) + nrValuesInEnvs * sizeof(Value *)));
|
||||
printMsg(v, format(" list elements: %1% (%2% bytes)")
|
||||
% nrListElems % (nrListElems * sizeof(Value *)));
|
||||
printMsg(v, format(" list concatenations: %1%") % nrListConcats);
|
||||
printMsg(v, format(" values allocated: %1% (%2% bytes)")
|
||||
% nrValues % (nrValues * sizeof(Value)));
|
||||
printMsg(v, format(" attribute sets allocated: %1%") % nrAttrsets);
|
||||
@@ -1216,6 +1254,36 @@ void EvalState::printStats()
|
||||
printMsg(v, format(" number of thunks: %1%") % nrThunks);
|
||||
printMsg(v, format(" number of thunks avoided: %1%") % nrAvoided);
|
||||
printMsg(v, format(" number of attr lookups: %1%") % nrLookups);
|
||||
printMsg(v, format(" number of primop calls: %1%") % nrPrimOpCalls);
|
||||
printMsg(v, format(" number of function calls: %1%") % nrFunctionCalls);
|
||||
|
||||
if (countCalls) {
|
||||
|
||||
printMsg(v, format("calls to %1% primops:") % primOpCalls.size());
|
||||
typedef std::multimap<unsigned int, Symbol> PrimOpCalls_;
|
||||
std::multimap<unsigned int, Symbol> primOpCalls_;
|
||||
foreach (PrimOpCalls::iterator, i, primOpCalls)
|
||||
primOpCalls_.insert(std::pair<unsigned int, Symbol>(i->second, i->first));
|
||||
foreach_reverse (PrimOpCalls_::reverse_iterator, i, primOpCalls_)
|
||||
printMsg(v, format("%1$10d %2%") % i->first % i->second);
|
||||
|
||||
printMsg(v, format("calls to %1% functions:") % functionCalls.size());
|
||||
typedef std::multimap<unsigned int, Pos> FunctionCalls_;
|
||||
std::multimap<unsigned int, Pos> functionCalls_;
|
||||
foreach (FunctionCalls::iterator, i, functionCalls)
|
||||
functionCalls_.insert(std::pair<unsigned int, Pos>(i->second, i->first));
|
||||
foreach_reverse (FunctionCalls_::reverse_iterator, i, functionCalls_)
|
||||
printMsg(v, format("%1$10d %2%") % i->first % i->second);
|
||||
|
||||
printMsg(v, format("evaluations of %1% attributes:") % attrSelects.size());
|
||||
typedef std::multimap<unsigned int, Pos> AttrSelects_;
|
||||
std::multimap<unsigned int, Pos> attrSelects_;
|
||||
foreach (AttrSelects::iterator, i, attrSelects)
|
||||
attrSelects_.insert(std::pair<unsigned int, Pos>(i->second, i->first));
|
||||
foreach_reverse (AttrSelects_::reverse_iterator, i, attrSelects_)
|
||||
printMsg(v, format("%1$10d %2%") % i->first % i->second);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
+26
-4
@@ -93,7 +93,11 @@ public:
|
||||
SymbolTable symbols;
|
||||
|
||||
const Symbol sWith, sOutPath, sDrvPath, sType, sMeta, sName,
|
||||
sSystem, sOverrides;
|
||||
sSystem, sOverrides, sOutputs, sOutputName, sIgnoreNulls;
|
||||
|
||||
/* If set, force copying files to the Nix store even if they
|
||||
already exist there. */
|
||||
bool repair;
|
||||
|
||||
private:
|
||||
SrcToStore srcToStore;
|
||||
@@ -232,11 +236,13 @@ public:
|
||||
void mkAttrs(Value & v, unsigned int expected);
|
||||
void mkThunk_(Value & v, Expr * expr);
|
||||
|
||||
void concatLists(Value & v, unsigned int nrLists, Value * * lists);
|
||||
|
||||
/* Print statistics. */
|
||||
void printStats();
|
||||
|
||||
private:
|
||||
|
||||
|
||||
unsigned long nrEnvs;
|
||||
unsigned long nrValuesInEnvs;
|
||||
unsigned long nrValues;
|
||||
@@ -244,9 +250,25 @@ private:
|
||||
unsigned long nrAttrsets;
|
||||
unsigned long nrOpUpdates;
|
||||
unsigned long nrOpUpdateValuesCopied;
|
||||
|
||||
friend class RecursionCounter;
|
||||
unsigned long nrListConcats;
|
||||
unsigned long nrPrimOpCalls;
|
||||
unsigned long nrFunctionCalls;
|
||||
|
||||
bool countCalls;
|
||||
|
||||
typedef std::map<Symbol, unsigned int> PrimOpCalls;
|
||||
PrimOpCalls primOpCalls;
|
||||
|
||||
typedef std::map<Pos, unsigned int> FunctionCalls;
|
||||
FunctionCalls functionCalls;
|
||||
|
||||
typedef std::map<Pos, unsigned int> AttrSelects;
|
||||
AttrSelects attrSelects;
|
||||
|
||||
friend class ExprOpUpdate;
|
||||
friend class ExprOpConcatLists;
|
||||
friend class ExprSelect;
|
||||
friend void prim_getAttr(EvalState & state, Value * * args, Value & v);
|
||||
};
|
||||
|
||||
|
||||
|
||||
+64
-19
@@ -28,12 +28,53 @@ string DrvInfo::queryOutPath(EvalState & state) const
|
||||
}
|
||||
|
||||
|
||||
DrvInfo::Outputs DrvInfo::queryOutputs(EvalState & state)
|
||||
{
|
||||
if (outputs.empty()) {
|
||||
/* Get the ‘outputs’ list. */
|
||||
Bindings::iterator i = attrs->find(state.sOutputs);
|
||||
|
||||
if (i == attrs->end())
|
||||
outputs["out"] = queryOutPath(state);
|
||||
else {
|
||||
state.forceList(*i->value);
|
||||
|
||||
/* For each output... */
|
||||
for (unsigned int j = 0; j < i->value->list.length; ++j) {
|
||||
/* Evaluate the corresponding attribute set. */
|
||||
string name = state.forceStringNoCtx(*i->value->list.elems[j]);
|
||||
Bindings::iterator out = attrs->find(state.symbols.create(name));
|
||||
if (out == attrs->end()) continue; // FIXME: throw error?
|
||||
state.forceAttrs(*out->value);
|
||||
|
||||
/* And evaluate its ‘outPath’ attribute. */
|
||||
Bindings::iterator outPath = out->value->attrs->find(state.sOutPath);
|
||||
if (outPath == out->value->attrs->end()) continue; // FIXME: throw error?
|
||||
PathSet context;
|
||||
outputs[name] = state.coerceToPath(*outPath->value, context);
|
||||
}
|
||||
}
|
||||
}
|
||||
return outputs;
|
||||
}
|
||||
|
||||
|
||||
string DrvInfo::queryOutputName(EvalState & state) const
|
||||
{
|
||||
if (outputName == "" && attrs) {
|
||||
Bindings::iterator i = attrs->find(state.sOutputName);
|
||||
(string &) outputName = i != attrs->end() ? state.forceStringNoCtx(*i->value) : "";
|
||||
}
|
||||
return outputName;
|
||||
}
|
||||
|
||||
|
||||
MetaInfo DrvInfo::queryMetaInfo(EvalState & state) const
|
||||
{
|
||||
if (metaInfoRead) return meta;
|
||||
|
||||
|
||||
(bool &) metaInfoRead = true;
|
||||
|
||||
|
||||
Bindings::iterator a = attrs->find(state.sMeta);
|
||||
if (a == attrs->end()) return meta; /* fine, empty meta information */
|
||||
|
||||
@@ -84,7 +125,8 @@ typedef set<Bindings *> Done;
|
||||
makes sense for the caller to recursively search for derivations in
|
||||
`v'. */
|
||||
static bool getDerivation(EvalState & state, Value & v,
|
||||
const string & attrPath, DrvInfos & drvs, Done & done)
|
||||
const string & attrPath, DrvInfos & drvs, Done & done,
|
||||
bool ignoreAssertionFailures)
|
||||
{
|
||||
try {
|
||||
state.forceValue(v);
|
||||
@@ -96,7 +138,7 @@ static bool getDerivation(EvalState & state, Value & v,
|
||||
done.insert(v.attrs);
|
||||
|
||||
DrvInfo drv;
|
||||
|
||||
|
||||
Bindings::iterator i = v.attrs->find(state.sName);
|
||||
/* !!! We really would like to have a decent back trace here. */
|
||||
if (i == v.attrs->end()) throw TypeError("derivation name missing");
|
||||
@@ -114,18 +156,20 @@ static bool getDerivation(EvalState & state, Value & v,
|
||||
|
||||
drvs.push_back(drv);
|
||||
return false;
|
||||
|
||||
|
||||
} catch (AssertionError & e) {
|
||||
return false;
|
||||
if (ignoreAssertionFailures) return false;
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
bool getDerivation(EvalState & state, Value & v, DrvInfo & drv)
|
||||
bool getDerivation(EvalState & state, Value & v, DrvInfo & drv,
|
||||
bool ignoreAssertionFailures)
|
||||
{
|
||||
Done done;
|
||||
DrvInfos drvs;
|
||||
getDerivation(state, v, "", drvs, done);
|
||||
getDerivation(state, v, "", drvs, done, ignoreAssertionFailures);
|
||||
if (drvs.size() != 1) return false;
|
||||
drv = drvs.front();
|
||||
return true;
|
||||
@@ -140,15 +184,16 @@ static string addToPath(const string & s1, const string & s2)
|
||||
|
||||
static void getDerivations(EvalState & state, Value & vIn,
|
||||
const string & pathPrefix, Bindings & autoArgs,
|
||||
DrvInfos & drvs, Done & done)
|
||||
DrvInfos & drvs, Done & done,
|
||||
bool ignoreAssertionFailures)
|
||||
{
|
||||
Value v;
|
||||
state.autoCallFunction(autoArgs, vIn, v);
|
||||
|
||||
|
||||
/* Process the expression. */
|
||||
DrvInfo drv;
|
||||
|
||||
if (!getDerivation(state, v, pathPrefix, drvs, done)) ;
|
||||
if (!getDerivation(state, v, pathPrefix, drvs, done, ignoreAssertionFailures)) ;
|
||||
|
||||
else if (v.type == tAttrs) {
|
||||
|
||||
@@ -171,8 +216,8 @@ static void getDerivations(EvalState & state, Value & vIn,
|
||||
string pathPrefix2 = addToPath(pathPrefix, i->first);
|
||||
Value & v2(*v.attrs->find(i->second)->value);
|
||||
if (combineChannels)
|
||||
getDerivations(state, v2, pathPrefix2, autoArgs, drvs, done);
|
||||
else if (getDerivation(state, v2, pathPrefix2, drvs, done)) {
|
||||
getDerivations(state, v2, pathPrefix2, autoArgs, drvs, done, ignoreAssertionFailures);
|
||||
else if (getDerivation(state, v2, pathPrefix2, drvs, done, ignoreAssertionFailures)) {
|
||||
/* If the value of this attribute is itself an
|
||||
attribute set, should we recurse into it? => Only
|
||||
if it has a `recurseForDerivations = true'
|
||||
@@ -180,7 +225,7 @@ static void getDerivations(EvalState & state, Value & vIn,
|
||||
if (v2.type == tAttrs) {
|
||||
Bindings::iterator j = v2.attrs->find(state.symbols.create("recurseForDerivations"));
|
||||
if (j != v2.attrs->end() && state.forceBool(*j->value))
|
||||
getDerivations(state, v2, pathPrefix2, autoArgs, drvs, done);
|
||||
getDerivations(state, v2, pathPrefix2, autoArgs, drvs, done, ignoreAssertionFailures);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -191,8 +236,8 @@ static void getDerivations(EvalState & state, Value & vIn,
|
||||
startNest(nest, lvlDebug,
|
||||
format("evaluating list element"));
|
||||
string pathPrefix2 = addToPath(pathPrefix, (format("%1%") % n).str());
|
||||
if (getDerivation(state, *v.list.elems[n], pathPrefix2, drvs, done))
|
||||
getDerivations(state, *v.list.elems[n], pathPrefix2, autoArgs, drvs, done);
|
||||
if (getDerivation(state, *v.list.elems[n], pathPrefix2, drvs, done, ignoreAssertionFailures))
|
||||
getDerivations(state, *v.list.elems[n], pathPrefix2, autoArgs, drvs, done, ignoreAssertionFailures);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -201,11 +246,11 @@ static void getDerivations(EvalState & state, Value & vIn,
|
||||
|
||||
|
||||
void getDerivations(EvalState & state, Value & v, const string & pathPrefix,
|
||||
Bindings & autoArgs, DrvInfos & drvs)
|
||||
Bindings & autoArgs, DrvInfos & drvs, bool ignoreAssertionFailures)
|
||||
{
|
||||
Done done;
|
||||
getDerivations(state, v, pathPrefix, autoArgs, drvs, done);
|
||||
getDerivations(state, v, pathPrefix, autoArgs, drvs, done, ignoreAssertionFailures);
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
+20
-6
@@ -25,13 +25,20 @@ typedef std::map<string, MetaValue> MetaInfo;
|
||||
|
||||
struct DrvInfo
|
||||
{
|
||||
public:
|
||||
typedef std::map<string, Path> Outputs;
|
||||
|
||||
private:
|
||||
string drvPath;
|
||||
string outPath;
|
||||
string outputName;
|
||||
Outputs outputs;
|
||||
|
||||
bool metaInfoRead;
|
||||
MetaInfo meta;
|
||||
|
||||
|
||||
bool failed; // set if we get an AssertionError
|
||||
|
||||
public:
|
||||
string name;
|
||||
string attrPath; /* path towards the derivation */
|
||||
@@ -40,10 +47,12 @@ public:
|
||||
/* !!! make this private */
|
||||
Bindings * attrs;
|
||||
|
||||
DrvInfo() : metaInfoRead(false), attrs(0) { };
|
||||
DrvInfo() : metaInfoRead(false), failed(false), attrs(0) { };
|
||||
|
||||
string queryDrvPath(EvalState & state) const;
|
||||
string queryOutPath(EvalState & state) const;
|
||||
string queryOutputName(EvalState & state) const;
|
||||
Outputs queryOutputs(EvalState & state);
|
||||
MetaInfo queryMetaInfo(EvalState & state) const;
|
||||
MetaValue queryMetaInfo(EvalState & state, const string & name) const;
|
||||
|
||||
@@ -51,13 +60,16 @@ public:
|
||||
{
|
||||
drvPath = s;
|
||||
}
|
||||
|
||||
|
||||
void setOutPath(const string & s)
|
||||
{
|
||||
outPath = s;
|
||||
}
|
||||
|
||||
void setMetaInfo(const MetaInfo & meta);
|
||||
|
||||
void setFailed() { failed = true; };
|
||||
bool hasFailed() { return failed; };
|
||||
};
|
||||
|
||||
|
||||
@@ -70,10 +82,12 @@ typedef list<DrvInfo> DrvInfos;
|
||||
|
||||
/* If value `v' denotes a derivation, store information about the
|
||||
derivation in `drv' and return true. Otherwise, return false. */
|
||||
bool getDerivation(EvalState & state, Value & v, DrvInfo & drv);
|
||||
bool getDerivation(EvalState & state, Value & v, DrvInfo & drv,
|
||||
bool ignoreAssertionFailures);
|
||||
|
||||
void getDerivations(EvalState & state, Value & v, const string & pathPrefix,
|
||||
Bindings & autoArgs, DrvInfos & drvs);
|
||||
Bindings & autoArgs, DrvInfos & drvs,
|
||||
bool ignoreAssertionFailures);
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
+1
-1
@@ -78,7 +78,7 @@ static Expr * unescapeStr(SymbolTable & symbols, const char * s)
|
||||
%}
|
||||
|
||||
|
||||
ID [a-zA-Z\_][a-zA-Z0-9\_\']*
|
||||
ID [a-zA-Z\_][a-zA-Z0-9\_\'\-]*
|
||||
INT [0-9]+
|
||||
PATH [a-zA-Z0-9\.\_\-\+]*(\/[a-zA-Z0-9\.\_\-\+]+)+
|
||||
SPATH \<[a-zA-Z0-9\.\_\-\+]+(\/[a-zA-Z0-9\.\_\-\+]+)*\>
|
||||
|
||||
+12
-1
@@ -27,6 +27,15 @@ struct Pos
|
||||
Pos() : line(0), column(0) { };
|
||||
Pos(const string & file, unsigned int line, unsigned int column)
|
||||
: file(file), line(line), column(column) { };
|
||||
bool operator < (const Pos & p2) const
|
||||
{
|
||||
int d = file.compare(p2.file);
|
||||
if (d < 0) return true;
|
||||
if (d > 0) return false;
|
||||
if (line < p2.line) return true;
|
||||
if (line > p2.line) return false;
|
||||
return column < p2.column;
|
||||
}
|
||||
};
|
||||
|
||||
extern Pos noPos;
|
||||
@@ -268,8 +277,10 @@ MakeBinOp(OpConcatLists, "++")
|
||||
|
||||
struct ExprConcatStrings : Expr
|
||||
{
|
||||
bool forceString;
|
||||
vector<Expr *> * es;
|
||||
ExprConcatStrings(vector<Expr *> * es) : es(es) { };
|
||||
ExprConcatStrings(bool forceString, vector<Expr *> * es)
|
||||
: forceString(forceString), es(es) { };
|
||||
COMMON_METHODS
|
||||
};
|
||||
|
||||
|
||||
@@ -203,7 +203,7 @@ static Expr * stripIndentation(SymbolTable & symbols, vector<Expr *> & es)
|
||||
es2->push_back(new ExprString(symbols.create(s2)));
|
||||
}
|
||||
|
||||
return new ExprConcatStrings(es2);
|
||||
return es2->size() == 1 ? (*es2)[0] : new ExprConcatStrings(true, es2);
|
||||
}
|
||||
|
||||
|
||||
@@ -318,7 +318,7 @@ expr_op
|
||||
{ vector<Expr *> * l = new vector<Expr *>;
|
||||
l->push_back($1);
|
||||
l->push_back($3);
|
||||
$$ = new ExprConcatStrings(l);
|
||||
$$ = new ExprConcatStrings(false, l);
|
||||
}
|
||||
| expr_op CONCAT expr_op { $$ = new ExprOpConcatLists($1, $3); }
|
||||
| expr_app
|
||||
@@ -349,7 +349,7 @@ expr_simple
|
||||
/* For efficiency, and to simplify parse trees a bit. */
|
||||
if ($2->empty()) $$ = new ExprString(data->symbols.create(""));
|
||||
else if ($2->size() == 1) $$ = $2->front();
|
||||
else $$ = new ExprConcatStrings($2);
|
||||
else $$ = new ExprConcatStrings(true, $2);
|
||||
}
|
||||
| IND_STRING_OPEN ind_string_parts IND_STRING_CLOSE {
|
||||
$$ = stripIndentation(data->symbols, *$2);
|
||||
|
||||
+166
-26
@@ -51,6 +51,12 @@ static void prim_import(EvalState & state, Value * * args, Value & v)
|
||||
% path % ctx);
|
||||
if (isDerivation(ctx))
|
||||
try {
|
||||
/* For performance, prefetch all substitute info. */
|
||||
PathSet willBuild, willSubstitute, unknown;
|
||||
unsigned long long downloadSize, narSize;
|
||||
queryMissing(*store, singleton<PathSet>(ctx),
|
||||
willBuild, willSubstitute, unknown, downloadSize, narSize);
|
||||
|
||||
/* !!! If using a substitute, we only need to fetch
|
||||
the selected output of this derivation. */
|
||||
store->buildPaths(singleton<PathSet>(ctx));
|
||||
@@ -59,7 +65,31 @@ static void prim_import(EvalState & state, Value * * args, Value & v)
|
||||
}
|
||||
}
|
||||
|
||||
state.evalFile(path, v);
|
||||
if (isStorePath(path) && store->isValidPath(path) && isDerivation(path)) {
|
||||
Derivation drv = parseDerivation(readFile(path));
|
||||
Value & w = *state.allocValue();
|
||||
state.mkAttrs(w, 1 + drv.outputs.size());
|
||||
mkString(*state.allocAttr(w, state.sDrvPath), path, singleton<PathSet>("=" + path));
|
||||
state.mkList(*state.allocAttr(w, state.symbols.create("outputs")), drv.outputs.size());
|
||||
unsigned int outputs_index = 0;
|
||||
|
||||
Value * outputsVal = w.attrs->find(state.symbols.create("outputs"))->value;
|
||||
foreach (DerivationOutputs::iterator, i, drv.outputs) {
|
||||
mkString(*state.allocAttr(w, state.symbols.create(i->first)),
|
||||
i->second.path, singleton<PathSet>("!" + i->first + "!" + path));
|
||||
mkString(*(outputsVal->list.elems[outputs_index++] = state.allocValue()),
|
||||
i->first);
|
||||
}
|
||||
w.attrs->sort();
|
||||
Value fun;
|
||||
state.mkThunk_(fun,
|
||||
state.parseExprFromFile(state.findFile("nix/imported-drv-to-derivation.nix")));
|
||||
state.forceFunction(fun);
|
||||
mkApp(v, fun, w);
|
||||
state.forceAttrs(v);
|
||||
} else {
|
||||
state.evalFile(path, v);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -280,16 +310,22 @@ static void prim_derivationStrict(EvalState & state, Value * * args, Value & v)
|
||||
throw EvalError("required attribute `name' missing");
|
||||
string drvName;
|
||||
Pos & posDrvName(*attr->pos);
|
||||
try {
|
||||
try {
|
||||
drvName = state.forceStringNoCtx(*attr->value);
|
||||
} catch (Error & e) {
|
||||
e.addPrefix(format("while evaluating the derivation attribute `name' at %1%:\n") % posDrvName);
|
||||
throw;
|
||||
}
|
||||
|
||||
|
||||
/* Check whether null attributes should be ignored. */
|
||||
bool ignoreNulls = false;
|
||||
attr = args[0]->attrs->find(state.sIgnoreNulls);
|
||||
if (attr != args[0]->attrs->end())
|
||||
ignoreNulls = state.forceBool(*attr->value);
|
||||
|
||||
/* Build the derivation expression by processing the attributes. */
|
||||
Derivation drv;
|
||||
|
||||
|
||||
PathSet context;
|
||||
|
||||
string outputHash, outputHashAlgo;
|
||||
@@ -299,11 +335,17 @@ static void prim_derivationStrict(EvalState & state, Value * * args, Value & v)
|
||||
outputs.insert("out");
|
||||
|
||||
foreach (Bindings::iterator, i, *args[0]->attrs) {
|
||||
if (i->name == state.sIgnoreNulls) continue;
|
||||
string key = i->name;
|
||||
startNest(nest, lvlVomit, format("processing attribute `%1%'") % key);
|
||||
|
||||
try {
|
||||
|
||||
if (ignoreNulls) {
|
||||
state.forceValue(*i->value);
|
||||
if (i->value->type == tNull) continue;
|
||||
}
|
||||
|
||||
/* The `args' attribute is special: it supplies the
|
||||
command-line arguments to the builder. */
|
||||
if (key == "args") {
|
||||
@@ -328,12 +370,12 @@ static void prim_derivationStrict(EvalState & state, Value * * args, Value & v)
|
||||
else if (key == "outputHash") outputHash = s;
|
||||
else if (key == "outputHashAlgo") outputHashAlgo = s;
|
||||
else if (key == "outputHashMode") {
|
||||
if (s == "recursive") outputHashRecursive = true;
|
||||
if (s == "recursive") outputHashRecursive = true;
|
||||
else if (s == "flat") outputHashRecursive = false;
|
||||
else throw EvalError(format("invalid value `%1%' for `outputHashMode' attribute") % s);
|
||||
}
|
||||
else if (key == "outputs") {
|
||||
Strings tmp = tokenizeString(s);
|
||||
Strings tmp = tokenizeString<Strings>(s);
|
||||
outputs.clear();
|
||||
foreach (Strings::iterator, j, tmp) {
|
||||
if (outputs.find(*j) != outputs.end())
|
||||
@@ -360,13 +402,13 @@ static void prim_derivationStrict(EvalState & state, Value * * args, Value & v)
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* Everything in the context of the strings in the derivation
|
||||
attributes should be added as dependencies of the resulting
|
||||
derivation. */
|
||||
foreach (PathSet::iterator, i, context) {
|
||||
Path path = *i;
|
||||
|
||||
|
||||
/* Paths marked with `=' denote that the path of a derivation
|
||||
is explicitly passed to the builder. Since that allows the
|
||||
builder to gain access to every path in the dependency
|
||||
@@ -403,7 +445,7 @@ static void prim_derivationStrict(EvalState & state, Value * * args, Value & v)
|
||||
else
|
||||
drv.inputSrcs.insert(path);
|
||||
}
|
||||
|
||||
|
||||
/* Do we have all required attributes? */
|
||||
if (drv.builder == "")
|
||||
throw EvalError("required attribute `builder' missing");
|
||||
@@ -420,14 +462,14 @@ static void prim_derivationStrict(EvalState & state, Value * * args, Value & v)
|
||||
/* Handle fixed-output derivations. */
|
||||
if (outputs.size() != 1 || *(outputs.begin()) != "out")
|
||||
throw Error("multiple outputs are not supported in fixed-output derivations");
|
||||
|
||||
|
||||
HashType ht = parseHashType(outputHashAlgo);
|
||||
if (ht == htUnknown)
|
||||
throw EvalError(format("unknown hash algorithm `%1%'") % outputHashAlgo);
|
||||
Hash h = parseHash16or32(ht, outputHash);
|
||||
outputHash = printHash(h);
|
||||
if (outputHashRecursive) outputHashAlgo = "r:" + outputHashAlgo;
|
||||
|
||||
|
||||
Path outPath = makeFixedOutputPath(outputHashRecursive, ht, h, drvName);
|
||||
drv.env["out"] = outPath;
|
||||
drv.outputs["out"] = DerivationOutput(outPath, outputHashAlgo, outputHash);
|
||||
@@ -447,7 +489,7 @@ static void prim_derivationStrict(EvalState & state, Value * * args, Value & v)
|
||||
/* Use the masked derivation expression to compute the output
|
||||
path. */
|
||||
Hash h = hashDerivationModulo(*store, drv);
|
||||
|
||||
|
||||
foreach (DerivationOutputs::iterator, i, drv.outputs)
|
||||
if (i->second.path == "") {
|
||||
Path outPath = makeOutputPath(i->first, h, drvName);
|
||||
@@ -457,7 +499,7 @@ static void prim_derivationStrict(EvalState & state, Value * * args, Value & v)
|
||||
}
|
||||
|
||||
/* Write the resulting term into the Nix store directory. */
|
||||
Path drvPath = writeDerivation(*store, drv, drvName);
|
||||
Path drvPath = writeDerivation(*store, drv, drvName, state.repair);
|
||||
|
||||
printMsg(lvlChatty, format("instantiated `%1%' -> `%2%'")
|
||||
% drvName % drvPath);
|
||||
@@ -593,9 +635,9 @@ static void prim_toFile(EvalState & state, Value * * args, Value & v)
|
||||
refs.insert(path);
|
||||
}
|
||||
|
||||
Path storePath = readOnlyMode
|
||||
Path storePath = settings.readOnlyMode
|
||||
? computeStorePathForText(name, contents, refs)
|
||||
: store->addTextToStore(name, contents, refs);
|
||||
: store->addTextToStore(name, contents, refs, state.repair);
|
||||
|
||||
/* Note: we don't need to add `context' to the context of the
|
||||
result, since `storePath' itself has references to the paths
|
||||
@@ -657,9 +699,9 @@ static void prim_filterSource(EvalState & state, Value * * args, Value & v)
|
||||
|
||||
FilterFromExpr filter(state, *args[0]);
|
||||
|
||||
Path dstPath = readOnlyMode
|
||||
Path dstPath = settings.readOnlyMode
|
||||
? computeStorePathForPath(path, true, htSHA256, filter).first
|
||||
: store->addToStore(path, true, htSHA256, filter);
|
||||
: store->addToStore(path, true, htSHA256, filter, state.repair);
|
||||
|
||||
mkString(v, dstPath, singleton<PathSet>(dstPath));
|
||||
}
|
||||
@@ -689,7 +731,7 @@ static void prim_attrNames(EvalState & state, Value * * args, Value & v)
|
||||
|
||||
|
||||
/* Dynamic version of the `.' operator. */
|
||||
static void prim_getAttr(EvalState & state, Value * * args, Value & v)
|
||||
void prim_getAttr(EvalState & state, Value * * args, Value & v)
|
||||
{
|
||||
string attr = state.forceStringNoCtx(*args[0]);
|
||||
state.forceAttrs(*args[1]);
|
||||
@@ -698,6 +740,7 @@ static void prim_getAttr(EvalState & state, Value * * args, Value & v)
|
||||
if (i == args[1]->attrs->end())
|
||||
throw EvalError(format("attribute `%1%' missing") % attr);
|
||||
// !!! add to stack trace?
|
||||
if (state.countCalls && i->pos) state.attrSelects[*i->pos]++;
|
||||
state.forceValue(*i->value);
|
||||
v = *i->value;
|
||||
}
|
||||
@@ -843,19 +886,33 @@ static void prim_isList(EvalState & state, Value * * args, Value & v)
|
||||
}
|
||||
|
||||
|
||||
static void elemAt(EvalState & state, Value & list, int n, Value & v)
|
||||
{
|
||||
state.forceList(list);
|
||||
if (n < 0 || n >= list.list.length)
|
||||
throw Error(format("list index %1% is out of bounds") % n);
|
||||
state.forceValue(*list.list.elems[n]);
|
||||
v = *list.list.elems[n];
|
||||
}
|
||||
|
||||
|
||||
/* Return the n-1'th element of a list. */
|
||||
static void prim_elemAt(EvalState & state, Value * * args, Value & v)
|
||||
{
|
||||
elemAt(state, *args[0], state.forceInt(*args[1]), v);
|
||||
}
|
||||
|
||||
|
||||
/* Return the first element of a list. */
|
||||
static void prim_head(EvalState & state, Value * * args, Value & v)
|
||||
{
|
||||
state.forceList(*args[0]);
|
||||
if (args[0]->list.length == 0)
|
||||
throw Error("`head' called on an empty list");
|
||||
state.forceValue(*args[0]->list.elems[0]);
|
||||
v = *args[0]->list.elems[0];
|
||||
elemAt(state, *args[0], 0, v);
|
||||
}
|
||||
|
||||
|
||||
/* Return a list consisting of everything but the the first element of
|
||||
a list. */
|
||||
a list. Warning: this function takes O(n) time, so you probably
|
||||
don't want to use it! */
|
||||
static void prim_tail(EvalState & state, Value * * args, Value & v)
|
||||
{
|
||||
state.forceList(*args[0]);
|
||||
@@ -881,6 +938,59 @@ static void prim_map(EvalState & state, Value * * args, Value & v)
|
||||
}
|
||||
|
||||
|
||||
/* Filter a list using a predicate; that is, return a list containing
|
||||
every element from the list for which the predicate function
|
||||
returns true. */
|
||||
static void prim_filter(EvalState & state, Value * * args, Value & v)
|
||||
{
|
||||
state.forceFunction(*args[0]);
|
||||
state.forceList(*args[1]);
|
||||
|
||||
// FIXME: putting this on the stack is risky.
|
||||
Value * vs[args[1]->list.length];
|
||||
unsigned int k = 0;
|
||||
|
||||
bool same = true;
|
||||
for (unsigned int n = 0; n < args[1]->list.length; ++n) {
|
||||
Value res;
|
||||
state.callFunction(*args[0], *args[1]->list.elems[n], res);
|
||||
if (state.forceBool(res))
|
||||
vs[k++] = args[1]->list.elems[n];
|
||||
else
|
||||
same = false;
|
||||
}
|
||||
|
||||
if (same)
|
||||
v = *args[1];
|
||||
else {
|
||||
state.mkList(v, k);
|
||||
for (unsigned int n = 0; n < k; ++n) v.list.elems[n] = vs[n];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* Return true if a list contains a given element. */
|
||||
static void prim_elem(EvalState & state, Value * * args, Value & v)
|
||||
{
|
||||
bool res = false;
|
||||
state.forceList(*args[1]);
|
||||
for (unsigned int n = 0; n < args[1]->list.length; ++n)
|
||||
if (state.eqValues(*args[0], *args[1]->list.elems[n])) {
|
||||
res = true;
|
||||
break;
|
||||
}
|
||||
mkBool(v, res);
|
||||
}
|
||||
|
||||
|
||||
/* Concatenate a list of lists. */
|
||||
static void prim_concatLists(EvalState & state, Value * * args, Value & v)
|
||||
{
|
||||
state.forceList(*args[0]);
|
||||
state.concatLists(v, args[0]->list.length, args[0]->list.elems);
|
||||
}
|
||||
|
||||
|
||||
/* Return the length of a list. This is an O(1) time operation. */
|
||||
static void prim_length(EvalState & state, Value * * args, Value & v)
|
||||
{
|
||||
@@ -997,6 +1107,21 @@ static void prim_unsafeDiscardOutputDependency(EvalState & state, Value * * args
|
||||
}
|
||||
|
||||
|
||||
/* Return the cryptographic hash of a string in base-16. */
|
||||
static void prim_hashString(EvalState & state, Value * * args, Value & v)
|
||||
{
|
||||
string type = state.forceStringNoCtx(*args[0]);
|
||||
HashType ht = parseHashType(type);
|
||||
if (ht == htUnknown)
|
||||
throw Error(format("unknown hash type `%1%'") % type);
|
||||
|
||||
PathSet context; // discarded
|
||||
string s = state.forceString(*args[1], context);
|
||||
|
||||
mkString(v, printHash(hashString(ht, s)), context);
|
||||
};
|
||||
|
||||
|
||||
/*************************************************************
|
||||
* Versions
|
||||
*************************************************************/
|
||||
@@ -1049,9 +1174,19 @@ void EvalState::createBaseEnv()
|
||||
mkInt(v, time(0));
|
||||
addConstant("__currentTime", v);
|
||||
|
||||
mkString(v, thisSystem.c_str());
|
||||
mkString(v, settings.thisSystem.c_str());
|
||||
addConstant("__currentSystem", v);
|
||||
|
||||
mkString(v, nixVersion.c_str());
|
||||
addConstant("__nixVersion", v);
|
||||
|
||||
/* Language version. This should be increased every time a new
|
||||
language feature gets added. It's not necessary to increase it
|
||||
when primops get added, because you can just use `builtins ?
|
||||
primOp' to check. */
|
||||
mkInt(v, 1);
|
||||
addConstant("__langVersion", v);
|
||||
|
||||
// Miscellaneous
|
||||
addPrimOp("import", 1, prim_import);
|
||||
addPrimOp("isNull", 1, prim_isNull);
|
||||
@@ -1092,11 +1227,15 @@ void EvalState::createBaseEnv()
|
||||
|
||||
// Lists
|
||||
addPrimOp("__isList", 1, prim_isList);
|
||||
addPrimOp("__elemAt", 2, prim_elemAt);
|
||||
addPrimOp("__head", 1, prim_head);
|
||||
addPrimOp("__tail", 1, prim_tail);
|
||||
addPrimOp("map", 2, prim_map);
|
||||
addPrimOp("__filter", 2, prim_filter);
|
||||
addPrimOp("__elem", 2, prim_elem);
|
||||
addPrimOp("__concatLists", 1, prim_concatLists);
|
||||
addPrimOp("__length", 1, prim_length);
|
||||
|
||||
|
||||
// Integer arithmetic
|
||||
addPrimOp("__add", 2, prim_add);
|
||||
addPrimOp("__sub", 2, prim_sub);
|
||||
@@ -1110,6 +1249,7 @@ void EvalState::createBaseEnv()
|
||||
addPrimOp("__stringLength", 1, prim_stringLength);
|
||||
addPrimOp("__unsafeDiscardStringContext", 1, prim_unsafeDiscardStringContext);
|
||||
addPrimOp("__unsafeDiscardOutputDependency", 1, prim_unsafeDiscardOutputDependency);
|
||||
addPrimOp("__hashString", 2, prim_hashString);
|
||||
|
||||
// Versions
|
||||
addPrimOp("__parseDrvName", 1, prim_parseDrvName);
|
||||
|
||||
@@ -131,7 +131,7 @@ static inline void mkStringNoCopy(Value & v, const char * s)
|
||||
|
||||
static inline void mkString(Value & v, const Symbol & s)
|
||||
{
|
||||
mkStringNoCopy(v, ((string) s).c_str());
|
||||
mkStringNoCopy(v, ((const string &) s).c_str());
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -7,6 +7,5 @@ libmain_la_LIBADD = ../libstore/libstore.la @BDW_GC_LIBS@
|
||||
pkginclude_HEADERS = shared.hh
|
||||
|
||||
AM_CXXFLAGS = \
|
||||
-DNIX_VERSION=\"$(VERSION)\" \
|
||||
-I$(srcdir)/.. -I$(srcdir)/../libutil \
|
||||
-I$(srcdir)/../libstore
|
||||
|
||||
+57
-95
@@ -10,6 +10,7 @@
|
||||
#include <cctype>
|
||||
#include <exception>
|
||||
|
||||
#include <sys/time.h>
|
||||
#include <sys/stat.h>
|
||||
#include <unistd.h>
|
||||
|
||||
@@ -33,20 +34,10 @@ static void sigintHandler(int signo)
|
||||
}
|
||||
|
||||
|
||||
Path makeRootName(const Path & gcRoot, int & counter)
|
||||
{
|
||||
counter++;
|
||||
if (counter == 1)
|
||||
return gcRoot;
|
||||
else
|
||||
return (format("%1%-%2%") % gcRoot % counter).str();
|
||||
}
|
||||
|
||||
|
||||
void printGCWarning()
|
||||
{
|
||||
static bool haveWarned = false;
|
||||
warnOnce(haveWarned,
|
||||
warnOnce(haveWarned,
|
||||
"you did not specify `--add-root'; "
|
||||
"the result might be removed by the garbage collector");
|
||||
}
|
||||
@@ -57,7 +48,14 @@ void printMissing(StoreAPI & store, const PathSet & paths)
|
||||
unsigned long long downloadSize, narSize;
|
||||
PathSet willBuild, willSubstitute, unknown;
|
||||
queryMissing(store, paths, willBuild, willSubstitute, unknown, downloadSize, narSize);
|
||||
printMissing(willBuild, willSubstitute, unknown, downloadSize, narSize);
|
||||
}
|
||||
|
||||
|
||||
void printMissing(const PathSet & willBuild,
|
||||
const PathSet & willSubstitute, const PathSet & unknown,
|
||||
unsigned long long downloadSize, unsigned long long narSize)
|
||||
{
|
||||
if (!willBuild.empty()) {
|
||||
printMsg(lvlInfo, format("these derivations will be built:"));
|
||||
foreach (PathSet::iterator, i, willBuild)
|
||||
@@ -74,7 +72,7 @@ void printMissing(StoreAPI & store, const PathSet & paths)
|
||||
|
||||
if (!unknown.empty()) {
|
||||
printMsg(lvlInfo, format("don't know how to build these paths%1%:")
|
||||
% (readOnlyMode ? " (may be caused by read-only store access)" : ""));
|
||||
% (settings.readOnlyMode ? " (may be caused by read-only store access)" : ""));
|
||||
foreach (PathSet::iterator, i, unknown)
|
||||
printMsg(lvlInfo, format(" %1%") % *i);
|
||||
}
|
||||
@@ -93,12 +91,22 @@ static void setLogType(string lt)
|
||||
static bool showTrace = false;
|
||||
|
||||
|
||||
string getArg(const string & opt,
|
||||
Strings::iterator & i, const Strings::iterator & end)
|
||||
{
|
||||
++i;
|
||||
if (i == end) throw UsageError(format("`%1%' requires an argument") % opt);
|
||||
return *i;
|
||||
}
|
||||
|
||||
|
||||
/* Initialize and reorder arguments, then call the actual argument
|
||||
processor. */
|
||||
static void initAndRun(int argc, char * * argv)
|
||||
{
|
||||
setDefaultsFromEnvironment();
|
||||
|
||||
settings.processEnvironment();
|
||||
settings.loadConfFile();
|
||||
|
||||
/* Catch SIGINT. */
|
||||
struct sigaction act;
|
||||
act.sa_handler = sigintHandler;
|
||||
@@ -125,10 +133,14 @@ static void initAndRun(int argc, char * * argv)
|
||||
|
||||
/* There is no privacy in the Nix system ;-) At least not for
|
||||
now. In particular, store objects should be readable by
|
||||
everybody. This prevents nasty surprises when using a shared
|
||||
store (with the setuid() hack). */
|
||||
everybody. */
|
||||
umask(0022);
|
||||
|
||||
/* Initialise the PRNG. */
|
||||
struct timeval tv;
|
||||
gettimeofday(&tv, 0);
|
||||
srandom(tv.tv_usec);
|
||||
|
||||
/* Process the NIX_LOG_TYPE environment variable. */
|
||||
string lt = getEnv("NIX_LOG_TYPE");
|
||||
if (lt != "") setLogType(lt);
|
||||
@@ -137,7 +149,7 @@ static void initAndRun(int argc, char * * argv)
|
||||
Strings args, remaining;
|
||||
while (argc--) args.push_back(*argv++);
|
||||
args.erase(args.begin());
|
||||
|
||||
|
||||
/* Expand compound dash options (i.e., `-qlf' -> `-q -l -f'), and
|
||||
ignore options for the ATerm library. */
|
||||
for (Strings::iterator i = args.begin(); i != args.end(); ++i) {
|
||||
@@ -156,46 +168,44 @@ static void initAndRun(int argc, char * * argv)
|
||||
remaining.clear();
|
||||
|
||||
/* Process default options. */
|
||||
int verbosityDelta = 0;
|
||||
for (Strings::iterator i = args.begin(); i != args.end(); ++i) {
|
||||
string arg = *i;
|
||||
if (arg == "--verbose" || arg == "-v") verbosityDelta++;
|
||||
else if (arg == "--quiet") verbosityDelta--;
|
||||
if (arg == "--verbose" || arg == "-v") verbosity = (Verbosity) (verbosity + 1);
|
||||
else if (arg == "--quiet") verbosity = verbosity > lvlError ? (Verbosity) (verbosity - 1) : lvlError;
|
||||
else if (arg == "--log-type") {
|
||||
++i;
|
||||
if (i == args.end()) throw UsageError("`--log-type' requires an argument");
|
||||
setLogType(*i);
|
||||
string s = getArg(arg, i, args.end());
|
||||
setLogType(s);
|
||||
}
|
||||
else if (arg == "--no-build-output" || arg == "-Q")
|
||||
buildVerbosity = lvlVomit;
|
||||
settings.buildVerbosity = lvlVomit;
|
||||
else if (arg == "--print-build-trace")
|
||||
printBuildTrace = true;
|
||||
settings.printBuildTrace = true;
|
||||
else if (arg == "--help") {
|
||||
printHelp();
|
||||
return;
|
||||
}
|
||||
else if (arg == "--version") {
|
||||
std::cout << format("%1% (Nix) %2%") % programId % NIX_VERSION << std::endl;
|
||||
std::cout << format("%1% (Nix) %2%") % programId % nixVersion << std::endl;
|
||||
return;
|
||||
}
|
||||
else if (arg == "--keep-failed" || arg == "-K")
|
||||
keepFailed = true;
|
||||
settings.keepFailed = true;
|
||||
else if (arg == "--keep-going" || arg == "-k")
|
||||
keepGoing = true;
|
||||
settings.keepGoing = true;
|
||||
else if (arg == "--fallback")
|
||||
tryFallback = true;
|
||||
settings.set("build-fallback", "true");
|
||||
else if (arg == "--max-jobs" || arg == "-j")
|
||||
maxBuildJobs = getIntArg<unsigned int>(arg, i, args.end());
|
||||
settings.set("build-max-jobs", getArg(arg, i, args.end()));
|
||||
else if (arg == "--cores")
|
||||
buildCores = getIntArg<unsigned int>(arg, i, args.end());
|
||||
settings.set("build-cores", getArg(arg, i, args.end()));
|
||||
else if (arg == "--readonly-mode")
|
||||
readOnlyMode = true;
|
||||
settings.readOnlyMode = true;
|
||||
else if (arg == "--max-silent-time")
|
||||
maxSilentTime = getIntArg<unsigned int>(arg, i, args.end());
|
||||
settings.set("build-max-silent-time", getArg(arg, i, args.end()));
|
||||
else if (arg == "--timeout")
|
||||
buildTimeout = getIntArg<unsigned int>(arg, i, args.end());
|
||||
settings.set("build-timeout", getArg(arg, i, args.end()));
|
||||
else if (arg == "--no-build-hook")
|
||||
useBuildHook = false;
|
||||
settings.useBuildHook = false;
|
||||
else if (arg == "--show-trace")
|
||||
showTrace = true;
|
||||
else if (arg == "--option") {
|
||||
@@ -203,14 +213,13 @@ static void initAndRun(int argc, char * * argv)
|
||||
string name = *i;
|
||||
++i; if (i == args.end()) throw UsageError("`--option' requires two arguments");
|
||||
string value = *i;
|
||||
overrideSetting(name, tokenizeString(value));
|
||||
settings.set(name, value);
|
||||
}
|
||||
else remaining.push_back(arg);
|
||||
}
|
||||
|
||||
verbosityDelta += queryIntSetting("verbosity", lvlInfo);
|
||||
verbosity = (Verbosity) (verbosityDelta < 0 ? 0 : verbosityDelta);
|
||||
|
||||
settings.update();
|
||||
|
||||
run(remaining);
|
||||
|
||||
/* Close the Nix database. */
|
||||
@@ -218,56 +227,6 @@ static void initAndRun(int argc, char * * argv)
|
||||
}
|
||||
|
||||
|
||||
bool setuidMode = false;
|
||||
|
||||
|
||||
static void setuidInit()
|
||||
{
|
||||
/* Don't do anything if this is not a setuid binary. */
|
||||
if (getuid() == geteuid() && getgid() == getegid()) return;
|
||||
|
||||
uid_t nixUid = geteuid();
|
||||
gid_t nixGid = getegid();
|
||||
|
||||
setuidCleanup();
|
||||
|
||||
/* Don't trust the current directory. */
|
||||
if (chdir("/") == -1) abort();
|
||||
|
||||
/* Set the real (and preferably also the save) uid/gid to the
|
||||
effective uid/gid. This matters mostly when we're not using
|
||||
build-users (bad!), since some builders (like Perl) complain
|
||||
when real != effective.
|
||||
|
||||
On systems where setresuid is unavailable, we can't drop the
|
||||
saved uid/gid. This means that we could go back to the
|
||||
original real uid (i.e., the uid of the caller). That's not
|
||||
really a problem, except maybe when we execute a builder and
|
||||
we're not using build-users. In that case, the builder may be
|
||||
able to switch to the uid of the caller and possibly do bad
|
||||
stuff. But note that when not using build-users, the builder
|
||||
could also modify the Nix executables (say, replace them by a
|
||||
Trojan horse), so the problem is already there. */
|
||||
|
||||
#if HAVE_SETRESUID
|
||||
if (setresuid(nixUid, nixUid, nixUid)) abort();
|
||||
if (setresgid(nixGid, nixGid, nixGid)) abort();
|
||||
#elif HAVE_SETREUID
|
||||
/* Note: doesn't set saved uid/gid! */
|
||||
fprintf(stderr, "warning: cannot set saved uid\n");
|
||||
if (setreuid(nixUid, nixUid)) abort();
|
||||
if (setregid(nixGid, nixGid)) abort();
|
||||
#else
|
||||
/* Note: doesn't set real and saved uid/gid! */
|
||||
fprintf(stderr, "warning: cannot set real and saved uids\n");
|
||||
if (setuid(nixUid)) abort();
|
||||
if (setgid(nixGid)) abort();
|
||||
#endif
|
||||
|
||||
setuidMode = true;
|
||||
}
|
||||
|
||||
|
||||
/* Called when the Boehm GC runs out of memory. */
|
||||
static void * oomHandler(size_t requested)
|
||||
{
|
||||
@@ -276,6 +235,14 @@ static void * oomHandler(size_t requested)
|
||||
}
|
||||
|
||||
|
||||
void showManPage(const string & name)
|
||||
{
|
||||
string cmd = "man " + name;
|
||||
if (system(cmd.c_str()) != 0)
|
||||
throw Error(format("command `%1%' failed") % cmd);
|
||||
}
|
||||
|
||||
|
||||
int exitCode = 0;
|
||||
char * * argvSaved = 0;
|
||||
|
||||
@@ -290,11 +257,6 @@ int main(int argc, char * * argv)
|
||||
|
||||
argvSaved = argv;
|
||||
|
||||
/* If we're setuid, then we need to take some security precautions
|
||||
right away. */
|
||||
if (argc == 0) abort();
|
||||
setuidInit();
|
||||
|
||||
/* Turn on buffering for cerr. */
|
||||
#if HAVE_PUBSETBUF
|
||||
std::cerr.rdbuf()->pubsetbuf(buf, sizeof(buf));
|
||||
@@ -323,7 +285,7 @@ int main(int argc, char * * argv)
|
||||
throw;
|
||||
}
|
||||
} catch (UsageError & e) {
|
||||
printMsg(lvlError,
|
||||
printMsg(lvlError,
|
||||
format(
|
||||
"error: %1%\n"
|
||||
"Try `%2% --help' for more information.")
|
||||
|
||||
@@ -26,11 +26,14 @@ MakeError(UsageError, nix::Error);
|
||||
class StoreAPI;
|
||||
|
||||
/* Ugh. No better place to put this. */
|
||||
Path makeRootName(const Path & gcRoot, int & counter);
|
||||
void printGCWarning();
|
||||
|
||||
void printMissing(StoreAPI & store, const PathSet & paths);
|
||||
|
||||
void printMissing(const PathSet & willBuild,
|
||||
const PathSet & willSubstitute, const PathSet & unknown,
|
||||
unsigned long long downloadSize, unsigned long long narSize);
|
||||
|
||||
template<class N> N getIntArg(const string & opt,
|
||||
Strings::iterator & i, const Strings::iterator & end)
|
||||
{
|
||||
@@ -42,8 +45,8 @@ template<class N> N getIntArg(const string & opt,
|
||||
return n;
|
||||
}
|
||||
|
||||
/* Whether we're running setuid. */
|
||||
extern bool setuidMode;
|
||||
/* Show the manual page for the specified program. */
|
||||
void showManPage(const string & name);
|
||||
|
||||
extern volatile ::sig_atomic_t blockInt;
|
||||
|
||||
|
||||
@@ -23,10 +23,11 @@ AM_CXXFLAGS = -Wall \
|
||||
-DNIX_CONF_DIR=\"$(sysconfdir)/nix\" \
|
||||
-DNIX_LIBEXEC_DIR=\"$(libexecdir)\" \
|
||||
-DNIX_BIN_DIR=\"$(bindir)\" \
|
||||
-DNIX_VERSION=\"$(VERSION)\" \
|
||||
-I$(srcdir)/.. -I$(srcdir)/../libutil \
|
||||
-I$(srcdir)/../libstore
|
||||
|
||||
local-store.lo: schema.sql.hh
|
||||
|
||||
%.sql.hh: %.sql
|
||||
../bin2c/bin2c schema < $< > $@ || (rm $@ && exit 1)
|
||||
sed -e 's/"/\\"/g' -e 's/\(.*\)/"\1\\n"/' < $< > $@ || (rm $@ && exit 1)
|
||||
|
||||
+699
-388
File diff suppressed because it is too large
Load Diff
+36
-13
@@ -12,7 +12,7 @@ void DerivationOutput::parseHashInfo(bool & recursive, HashType & hashType, Hash
|
||||
{
|
||||
recursive = false;
|
||||
string algo = hashAlgo;
|
||||
|
||||
|
||||
if (string(algo, 0, 2) == "r:") {
|
||||
recursive = true;
|
||||
algo = string(algo, 2);
|
||||
@@ -21,13 +21,13 @@ void DerivationOutput::parseHashInfo(bool & recursive, HashType & hashType, Hash
|
||||
hashType = parseHashType(algo);
|
||||
if (hashType == htUnknown)
|
||||
throw Error(format("unknown hash algorithm `%1%'") % algo);
|
||||
|
||||
|
||||
hash = parseHash(hashType, this->hash);
|
||||
}
|
||||
|
||||
|
||||
Path writeDerivation(StoreAPI & store,
|
||||
const Derivation & drv, const string & name)
|
||||
const Derivation & drv, const string & name, bool repair)
|
||||
{
|
||||
PathSet references;
|
||||
references.insert(drv.inputSrcs.begin(), drv.inputSrcs.end());
|
||||
@@ -38,9 +38,9 @@ Path writeDerivation(StoreAPI & store,
|
||||
held during a garbage collection). */
|
||||
string suffix = name + drvExtension;
|
||||
string contents = unparseDerivation(drv);
|
||||
return readOnlyMode
|
||||
return settings.readOnlyMode
|
||||
? computeStorePathForText(suffix, contents, references)
|
||||
: store.addTextToStore(suffix, contents, references);
|
||||
: store.addTextToStore(suffix, contents, references, repair);
|
||||
}
|
||||
|
||||
|
||||
@@ -51,7 +51,7 @@ static Path parsePath(std::istream & str)
|
||||
throw Error(format("bad path `%1%' in derivation") % s);
|
||||
return s;
|
||||
}
|
||||
|
||||
|
||||
|
||||
static StringSet parseStrings(std::istream & str, bool arePaths)
|
||||
{
|
||||
@@ -60,7 +60,7 @@ static StringSet parseStrings(std::istream & str, bool arePaths)
|
||||
res.insert(arePaths ? parsePath(str) : parseString(str));
|
||||
return res;
|
||||
}
|
||||
|
||||
|
||||
|
||||
Derivation parseDerivation(const string & s)
|
||||
{
|
||||
@@ -106,7 +106,7 @@ Derivation parseDerivation(const string & s)
|
||||
expect(str, ")");
|
||||
drv.env[name] = value;
|
||||
}
|
||||
|
||||
|
||||
expect(str, ")");
|
||||
return drv;
|
||||
}
|
||||
@@ -165,7 +165,7 @@ string unparseDerivation(const Derivation & drv)
|
||||
|
||||
s += "],";
|
||||
printStrings(s, drv.inputSrcs.begin(), drv.inputSrcs.end());
|
||||
|
||||
|
||||
s += ','; printString(s, drv.platform);
|
||||
s += ','; printString(s, drv.builder);
|
||||
s += ','; printStrings(s, drv.args.begin(), drv.args.end());
|
||||
@@ -178,9 +178,9 @@ string unparseDerivation(const Derivation & drv)
|
||||
s += ','; printString(s, i->second);
|
||||
s += ')';
|
||||
}
|
||||
|
||||
|
||||
s += "])";
|
||||
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
@@ -190,7 +190,7 @@ bool isDerivation(const string & fileName)
|
||||
return hasSuffix(fileName, drvExtension);
|
||||
}
|
||||
|
||||
|
||||
|
||||
bool isFixedOutputDrv(const Derivation & drv)
|
||||
{
|
||||
return drv.outputs.size() == 1 &&
|
||||
@@ -247,9 +247,32 @@ Hash hashDerivationModulo(StoreAPI & store, Derivation drv)
|
||||
inputs2[printHash(h)] = i->second;
|
||||
}
|
||||
drv.inputDrvs = inputs2;
|
||||
|
||||
|
||||
return hashString(htSHA256, unparseDerivation(drv));
|
||||
}
|
||||
|
||||
|
||||
DrvPathWithOutputs parseDrvPathWithOutputs(const string & s)
|
||||
{
|
||||
size_t n = s.find("!");
|
||||
return n == s.npos
|
||||
? DrvPathWithOutputs(s, std::set<string>())
|
||||
: DrvPathWithOutputs(string(s, 0, n), tokenizeString<std::set<string> >(string(s, n + 1), ","));
|
||||
}
|
||||
|
||||
|
||||
Path makeDrvPathWithOutputs(const Path & drvPath, const std::set<string> & outputs)
|
||||
{
|
||||
return outputs.empty()
|
||||
? drvPath
|
||||
: drvPath + "!" + concatStringsSep(",", outputs);
|
||||
}
|
||||
|
||||
|
||||
bool wantOutput(const string & output, const std::set<string> & wanted)
|
||||
{
|
||||
return wanted.empty() || wanted.find(output) != wanted.end();
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
#pragma once
|
||||
|
||||
#include <map>
|
||||
|
||||
#include "types.hh"
|
||||
#include "hash.hh"
|
||||
|
||||
#include <map>
|
||||
|
||||
|
||||
namespace nix {
|
||||
|
||||
@@ -57,7 +57,7 @@ class StoreAPI;
|
||||
|
||||
/* Write a derivation to the Nix store, and return its path. */
|
||||
Path writeDerivation(StoreAPI & store,
|
||||
const Derivation & drv, const string & name);
|
||||
const Derivation & drv, const string & name, bool repair = false);
|
||||
|
||||
/* Parse a derivation. */
|
||||
Derivation parseDerivation(const string & s);
|
||||
@@ -79,4 +79,15 @@ typedef std::map<Path, Hash> DrvHashes;
|
||||
|
||||
extern DrvHashes drvHashes;
|
||||
|
||||
/* Split a string specifying a derivation and a set of outputs
|
||||
(/nix/store/hash-foo!out1,out2,...) into the derivation path and
|
||||
the outputs. */
|
||||
typedef std::pair<string, std::set<string> > DrvPathWithOutputs;
|
||||
DrvPathWithOutputs parseDrvPathWithOutputs(const string & s);
|
||||
|
||||
Path makeDrvPathWithOutputs(const Path & drvPath, const std::set<string> & outputs);
|
||||
|
||||
bool wantOutput(const string & output, const std::set<string> & wanted);
|
||||
|
||||
|
||||
}
|
||||
|
||||
+240
-173
@@ -1,7 +1,6 @@
|
||||
#include "globals.hh"
|
||||
#include "misc.hh"
|
||||
#include "local-store.hh"
|
||||
#include "immutable.hh"
|
||||
|
||||
#include <boost/shared_ptr.hpp>
|
||||
|
||||
@@ -34,10 +33,10 @@ static const int defaultGcLevel = 1000;
|
||||
int LocalStore::openGCLock(LockType lockType)
|
||||
{
|
||||
Path fnGCLock = (format("%1%/%2%")
|
||||
% nixStateDir % gcLockName).str();
|
||||
|
||||
% settings.nixStateDir % gcLockName).str();
|
||||
|
||||
debug(format("acquiring global GC lock `%1%'") % fnGCLock);
|
||||
|
||||
|
||||
AutoCloseFD fdGCLock = open(fnGCLock.c_str(), O_RDWR | O_CREAT, 0600);
|
||||
if (fdGCLock == -1)
|
||||
throw SysError(format("opening global GC lock `%1%'") % fnGCLock);
|
||||
@@ -51,7 +50,7 @@ int LocalStore::openGCLock(LockType lockType)
|
||||
/* !!! Restrict read permission on the GC root. Otherwise any
|
||||
process that can open the file for reading can DoS the
|
||||
collector. */
|
||||
|
||||
|
||||
return fdGCLock.borrow();
|
||||
}
|
||||
|
||||
@@ -85,7 +84,7 @@ void LocalStore::addIndirectRoot(const Path & path)
|
||||
{
|
||||
string hash = printHash32(hashString(htSHA1, path));
|
||||
Path realRoot = canonPath((format("%1%/%2%/auto/%3%")
|
||||
% nixStateDir % gcRootsDir % hash).str());
|
||||
% settings.nixStateDir % gcRootsDir % hash).str());
|
||||
createSymlink(realRoot, path);
|
||||
}
|
||||
|
||||
@@ -113,15 +112,15 @@ Path addPermRoot(StoreAPI & store, const Path & _storePath,
|
||||
|
||||
else {
|
||||
if (!allowOutsideRootsDir) {
|
||||
Path rootsDir = canonPath((format("%1%/%2%") % nixStateDir % gcRootsDir).str());
|
||||
|
||||
Path rootsDir = canonPath((format("%1%/%2%") % settings.nixStateDir % gcRootsDir).str());
|
||||
|
||||
if (string(gcRoot, 0, rootsDir.size() + 1) != rootsDir + "/")
|
||||
throw Error(format(
|
||||
"path `%1%' is not a valid garbage collector root; "
|
||||
"it's not in the directory `%2%'")
|
||||
% gcRoot % rootsDir);
|
||||
}
|
||||
|
||||
|
||||
createSymlink(gcRoot, storePath);
|
||||
}
|
||||
|
||||
@@ -130,10 +129,10 @@ Path addPermRoot(StoreAPI & store, const Path & _storePath,
|
||||
Instead of reading all the roots, it would be more efficient to
|
||||
check if the root is in a directory in or linked from the
|
||||
gcroots directory. */
|
||||
if (queryBoolSetting("gc-check-reachability", false)) {
|
||||
if (settings.checkRootReachability) {
|
||||
Roots roots = store.findRoots();
|
||||
if (roots.find(gcRoot) == roots.end())
|
||||
printMsg(lvlError,
|
||||
printMsg(lvlError,
|
||||
format(
|
||||
"warning: `%1%' is not in a directory where the garbage collector looks for roots; "
|
||||
"therefore, `%2%' might be removed by the garbage collector")
|
||||
@@ -144,7 +143,7 @@ Path addPermRoot(StoreAPI & store, const Path & _storePath,
|
||||
progress. This prevents the set of permanent roots from
|
||||
increasing while a GC is in progress. */
|
||||
store.syncWithGC();
|
||||
|
||||
|
||||
return gcRoot;
|
||||
}
|
||||
|
||||
@@ -160,23 +159,23 @@ void LocalStore::addTempRoot(const Path & path)
|
||||
if (fdTempRoots == -1) {
|
||||
|
||||
while (1) {
|
||||
Path dir = (format("%1%/%2%") % nixStateDir % tempRootsDir).str();
|
||||
Path dir = (format("%1%/%2%") % settings.nixStateDir % tempRootsDir).str();
|
||||
createDirs(dir);
|
||||
|
||||
|
||||
fnTempRoots = (format("%1%/%2%")
|
||||
% dir % getpid()).str();
|
||||
|
||||
AutoCloseFD fdGCLock = openGCLock(ltRead);
|
||||
|
||||
|
||||
if (pathExists(fnTempRoots))
|
||||
/* It *must* be stale, since there can be no two
|
||||
processes with the same pid. */
|
||||
unlink(fnTempRoots.c_str());
|
||||
|
||||
fdTempRoots = openLockFile(fnTempRoots, true);
|
||||
fdTempRoots = openLockFile(fnTempRoots, true);
|
||||
|
||||
fdGCLock.close();
|
||||
|
||||
|
||||
debug(format("acquiring read lock on `%1%'") % fnTempRoots);
|
||||
lockFile(fdTempRoots, ltRead, true);
|
||||
|
||||
@@ -186,7 +185,7 @@ void LocalStore::addTempRoot(const Path & path)
|
||||
if (fstat(fdTempRoots, &st) == -1)
|
||||
throw SysError(format("statting `%1%'") % fnTempRoots);
|
||||
if (st.st_size == 0) break;
|
||||
|
||||
|
||||
/* The garbage collector deleted this file before we could
|
||||
get a lock. (It won't delete the file after we get a
|
||||
lock.) Try again. */
|
||||
@@ -218,7 +217,7 @@ void removeTempRoots()
|
||||
|
||||
|
||||
/* Automatically clean up the temporary roots file when we exit. */
|
||||
struct RemoveTempRoots
|
||||
struct RemoveTempRoots
|
||||
{
|
||||
~RemoveTempRoots()
|
||||
{
|
||||
@@ -238,10 +237,10 @@ static void readTempRoots(PathSet & tempRoots, FDs & fds)
|
||||
/* Read the `temproots' directory for per-process temporary root
|
||||
files. */
|
||||
Strings tempRootFiles = readDirectory(
|
||||
(format("%1%/%2%") % nixStateDir % tempRootsDir).str());
|
||||
(format("%1%/%2%") % settings.nixStateDir % tempRootsDir).str());
|
||||
|
||||
foreach (Strings::iterator, i, tempRootFiles) {
|
||||
Path path = (format("%1%/%2%/%3%") % nixStateDir % tempRootsDir % *i).str();
|
||||
Path path = (format("%1%/%2%/%3%") % settings.nixStateDir % tempRootsDir % *i).str();
|
||||
|
||||
debug(format("reading temporary root file `%1%'") % path);
|
||||
FDPtr fd(new AutoCloseFD(open(path.c_str(), O_RDWR, 0666)));
|
||||
@@ -295,7 +294,7 @@ static void findRoots(StoreAPI & store, const Path & path,
|
||||
bool recurseSymlinks, bool deleteStale, Roots & roots)
|
||||
{
|
||||
try {
|
||||
|
||||
|
||||
struct stat st;
|
||||
if (lstat(path.c_str(), &st) == -1)
|
||||
throw SysError(format("statting `%1%'") % path);
|
||||
@@ -315,7 +314,7 @@ static void findRoots(StoreAPI & store, const Path & path,
|
||||
debug(format("found root `%1%' in `%2%'")
|
||||
% target % path);
|
||||
Path storePath = toStorePath(target);
|
||||
if (store.isValidPath(storePath))
|
||||
if (store.isValidPath(storePath))
|
||||
roots[path] = storePath;
|
||||
else
|
||||
printMsg(lvlInfo, format("skipping invalid root from `%1%' to `%2%'")
|
||||
@@ -350,7 +349,7 @@ static void findRoots(StoreAPI & store, const Path & path,
|
||||
static Roots findRoots(StoreAPI & store, bool deleteStale)
|
||||
{
|
||||
Roots roots;
|
||||
Path rootsDir = canonPath((format("%1%/%2%") % nixStateDir % gcRootsDir).str());
|
||||
Path rootsDir = canonPath((format("%1%/%2%") % settings.nixStateDir % gcRootsDir).str());
|
||||
findRoots(store, rootsDir, true, deleteStale, roots);
|
||||
return roots;
|
||||
}
|
||||
@@ -365,16 +364,16 @@ Roots LocalStore::findRoots()
|
||||
static void addAdditionalRoots(StoreAPI & store, PathSet & roots)
|
||||
{
|
||||
Path rootFinder = getEnv("NIX_ROOT_FINDER",
|
||||
nixLibexecDir + "/nix/find-runtime-roots.pl");
|
||||
settings.nixLibexecDir + "/nix/find-runtime-roots.pl");
|
||||
|
||||
if (rootFinder.empty()) return;
|
||||
|
||||
|
||||
debug(format("executing `%1%' to find additional roots") % rootFinder);
|
||||
|
||||
string result = runProgram(rootFinder);
|
||||
|
||||
Strings paths = tokenizeString(result, "\n");
|
||||
|
||||
Strings paths = tokenizeString<Strings>(result, "\n");
|
||||
|
||||
foreach (Strings::iterator, i, paths) {
|
||||
if (isInStore(*i)) {
|
||||
Path path = toStorePath(*i);
|
||||
@@ -396,24 +395,17 @@ struct LocalStore::GCState
|
||||
GCResults & results;
|
||||
PathSet roots;
|
||||
PathSet tempRoots;
|
||||
PathSet deleted;
|
||||
PathSet live;
|
||||
PathSet busy;
|
||||
PathSet invalidated;
|
||||
PathSet dead;
|
||||
PathSet alive;
|
||||
bool gcKeepOutputs;
|
||||
bool gcKeepDerivations;
|
||||
unsigned long long bytesInvalidated;
|
||||
Path trashDir;
|
||||
bool shouldDelete;
|
||||
GCState(GCResults & results_) : results(results_), bytesInvalidated(0) { }
|
||||
};
|
||||
|
||||
|
||||
static bool shouldDelete(GCOptions::GCAction action)
|
||||
{
|
||||
return action == GCOptions::gcDeleteDead
|
||||
|| action == GCOptions::gcDeleteSpecific;
|
||||
}
|
||||
|
||||
|
||||
bool LocalStore::isActiveTempFile(const GCState & state,
|
||||
const Path & path, const string & suffix)
|
||||
{
|
||||
@@ -424,158 +416,207 @@ bool LocalStore::isActiveTempFile(const GCState & state,
|
||||
|
||||
void LocalStore::deleteGarbage(GCState & state, const Path & path)
|
||||
{
|
||||
printMsg(lvlInfo, format("deleting `%1%'") % path);
|
||||
unsigned long long bytesFreed, blocksFreed;
|
||||
deletePathWrapped(path, bytesFreed, blocksFreed);
|
||||
unsigned long long bytesFreed;
|
||||
deletePathWrapped(path, bytesFreed);
|
||||
state.results.bytesFreed += bytesFreed;
|
||||
state.results.blocksFreed += blocksFreed;
|
||||
}
|
||||
|
||||
|
||||
bool LocalStore::tryToDelete(GCState & state, const Path & path)
|
||||
void LocalStore::deletePathRecursive(GCState & state, const Path & path)
|
||||
{
|
||||
checkInterrupt();
|
||||
|
||||
struct stat st;
|
||||
if (lstat(path.c_str(), &st)) {
|
||||
if (errno == ENOENT) return true;
|
||||
throw SysError(format("getting status of %1%") % path);
|
||||
}
|
||||
|
||||
if (state.deleted.find(path) != state.deleted.end()) return true;
|
||||
if (state.live.find(path) != state.live.end()) return false;
|
||||
|
||||
startNest(nest, lvlDebug, format("considering whether to delete `%1%'") % path);
|
||||
|
||||
if (state.roots.find(path) != state.roots.end()) {
|
||||
printMsg(lvlDebug, format("cannot delete `%1%' because it's a root") % path);
|
||||
goto isLive;
|
||||
}
|
||||
unsigned long long size = 0;
|
||||
|
||||
if (isValidPath(path)) {
|
||||
|
||||
/* Recursively try to delete the referrers of this path. If
|
||||
any referrer can't be deleted, then this path can't be
|
||||
deleted either. */
|
||||
PathSet referrers;
|
||||
queryReferrers(path, referrers);
|
||||
foreach (PathSet::iterator, i, referrers)
|
||||
if (*i != path && !tryToDelete(state, *i)) {
|
||||
printMsg(lvlDebug, format("cannot delete `%1%' because it has live referrers") % path);
|
||||
goto isLive;
|
||||
}
|
||||
|
||||
/* If gc-keep-derivations is set and this is a derivation,
|
||||
then don't delete the derivation if any of the outputs are
|
||||
live. */
|
||||
if (state.gcKeepDerivations && isDerivation(path)) {
|
||||
PathSet outputs = queryDerivationOutputs(path);
|
||||
foreach (PathSet::iterator, i, outputs)
|
||||
if (!tryToDelete(state, *i)) {
|
||||
printMsg(lvlDebug, format("cannot delete derivation `%1%' because its output `%2%' is alive") % path % *i);
|
||||
goto isLive;
|
||||
}
|
||||
}
|
||||
|
||||
/* If gc-keep-derivations and gc-keep-outputs are both set,
|
||||
it's possible that the path has already been deleted (due
|
||||
to the recursion below), so bail out. */
|
||||
if (!pathExists(path)) return true;
|
||||
|
||||
/* If gc-keep-outputs is set, then don't delete this path if
|
||||
there are derivers of this path that are not garbage. */
|
||||
if (state.gcKeepOutputs) {
|
||||
PathSet derivers = queryValidDerivers(path);
|
||||
foreach (PathSet::iterator, deriver, derivers) {
|
||||
/* Break an infinite recursion if gc-keep-derivations
|
||||
and gc-keep-outputs are both set by tentatively
|
||||
assuming that this path is garbage. This is a safe
|
||||
assumption because at this point, the only thing
|
||||
that can prevent it from being garbage is the
|
||||
deriver. Since tryToDelete() works "upwards"
|
||||
through the dependency graph, it won't encouter
|
||||
this path except in the call to tryToDelete() in
|
||||
the gc-keep-derivation branch. */
|
||||
state.deleted.insert(path);
|
||||
if (!tryToDelete(state, *deriver)) {
|
||||
state.deleted.erase(path);
|
||||
printMsg(lvlDebug, format("cannot delete `%1%' because its deriver `%2%' is alive") % path % *deriver);
|
||||
goto isLive;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (*i != path) deletePathRecursive(state, *i);
|
||||
size = queryPathInfo(path).narSize;
|
||||
invalidatePathChecked(path);
|
||||
}
|
||||
|
||||
else {
|
||||
struct stat st;
|
||||
if (lstat(path.c_str(), &st)) {
|
||||
if (errno == ENOENT) return;
|
||||
throw SysError(format("getting status of %1%") % path);
|
||||
}
|
||||
|
||||
printMsg(lvlInfo, format("deleting `%1%'") % path);
|
||||
|
||||
state.results.paths.insert(path);
|
||||
|
||||
/* If the path is not a regular file or symlink, move it to the
|
||||
trash directory. The move is to ensure that later (when we're
|
||||
not holding the global GC lock) we can delete the path without
|
||||
being afraid that the path has become alive again. Otherwise
|
||||
delete it right away. */
|
||||
if (S_ISDIR(st.st_mode)) {
|
||||
// Estimate the amount freed using the narSize field. FIXME:
|
||||
// if the path was not valid, need to determine the actual
|
||||
// size.
|
||||
state.bytesInvalidated += size;
|
||||
// Mac OS X cannot rename directories if they are read-only.
|
||||
if (chmod(path.c_str(), st.st_mode | S_IWUSR) == -1)
|
||||
throw SysError(format("making `%1%' writable") % path);
|
||||
Path tmp = state.trashDir + "/" + baseNameOf(path);
|
||||
if (rename(path.c_str(), tmp.c_str()))
|
||||
throw SysError(format("unable to rename `%1%' to `%2%'") % path % tmp);
|
||||
} else
|
||||
deleteGarbage(state, path);
|
||||
|
||||
if (state.results.bytesFreed + state.bytesInvalidated > state.options.maxFreed) {
|
||||
printMsg(lvlInfo, format("deleted or invalidated more than %1% bytes; stopping") % state.options.maxFreed);
|
||||
throw GCLimitReached();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
bool LocalStore::canReachRoot(GCState & state, PathSet & visited, const Path & path)
|
||||
{
|
||||
if (visited.find(path) != visited.end()) return false;
|
||||
|
||||
if (state.alive.find(path) != state.alive.end()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (state.dead.find(path) != state.dead.end()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (state.roots.find(path) != state.roots.end()) {
|
||||
printMsg(lvlDebug, format("cannot delete `%1%' because it's a root") % path);
|
||||
state.alive.insert(path);
|
||||
return true;
|
||||
}
|
||||
|
||||
visited.insert(path);
|
||||
|
||||
if (!isValidPath(path)) return false;
|
||||
|
||||
PathSet incoming;
|
||||
|
||||
/* Don't delete this path if any of its referrers are alive. */
|
||||
queryReferrers(path, incoming);
|
||||
|
||||
/* If gc-keep-derivations is set and this is a derivation, then
|
||||
don't delete the derivation if any of the outputs are alive. */
|
||||
if (state.gcKeepDerivations && isDerivation(path)) {
|
||||
PathSet outputs = queryDerivationOutputs(path);
|
||||
foreach (PathSet::iterator, i, outputs)
|
||||
if (isValidPath(*i) && queryDeriver(*i) == path)
|
||||
incoming.insert(*i);
|
||||
}
|
||||
|
||||
/* If gc-keep-outputs is set, then don't delete this path if there
|
||||
are derivers of this path that are not garbage. */
|
||||
if (state.gcKeepOutputs) {
|
||||
PathSet derivers = queryValidDerivers(path);
|
||||
foreach (PathSet::iterator, i, derivers)
|
||||
incoming.insert(*i);
|
||||
}
|
||||
|
||||
foreach (PathSet::iterator, i, incoming)
|
||||
if (*i != path)
|
||||
if (canReachRoot(state, visited, *i)) {
|
||||
state.alive.insert(path);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
void LocalStore::tryToDelete(GCState & state, const Path & path)
|
||||
{
|
||||
checkInterrupt();
|
||||
|
||||
if (path == linksDir || path == state.trashDir) return;
|
||||
|
||||
startNest(nest, lvlDebug, format("considering whether to delete `%1%'") % path);
|
||||
|
||||
if (!isValidPath(path)) {
|
||||
/* A lock file belonging to a path that we're building right
|
||||
now isn't garbage. */
|
||||
if (isActiveTempFile(state, path, ".lock")) return false;
|
||||
if (isActiveTempFile(state, path, ".lock")) return;
|
||||
|
||||
/* Don't delete .chroot directories for derivations that are
|
||||
currently being built. */
|
||||
if (isActiveTempFile(state, path, ".chroot")) return false;
|
||||
|
||||
if (isActiveTempFile(state, path, ".chroot")) return;
|
||||
}
|
||||
|
||||
/* The path is garbage, so delete it. */
|
||||
if (shouldDelete(state.options.action)) {
|
||||
PathSet visited;
|
||||
|
||||
/* If it's a valid path that's not a regular file or symlink,
|
||||
invalidate it, rename it, and schedule it for deletion.
|
||||
The renaming is to ensure that later (when we're not
|
||||
holding the global GC lock) we can delete the path without
|
||||
being afraid that the path has become alive again.
|
||||
Otherwise delete it right away. */
|
||||
if (isValidPath(path)) {
|
||||
if (S_ISDIR(st.st_mode)) {
|
||||
printMsg(lvlInfo, format("invalidating `%1%'") % path);
|
||||
// Estimate the amount freed using the narSize field.
|
||||
state.bytesInvalidated += queryPathInfo(path).narSize;
|
||||
invalidatePathChecked(path);
|
||||
makeMutable(path.c_str());
|
||||
// Mac OS X cannot rename directories if they are read-only.
|
||||
if (chmod(path.c_str(), st.st_mode | S_IWUSR) == -1)
|
||||
throw SysError(format("making `%1%' writable") % path);
|
||||
Path tmp = (format("%1%-gc-%2%") % path % getpid()).str();
|
||||
if (rename(path.c_str(), tmp.c_str()))
|
||||
throw SysError(format("unable to rename `%1%' to `%2%'") % path % tmp);
|
||||
state.invalidated.insert(tmp);
|
||||
} else {
|
||||
invalidatePathChecked(path);
|
||||
deleteGarbage(state, path);
|
||||
}
|
||||
} else
|
||||
deleteGarbage(state, path);
|
||||
if (canReachRoot(state, visited, path)) {
|
||||
printMsg(lvlDebug, format("cannot delete `%1%' because it's still reachable") % path);
|
||||
} else {
|
||||
/* No path we visited was a root, so everything is garbage.
|
||||
But we only delete ‘path’ and its referrers here so that
|
||||
‘nix-store --delete’ doesn't have the unexpected effect of
|
||||
recursing into derivations and outputs. */
|
||||
state.dead.insert(visited.begin(), visited.end());
|
||||
if (state.shouldDelete)
|
||||
deletePathRecursive(state, path);
|
||||
}
|
||||
}
|
||||
|
||||
if (state.options.maxFreed && state.results.bytesFreed + state.bytesInvalidated > state.options.maxFreed) {
|
||||
printMsg(lvlInfo, format("deleted or invalidated more than %1% bytes; stopping") % state.options.maxFreed);
|
||||
throw GCLimitReached();
|
||||
|
||||
/* Unlink all files in /nix/store/.links that have a link count of 1,
|
||||
which indicates that there are no other links and so they can be
|
||||
safely deleted. FIXME: race condition with optimisePath(): we
|
||||
might see a link count of 1 just before optimisePath() increases
|
||||
the link count. */
|
||||
void LocalStore::removeUnusedLinks(const GCState & state)
|
||||
{
|
||||
AutoCloseDir dir = opendir(linksDir.c_str());
|
||||
if (!dir) throw SysError(format("opening directory `%1%'") % linksDir);
|
||||
|
||||
long long actualSize = 0, unsharedSize = 0;
|
||||
|
||||
struct dirent * dirent;
|
||||
while (errno = 0, dirent = readdir(dir)) {
|
||||
checkInterrupt();
|
||||
string name = dirent->d_name;
|
||||
if (name == "." || name == "..") continue;
|
||||
Path path = linksDir + "/" + name;
|
||||
|
||||
struct stat st;
|
||||
if (lstat(path.c_str(), &st) == -1)
|
||||
throw SysError(format("statting `%1%'") % path);
|
||||
|
||||
if (st.st_nlink != 1) {
|
||||
unsigned long long size = st.st_blocks * 512ULL;
|
||||
actualSize += size;
|
||||
unsharedSize += (st.st_nlink - 1) * size;
|
||||
continue;
|
||||
}
|
||||
|
||||
} else
|
||||
printMsg(lvlTalkative, format("would delete `%1%'") % path);
|
||||
|
||||
state.deleted.insert(path);
|
||||
if (state.options.action != GCOptions::gcReturnLive)
|
||||
state.results.paths.insert(path);
|
||||
return true;
|
||||
printMsg(lvlTalkative, format("deleting unused link `%1%'") % path);
|
||||
|
||||
isLive:
|
||||
state.live.insert(path);
|
||||
if (state.options.action == GCOptions::gcReturnLive)
|
||||
state.results.paths.insert(path);
|
||||
return false;
|
||||
if (unlink(path.c_str()) == -1)
|
||||
throw SysError(format("deleting `%1%'") % path);
|
||||
|
||||
state.results.bytesFreed += st.st_blocks * 512;
|
||||
}
|
||||
|
||||
struct stat st;
|
||||
if (stat(linksDir.c_str(), &st) == -1)
|
||||
throw SysError(format("statting `%1%'") % linksDir);
|
||||
long long overhead = st.st_blocks * 512ULL;
|
||||
|
||||
printMsg(lvlInfo, format("note: currently hard linking saves %.2f MiB")
|
||||
% ((unsharedSize - actualSize - overhead) / (1024.0 * 1024.0)));
|
||||
}
|
||||
|
||||
|
||||
void LocalStore::collectGarbage(const GCOptions & options, GCResults & results)
|
||||
{
|
||||
GCState state(results);
|
||||
state.options = options;
|
||||
|
||||
state.gcKeepOutputs = queryBoolSetting("gc-keep-outputs", false);
|
||||
state.gcKeepDerivations = queryBoolSetting("gc-keep-derivations", true);
|
||||
state.options = options;
|
||||
state.trashDir = settings.nixStore + "/trash";
|
||||
state.gcKeepOutputs = settings.gcKeepOutputs;
|
||||
state.gcKeepDerivations = settings.gcKeepDerivations;
|
||||
|
||||
/* Using `--ignore-liveness' with `--delete' can have unintended
|
||||
consequences if `gc-keep-outputs' or `gc-keep-derivations' are
|
||||
@@ -585,7 +626,9 @@ void LocalStore::collectGarbage(const GCOptions & options, GCResults & results)
|
||||
state.gcKeepOutputs = false;
|
||||
state.gcKeepDerivations = false;
|
||||
}
|
||||
|
||||
|
||||
state.shouldDelete = options.action == GCOptions::gcDeleteDead || options.action == GCOptions::gcDeleteSpecific;
|
||||
|
||||
/* Acquire the global GC root. This prevents
|
||||
a) New roots from being added.
|
||||
b) Processes from creating new temporary root files. */
|
||||
@@ -616,6 +659,11 @@ void LocalStore::collectGarbage(const GCOptions & options, GCResults & results)
|
||||
increase, since we hold locks on everything. So everything
|
||||
that is not reachable from `roots'. */
|
||||
|
||||
if (state.shouldDelete) {
|
||||
if (pathExists(state.trashDir)) deleteGarbage(state, state.trashDir);
|
||||
createDirs(state.trashDir);
|
||||
}
|
||||
|
||||
/* Now either delete all garbage paths, or just the specified
|
||||
paths (for gcDeleteSpecific). */
|
||||
|
||||
@@ -623,21 +671,22 @@ void LocalStore::collectGarbage(const GCOptions & options, GCResults & results)
|
||||
|
||||
foreach (PathSet::iterator, i, options.pathsToDelete) {
|
||||
assertStorePath(*i);
|
||||
if (!tryToDelete(state, *i))
|
||||
tryToDelete(state, *i);
|
||||
if (state.dead.find(*i) == state.dead.end())
|
||||
throw Error(format("cannot delete path `%1%' since it is still alive") % *i);
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
if (shouldDelete(state.options.action))
|
||||
|
||||
} else if (options.maxFreed > 0) {
|
||||
|
||||
if (state.shouldDelete)
|
||||
printMsg(lvlError, format("deleting garbage..."));
|
||||
else
|
||||
printMsg(lvlError, format("determining live/dead paths..."));
|
||||
|
||||
|
||||
try {
|
||||
|
||||
AutoCloseDir dir = opendir(nixStore.c_str());
|
||||
if (!dir) throw SysError(format("opening directory `%1%'") % nixStore);
|
||||
AutoCloseDir dir = opendir(settings.nixStore.c_str());
|
||||
if (!dir) throw SysError(format("opening directory `%1%'") % settings.nixStore);
|
||||
|
||||
/* Read the store and immediately delete all paths that
|
||||
aren't valid. When using --max-freed etc., deleting
|
||||
@@ -651,14 +700,14 @@ void LocalStore::collectGarbage(const GCOptions & options, GCResults & results)
|
||||
checkInterrupt();
|
||||
string name = dirent->d_name;
|
||||
if (name == "." || name == "..") continue;
|
||||
Path path = nixStore + "/" + name;
|
||||
Path path = settings.nixStore + "/" + name;
|
||||
if (isValidPath(path))
|
||||
entries.push_back(path);
|
||||
else
|
||||
tryToDelete(state, path);
|
||||
}
|
||||
|
||||
dir.close();
|
||||
dir.close();
|
||||
|
||||
/* Now delete the unreachable valid paths. Randomise the
|
||||
order in which we delete entries to make the collector
|
||||
@@ -675,13 +724,31 @@ void LocalStore::collectGarbage(const GCOptions & options, GCResults & results)
|
||||
}
|
||||
}
|
||||
|
||||
if (state.options.action == GCOptions::gcReturnLive) {
|
||||
state.results.paths = state.alive;
|
||||
return;
|
||||
}
|
||||
|
||||
if (state.options.action == GCOptions::gcReturnDead) {
|
||||
state.results.paths = state.dead;
|
||||
return;
|
||||
}
|
||||
|
||||
/* Allow other processes to add to the store from here on. */
|
||||
fdGCLock.close();
|
||||
|
||||
/* Delete the invalidated paths now that the lock has been
|
||||
released. */
|
||||
foreach (PathSet::iterator, i, state.invalidated)
|
||||
deleteGarbage(state, *i);
|
||||
/* Delete the trash directory. */
|
||||
printMsg(lvlInfo, format("deleting `%1%'") % state.trashDir);
|
||||
deleteGarbage(state, state.trashDir);
|
||||
|
||||
/* Clean up the links directory. */
|
||||
if (options.action == GCOptions::gcDeleteDead || options.action == GCOptions::gcDeleteSpecific) {
|
||||
printMsg(lvlError, format("deleting unused links..."));
|
||||
removeUnusedLinks(state);
|
||||
}
|
||||
|
||||
/* While we're at it, vacuum the database. */
|
||||
if (options.action == GCOptions::gcDeleteDead) vacuumDB();
|
||||
}
|
||||
|
||||
|
||||
|
||||
+136
-96
@@ -10,47 +10,76 @@
|
||||
namespace nix {
|
||||
|
||||
|
||||
string nixStore = "/UNINIT";
|
||||
string nixDataDir = "/UNINIT";
|
||||
string nixLogDir = "/UNINIT";
|
||||
string nixStateDir = "/UNINIT";
|
||||
string nixDBPath = "/UNINIT";
|
||||
string nixConfDir = "/UNINIT";
|
||||
string nixLibexecDir = "/UNINIT";
|
||||
string nixBinDir = "/UNINIT";
|
||||
|
||||
bool keepFailed = false;
|
||||
bool keepGoing = false;
|
||||
bool tryFallback = false;
|
||||
Verbosity buildVerbosity = lvlError;
|
||||
unsigned int maxBuildJobs = 1;
|
||||
unsigned int buildCores = 1;
|
||||
bool readOnlyMode = false;
|
||||
string thisSystem = "unset";
|
||||
time_t maxSilentTime = 0;
|
||||
time_t buildTimeout = 0;
|
||||
Paths substituters;
|
||||
bool useBuildHook = true;
|
||||
bool printBuildTrace = false;
|
||||
/* The default location of the daemon socket, relative to nixStateDir.
|
||||
The socket is in a directory to allow you to control access to the
|
||||
Nix daemon by setting the mode/ownership of the directory
|
||||
appropriately. (This wouldn't work on the socket itself since it
|
||||
must be deleted and recreated on startup.) */
|
||||
#define DEFAULT_SOCKET_PATH "/daemon-socket/socket"
|
||||
|
||||
|
||||
static bool settingsRead = false;
|
||||
|
||||
static std::map<string, Strings> settings;
|
||||
|
||||
/* Overriden settings. */
|
||||
std::map<string, Strings> settingsCmdline;
|
||||
Settings settings;
|
||||
|
||||
|
||||
string & at(Strings & ss, unsigned int n)
|
||||
Settings::Settings()
|
||||
{
|
||||
Strings::iterator i = ss.begin();
|
||||
advance(i, n);
|
||||
return *i;
|
||||
keepFailed = false;
|
||||
keepGoing = false;
|
||||
tryFallback = false;
|
||||
buildVerbosity = lvlError;
|
||||
maxBuildJobs = 1;
|
||||
buildCores = 1;
|
||||
readOnlyMode = false;
|
||||
thisSystem = SYSTEM;
|
||||
maxSilentTime = 0;
|
||||
buildTimeout = 0;
|
||||
useBuildHook = true;
|
||||
printBuildTrace = false;
|
||||
reservedSize = 1024 * 1024;
|
||||
fsyncMetadata = true;
|
||||
useSQLiteWAL = true;
|
||||
syncBeforeRegistering = false;
|
||||
useSubstitutes = true;
|
||||
useChroot = false;
|
||||
dirsInChroot.insert("/dev");
|
||||
dirsInChroot.insert("/dev/pts");
|
||||
impersonateLinux26 = false;
|
||||
keepLog = true;
|
||||
compressLog = true;
|
||||
cacheFailure = false;
|
||||
pollInterval = 5;
|
||||
checkRootReachability = false;
|
||||
gcKeepOutputs = false;
|
||||
gcKeepDerivations = true;
|
||||
autoOptimiseStore = false;
|
||||
envKeepDerivations = false;
|
||||
}
|
||||
|
||||
|
||||
static void readSettings()
|
||||
void Settings::processEnvironment()
|
||||
{
|
||||
nixStore = canonPath(getEnv("NIX_STORE_DIR", getEnv("NIX_STORE", NIX_STORE_DIR)));
|
||||
nixDataDir = canonPath(getEnv("NIX_DATA_DIR", NIX_DATA_DIR));
|
||||
nixLogDir = canonPath(getEnv("NIX_LOG_DIR", NIX_LOG_DIR));
|
||||
nixStateDir = canonPath(getEnv("NIX_STATE_DIR", NIX_STATE_DIR));
|
||||
nixDBPath = getEnv("NIX_DB_DIR", nixStateDir + "/db");
|
||||
nixConfDir = canonPath(getEnv("NIX_CONF_DIR", NIX_CONF_DIR));
|
||||
nixLibexecDir = canonPath(getEnv("NIX_LIBEXEC_DIR", NIX_LIBEXEC_DIR));
|
||||
nixBinDir = canonPath(getEnv("NIX_BIN_DIR", NIX_BIN_DIR));
|
||||
nixDaemonSocketFile = canonPath(nixStateDir + DEFAULT_SOCKET_PATH);
|
||||
|
||||
string subs = getEnv("NIX_SUBSTITUTERS", "default");
|
||||
if (subs == "default") {
|
||||
if (getEnv("NIX_OTHER_STORES") != "")
|
||||
substituters.push_back(nixLibexecDir + "/nix/substituters/copy-from-other-stores.pl");
|
||||
substituters.push_back(nixLibexecDir + "/nix/substituters/download-using-manifests.pl");
|
||||
substituters.push_back(nixLibexecDir + "/nix/substituters/download-from-binary-cache.pl");
|
||||
} else
|
||||
substituters = tokenizeString<Strings>(subs, ":");
|
||||
}
|
||||
|
||||
|
||||
void Settings::loadConfFile()
|
||||
{
|
||||
Path settingsFile = (format("%1%/%2%") % nixConfDir % "nix.conf").str();
|
||||
if (!pathExists(settingsFile)) return;
|
||||
@@ -68,105 +97,116 @@ static void readSettings()
|
||||
if (hash != string::npos)
|
||||
line = string(line, 0, hash);
|
||||
|
||||
Strings tokens = tokenizeString(line);
|
||||
vector<string> tokens = tokenizeString<vector<string> >(line);
|
||||
if (tokens.empty()) continue;
|
||||
|
||||
if (tokens.size() < 2 || at(tokens, 1) != "=")
|
||||
if (tokens.size() < 2 || tokens[1] != "=")
|
||||
throw Error(format("illegal configuration line `%1%' in `%2%'") % line % settingsFile);
|
||||
|
||||
string name = at(tokens, 0);
|
||||
string name = tokens[0];
|
||||
|
||||
Strings::iterator i = tokens.begin();
|
||||
vector<string>::iterator i = tokens.begin();
|
||||
advance(i, 2);
|
||||
settings[name] = Strings(i, tokens.end());
|
||||
settings[name] = concatStringsSep(" ", Strings(i, tokens.end())); // FIXME: slow
|
||||
};
|
||||
|
||||
settings.insert(settingsCmdline.begin(), settingsCmdline.end());
|
||||
|
||||
settingsRead = true;
|
||||
}
|
||||
|
||||
|
||||
Strings querySetting(const string & name, const Strings & def)
|
||||
void Settings::set(const string & name, const string & value)
|
||||
{
|
||||
if (!settingsRead) readSettings();
|
||||
std::map<string, Strings>::iterator i = settings.find(name);
|
||||
return i == settings.end() ? def : i->second;
|
||||
settings[name] = value;
|
||||
overrides[name] = value;
|
||||
}
|
||||
|
||||
|
||||
string querySetting(const string & name, const string & def)
|
||||
void Settings::update()
|
||||
{
|
||||
Strings defs;
|
||||
defs.push_back(def);
|
||||
|
||||
Strings value = querySetting(name, defs);
|
||||
if (value.size() != 1)
|
||||
throw Error(format("configuration option `%1%' should not be a list") % name);
|
||||
|
||||
return value.front();
|
||||
get(tryFallback, "build-fallback");
|
||||
get(maxBuildJobs, "build-max-jobs");
|
||||
get(buildCores, "build-cores");
|
||||
get(thisSystem, "system");
|
||||
get(maxSilentTime, "build-max-silent-time");
|
||||
get(buildTimeout, "build-timeout");
|
||||
get(reservedSize, "gc-reserved-space");
|
||||
get(fsyncMetadata, "fsync-metadata");
|
||||
get(useSQLiteWAL, "use-sqlite-wal");
|
||||
get(syncBeforeRegistering, "sync-before-registering");
|
||||
get(useSubstitutes, "build-use-substitutes");
|
||||
get(buildUsersGroup, "build-users-group");
|
||||
get(useChroot, "build-use-chroot");
|
||||
get(dirsInChroot, "build-chroot-dirs");
|
||||
get(impersonateLinux26, "build-impersonate-linux-26");
|
||||
get(keepLog, "build-keep-log");
|
||||
get(compressLog, "build-compress-log");
|
||||
get(cacheFailure, "build-cache-failure");
|
||||
get(pollInterval, "build-poll-interval");
|
||||
get(checkRootReachability, "gc-check-reachability");
|
||||
get(gcKeepOutputs, "gc-keep-outputs");
|
||||
get(gcKeepDerivations, "gc-keep-derivations");
|
||||
get(autoOptimiseStore, "auto-optimise-store");
|
||||
get(envKeepDerivations, "env-keep-derivations");
|
||||
}
|
||||
|
||||
|
||||
bool queryBoolSetting(const string & name, bool def)
|
||||
void Settings::get(string & res, const string & name)
|
||||
{
|
||||
string v = querySetting(name, def ? "true" : "false");
|
||||
if (v == "true") return true;
|
||||
else if (v == "false") return false;
|
||||
SettingsMap::iterator i = settings.find(name);
|
||||
if (i == settings.end()) return;
|
||||
res = i->second;
|
||||
}
|
||||
|
||||
|
||||
void Settings::get(bool & res, const string & name)
|
||||
{
|
||||
SettingsMap::iterator i = settings.find(name);
|
||||
if (i == settings.end()) return;
|
||||
if (i->second == "true") res = true;
|
||||
else if (i->second == "false") res = false;
|
||||
else throw Error(format("configuration option `%1%' should be either `true' or `false', not `%2%'")
|
||||
% name % v);
|
||||
% name % i->second);
|
||||
}
|
||||
|
||||
|
||||
unsigned int queryIntSetting(const string & name, unsigned int def)
|
||||
void Settings::get(StringSet & res, const string & name)
|
||||
{
|
||||
int n;
|
||||
if (!string2Int(querySetting(name, int2String(def)), n) || n < 0)
|
||||
SettingsMap::iterator i = settings.find(name);
|
||||
if (i == settings.end()) return;
|
||||
res.clear();
|
||||
Strings ss = tokenizeString<Strings>(i->second);
|
||||
res.insert(ss.begin(), ss.end());
|
||||
}
|
||||
|
||||
|
||||
template<class N> void Settings::get(N & res, const string & name)
|
||||
{
|
||||
SettingsMap::iterator i = settings.find(name);
|
||||
if (i == settings.end()) return;
|
||||
if (!string2Int(i->second, res))
|
||||
throw Error(format("configuration setting `%1%' should have an integer value") % name);
|
||||
return n;
|
||||
}
|
||||
|
||||
|
||||
void overrideSetting(const string & name, const Strings & value)
|
||||
string Settings::pack()
|
||||
{
|
||||
if (settingsRead) settings[name] = value;
|
||||
settingsCmdline[name] = value;
|
||||
string s;
|
||||
foreach (SettingsMap::iterator, i, settings) {
|
||||
if (i->first.find('\n') != string::npos ||
|
||||
i->first.find('=') != string::npos ||
|
||||
i->second.find('\n') != string::npos)
|
||||
throw Error("illegal option name/value");
|
||||
s += i->first; s += '='; s += i->second; s += '\n';
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
|
||||
void reloadSettings()
|
||||
Settings::SettingsMap Settings::getOverrides()
|
||||
{
|
||||
settingsRead = false;
|
||||
settings.clear();
|
||||
return overrides;
|
||||
}
|
||||
|
||||
|
||||
void setDefaultsFromEnvironment()
|
||||
{
|
||||
/* Setup Nix paths. */
|
||||
nixStore = canonPath(getEnv("NIX_STORE_DIR", getEnv("NIX_STORE", NIX_STORE_DIR)));
|
||||
nixDataDir = canonPath(getEnv("NIX_DATA_DIR", NIX_DATA_DIR));
|
||||
nixLogDir = canonPath(getEnv("NIX_LOG_DIR", NIX_LOG_DIR));
|
||||
nixStateDir = canonPath(getEnv("NIX_STATE_DIR", NIX_STATE_DIR));
|
||||
nixDBPath = getEnv("NIX_DB_DIR", nixStateDir + "/db");
|
||||
nixConfDir = canonPath(getEnv("NIX_CONF_DIR", NIX_CONF_DIR));
|
||||
nixLibexecDir = canonPath(getEnv("NIX_LIBEXEC_DIR", NIX_LIBEXEC_DIR));
|
||||
nixBinDir = canonPath(getEnv("NIX_BIN_DIR", NIX_BIN_DIR));
|
||||
const string nixVersion = NIX_VERSION;
|
||||
|
||||
string subs = getEnv("NIX_SUBSTITUTERS", "default");
|
||||
if (subs == "default") {
|
||||
substituters.push_back(nixLibexecDir + "/nix/substituters/copy-from-other-stores.pl");
|
||||
substituters.push_back(nixLibexecDir + "/nix/substituters/download-using-manifests.pl");
|
||||
} else
|
||||
substituters = tokenizeString(subs, ":");
|
||||
|
||||
/* Get some settings from the configuration file. */
|
||||
thisSystem = querySetting("system", SYSTEM);
|
||||
maxBuildJobs = queryIntSetting("build-max-jobs", 1);
|
||||
buildCores = queryIntSetting("build-cores", 1);
|
||||
maxSilentTime = queryIntSetting("build-max-silent-time", 0);
|
||||
buildTimeout = queryIntSetting("build-timeout", 0);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
+176
-96
@@ -2,118 +2,198 @@
|
||||
|
||||
#include "types.hh"
|
||||
|
||||
#include <map>
|
||||
#include <sys/types.h>
|
||||
|
||||
|
||||
namespace nix {
|
||||
|
||||
|
||||
/* Path names. */
|
||||
struct Settings {
|
||||
|
||||
/* nixStore is the directory where we generally store atomic and
|
||||
derived files. */
|
||||
extern string nixStore;
|
||||
typedef std::map<string, string> SettingsMap;
|
||||
|
||||
extern string nixDataDir; /* !!! fix */
|
||||
Settings();
|
||||
|
||||
/* nixLogDir is the directory where we log various operations. */
|
||||
extern string nixLogDir;
|
||||
void processEnvironment();
|
||||
|
||||
/* nixStateDir is the directory where state is stored. */
|
||||
extern string nixStateDir;
|
||||
void loadConfFile();
|
||||
|
||||
/* nixDBPath is the path name of our Berkeley DB environment. */
|
||||
extern string nixDBPath;
|
||||
void set(const string & name, const string & value);
|
||||
|
||||
/* nixConfDir is the directory where configuration files are
|
||||
stored. */
|
||||
extern string nixConfDir;
|
||||
void update();
|
||||
|
||||
/* nixLibexecDir is the directory where internal helper programs are
|
||||
stored. */
|
||||
extern string nixLibexecDir;
|
||||
string pack();
|
||||
|
||||
/* nixBinDir is the directory where the main programs are stored. */
|
||||
extern string nixBinDir;
|
||||
SettingsMap getOverrides();
|
||||
|
||||
/* The directory where we store sources and derived files. */
|
||||
Path nixStore;
|
||||
|
||||
Path nixDataDir; /* !!! fix */
|
||||
|
||||
/* The directory where we log various operations. */
|
||||
Path nixLogDir;
|
||||
|
||||
/* The directory where state is stored. */
|
||||
Path nixStateDir;
|
||||
|
||||
/* The directory where we keep the SQLite database. */
|
||||
Path nixDBPath;
|
||||
|
||||
/* The directory where configuration files are stored. */
|
||||
Path nixConfDir;
|
||||
|
||||
/* The directory where internal helper programs are stored. */
|
||||
Path nixLibexecDir;
|
||||
|
||||
/* The directory where the main programs are stored. */
|
||||
Path nixBinDir;
|
||||
|
||||
/* File name of the socket the daemon listens to. */
|
||||
Path nixDaemonSocketFile;
|
||||
|
||||
/* Whether to keep temporary directories of failed builds. */
|
||||
bool keepFailed;
|
||||
|
||||
/* Whether to keep building subgoals when a sibling (another
|
||||
subgoal of the same goal) fails. */
|
||||
bool keepGoing;
|
||||
|
||||
/* Whether, if we cannot realise the known closure corresponding
|
||||
to a derivation, we should try to normalise the derivation
|
||||
instead. */
|
||||
bool tryFallback;
|
||||
|
||||
/* Verbosity level for build output. */
|
||||
Verbosity buildVerbosity;
|
||||
|
||||
/* Maximum number of parallel build jobs. 0 means unlimited. */
|
||||
unsigned int maxBuildJobs;
|
||||
|
||||
/* Number of CPU cores to utilize in parallel within a build,
|
||||
i.e. by passing this number to Make via '-j'. 0 means that the
|
||||
number of actual CPU cores on the local host ought to be
|
||||
auto-detected. */
|
||||
unsigned int buildCores;
|
||||
|
||||
/* Read-only mode. Don't copy stuff to the store, don't change
|
||||
the database. */
|
||||
bool readOnlyMode;
|
||||
|
||||
/* The canonical system name, as returned by config.guess. */
|
||||
string thisSystem;
|
||||
|
||||
/* The maximum time in seconds that a builer can go without
|
||||
producing any output on stdout/stderr before it is killed. 0
|
||||
means infinity. */
|
||||
time_t maxSilentTime;
|
||||
|
||||
/* The maximum duration in seconds that a builder can run. 0
|
||||
means infinity. */
|
||||
time_t buildTimeout;
|
||||
|
||||
/* The substituters. There are programs that can somehow realise
|
||||
a store path without building, e.g., by downloading it or
|
||||
copying it from a CD. */
|
||||
Paths substituters;
|
||||
|
||||
/* Whether to use build hooks (for distributed builds). Sometimes
|
||||
users want to disable this from the command-line. */
|
||||
bool useBuildHook;
|
||||
|
||||
/* Whether buildDerivations() should print out lines on stderr in
|
||||
a fixed format to allow its progress to be monitored. Each
|
||||
line starts with a "@". The following are defined:
|
||||
|
||||
@ build-started <drvpath> <outpath> <system> <logfile>
|
||||
@ build-failed <drvpath> <outpath> <exitcode> <error text>
|
||||
@ build-succeeded <drvpath> <outpath>
|
||||
@ substituter-started <outpath> <substituter>
|
||||
@ substituter-failed <outpath> <exitcode> <error text>
|
||||
@ substituter-succeeded <outpath>
|
||||
|
||||
Best combined with --no-build-output, otherwise stderr might
|
||||
conceivably contain lines in this format printed by the
|
||||
builders. */
|
||||
bool printBuildTrace;
|
||||
|
||||
/* Amount of reserved space for the garbage collector
|
||||
(/nix/var/nix/db/reserved). */
|
||||
off_t reservedSize;
|
||||
|
||||
/* Whether SQLite should use fsync. */
|
||||
bool fsyncMetadata;
|
||||
|
||||
/* Whether SQLite should use WAL mode. */
|
||||
bool useSQLiteWAL;
|
||||
|
||||
/* Whether to call sync() before registering a path as valid. */
|
||||
bool syncBeforeRegistering;
|
||||
|
||||
/* Whether to use substitutes. */
|
||||
bool useSubstitutes;
|
||||
|
||||
/* The Unix group that contains the build users. */
|
||||
string buildUsersGroup;
|
||||
|
||||
/* Whether to build in chroot. */
|
||||
bool useChroot;
|
||||
|
||||
/* The directories from the host filesystem to be included in the
|
||||
chroot. */
|
||||
StringSet dirsInChroot;
|
||||
|
||||
/* Whether to impersonate a Linux 2.6 machine on newer kernels. */
|
||||
bool impersonateLinux26;
|
||||
|
||||
/* Whether to store build logs. */
|
||||
bool keepLog;
|
||||
|
||||
/* Whether to compress logs. */
|
||||
bool compressLog;
|
||||
|
||||
/* Whether to cache build failures. */
|
||||
bool cacheFailure;
|
||||
|
||||
/* How often (in seconds) to poll for locks. */
|
||||
unsigned int pollInterval;
|
||||
|
||||
/* Whether to check if new GC roots can in fact be found by the
|
||||
garbage collector. */
|
||||
bool checkRootReachability;
|
||||
|
||||
/* Whether the garbage collector should keep outputs of live
|
||||
derivations. */
|
||||
bool gcKeepOutputs;
|
||||
|
||||
/* Whether the garbage collector should keep derivers of live
|
||||
paths. */
|
||||
bool gcKeepDerivations;
|
||||
|
||||
/* Whether to automatically replace files with identical contents
|
||||
with hard links. */
|
||||
bool autoOptimiseStore;
|
||||
|
||||
/* Whether to add derivations as a dependency of user environments
|
||||
(to prevent them from being GCed). */
|
||||
bool envKeepDerivations;
|
||||
|
||||
private:
|
||||
SettingsMap settings, overrides;
|
||||
|
||||
void get(string & res, const string & name);
|
||||
void get(bool & res, const string & name);
|
||||
void get(StringSet & res, const string & name);
|
||||
template<class N> void get(N & res, const string & name);
|
||||
};
|
||||
|
||||
|
||||
/* Misc. global flags. */
|
||||
|
||||
/* Whether to keep temporary directories of failed builds. */
|
||||
extern bool keepFailed;
|
||||
|
||||
/* Whether to keep building subgoals when a sibling (another subgoal
|
||||
of the same goal) fails. */
|
||||
extern bool keepGoing;
|
||||
|
||||
/* Whether, if we cannot realise the known closure corresponding to a
|
||||
derivation, we should try to normalise the derivation instead. */
|
||||
extern bool tryFallback;
|
||||
|
||||
/* Verbosity level for build output. */
|
||||
extern Verbosity buildVerbosity;
|
||||
|
||||
/* Maximum number of parallel build jobs. 0 means unlimited. */
|
||||
extern unsigned int maxBuildJobs;
|
||||
|
||||
/* Number of CPU cores to utilize in parallel within a build, i.e. by passing
|
||||
this number to Make via '-j'. 0 means that the number of actual CPU cores on
|
||||
the local host ought to be auto-detected. */
|
||||
extern unsigned int buildCores;
|
||||
|
||||
/* Read-only mode. Don't copy stuff to the store, don't change the
|
||||
database. */
|
||||
extern bool readOnlyMode;
|
||||
|
||||
/* The canonical system name, as returned by config.guess. */
|
||||
extern string thisSystem;
|
||||
|
||||
/* The maximum time in seconds that a builer can go without producing
|
||||
any output on stdout/stderr before it is killed. 0 means
|
||||
infinity. */
|
||||
extern time_t maxSilentTime;
|
||||
|
||||
/* The maximum duration in seconds that a builder can run. 0 means
|
||||
infinity. */
|
||||
extern time_t buildTimeout;
|
||||
|
||||
/* The substituters. There are programs that can somehow realise a
|
||||
store path without building, e.g., by downloading it or copying it
|
||||
from a CD. */
|
||||
extern Paths substituters;
|
||||
|
||||
/* Whether to use build hooks (for distributed builds). Sometimes
|
||||
users want to disable this from the command-line. */
|
||||
extern bool useBuildHook;
|
||||
|
||||
/* Whether buildDerivations() should print out lines on stderr in a
|
||||
fixed format to allow its progress to be monitored. Each line
|
||||
starts with a "@". The following are defined:
|
||||
|
||||
@ build-started <drvpath> <outpath> <system> <logfile>
|
||||
@ build-failed <drvpath> <outpath> <exitcode> <error text>
|
||||
@ build-succeeded <drvpath> <outpath>
|
||||
@ substituter-started <outpath> <substituter>
|
||||
@ substituter-failed <outpath> <exitcode> <error text>
|
||||
@ substituter-succeeded <outpath>
|
||||
|
||||
Best combined with --no-build-output, otherwise stderr might
|
||||
conceivably contain lines in this format printed by the builders.
|
||||
*/
|
||||
extern bool printBuildTrace;
|
||||
// FIXME: don't use a global variable.
|
||||
extern Settings settings;
|
||||
|
||||
|
||||
Strings querySetting(const string & name, const Strings & def);
|
||||
|
||||
string querySetting(const string & name, const string & def);
|
||||
|
||||
bool queryBoolSetting(const string & name, bool def);
|
||||
|
||||
unsigned int queryIntSetting(const string & name, unsigned int def);
|
||||
|
||||
void overrideSetting(const string & name, const Strings & value);
|
||||
|
||||
void reloadSettings();
|
||||
|
||||
void setDefaultsFromEnvironment();
|
||||
extern const string nixVersion;
|
||||
|
||||
|
||||
}
|
||||
|
||||
+441
-193
File diff suppressed because it is too large
Load Diff
+81
-56
@@ -17,8 +17,8 @@ namespace nix {
|
||||
/* Nix store and database schema version. Version 1 (or 0) was Nix <=
|
||||
0.7. Version 2 was Nix 0.8 and 0.9. Version 3 is Nix 0.10.
|
||||
Version 4 is Nix 0.11. Version 5 is Nix 0.12-0.16. Version 6 is
|
||||
Nix 1.0. */
|
||||
const int nixSchemaVersion = 6;
|
||||
Nix 1.0. Version 7 is Nix 1.3. */
|
||||
const int nixSchemaVersion = 7;
|
||||
|
||||
|
||||
extern string drvsLogDir;
|
||||
@@ -45,7 +45,7 @@ struct OptimiseStats
|
||||
struct RunningSubstituter
|
||||
{
|
||||
Pid pid;
|
||||
AutoCloseFD to, from;
|
||||
AutoCloseFD to, from, error;
|
||||
};
|
||||
|
||||
|
||||
@@ -75,17 +75,16 @@ struct SQLiteStmt
|
||||
void bind64(long long value);
|
||||
void bind();
|
||||
};
|
||||
|
||||
|
||||
|
||||
class LocalStore : public StoreAPI
|
||||
{
|
||||
private:
|
||||
bool substitutablePathsLoaded;
|
||||
PathSet substitutablePaths;
|
||||
|
||||
typedef std::map<Path, RunningSubstituter> RunningSubstituters;
|
||||
RunningSubstituters runningSubstituters;
|
||||
|
||||
|
||||
Path linksDir;
|
||||
|
||||
public:
|
||||
|
||||
/* Initialise the local store, upgrading the schema if
|
||||
@@ -93,13 +92,15 @@ public:
|
||||
LocalStore(bool reserveSpace = true);
|
||||
|
||||
~LocalStore();
|
||||
|
||||
|
||||
/* Implementations of abstract store API methods. */
|
||||
|
||||
|
||||
bool isValidPath(const Path & path);
|
||||
|
||||
PathSet queryValidPaths();
|
||||
|
||||
PathSet queryValidPaths(const PathSet & paths);
|
||||
|
||||
PathSet queryAllValidPaths();
|
||||
|
||||
ValidPathInfo queryPathInfo(const Path & path);
|
||||
|
||||
Hash queryPathHash(const Path & path);
|
||||
@@ -110,55 +111,49 @@ public:
|
||||
|
||||
Path queryDeriver(const Path & path);
|
||||
|
||||
/* Return all currently valid derivations that have `path' as an
|
||||
output. (Note that the result of `queryDeriver()' is the
|
||||
derivation that was actually used to produce `path', which may
|
||||
not exist anymore.) */
|
||||
PathSet queryValidDerivers(const Path & path);
|
||||
|
||||
PathSet queryDerivationOutputs(const Path & path);
|
||||
|
||||
StringSet queryDerivationOutputNames(const Path & path);
|
||||
|
||||
Path queryPathFromHashPart(const string & hashPart);
|
||||
|
||||
PathSet querySubstitutablePaths();
|
||||
|
||||
bool hasSubstitutes(const Path & path);
|
||||
|
||||
bool querySubstitutablePathInfo(const Path & path,
|
||||
SubstitutablePathInfo & info);
|
||||
|
||||
bool querySubstitutablePathInfo(const Path & substituter,
|
||||
const Path & path, SubstitutablePathInfo & info);
|
||||
|
||||
Path queryPathFromHashPart(const string & hashPart);
|
||||
|
||||
PathSet querySubstitutablePaths(const PathSet & paths);
|
||||
|
||||
void querySubstitutablePathInfos(const Path & substituter,
|
||||
PathSet & paths, SubstitutablePathInfos & infos);
|
||||
|
||||
void querySubstitutablePathInfos(const PathSet & paths,
|
||||
SubstitutablePathInfos & infos);
|
||||
|
||||
Path addToStore(const Path & srcPath,
|
||||
bool recursive = true, HashType hashAlgo = htSHA256,
|
||||
PathFilter & filter = defaultPathFilter);
|
||||
PathFilter & filter = defaultPathFilter, bool repair = false);
|
||||
|
||||
/* Like addToStore(), but the contents of the path are contained
|
||||
in `dump', which is either a NAR serialisation (if recursive ==
|
||||
true) or simply the contents of a regular file (if recursive ==
|
||||
false). */
|
||||
Path addToStoreFromDump(const string & dump, const string & name,
|
||||
bool recursive = true, HashType hashAlgo = htSHA256);
|
||||
bool recursive = true, HashType hashAlgo = htSHA256, bool repair = false);
|
||||
|
||||
Path addTextToStore(const string & name, const string & s,
|
||||
const PathSet & references);
|
||||
const PathSet & references, bool repair = false);
|
||||
|
||||
void exportPath(const Path & path, bool sign,
|
||||
Sink & sink);
|
||||
|
||||
Paths importPaths(bool requireSignature, Source & source);
|
||||
|
||||
void buildPaths(const PathSet & paths);
|
||||
|
||||
void buildPaths(const PathSet & paths, bool repair = false);
|
||||
|
||||
void ensurePath(const Path & path);
|
||||
|
||||
void addTempRoot(const Path & path);
|
||||
|
||||
void addIndirectRoot(const Path & path);
|
||||
|
||||
|
||||
void syncWithGC();
|
||||
|
||||
Roots findRoots();
|
||||
@@ -167,10 +162,14 @@ public:
|
||||
|
||||
/* Optimise the disk space usage of the Nix store by hard-linking
|
||||
files with the same contents. */
|
||||
void optimiseStore(bool dryRun, OptimiseStats & stats);
|
||||
void optimiseStore(OptimiseStats & stats);
|
||||
|
||||
/* Check the integrity of the Nix store. */
|
||||
void verifyStore(bool checkContents);
|
||||
/* Optimise a single store path. */
|
||||
void optimisePath(const Path & path);
|
||||
|
||||
/* Check the integrity of the Nix store. Returns true if errors
|
||||
remain. */
|
||||
bool verifyStore(bool checkContents, bool repair);
|
||||
|
||||
/* Register the validity of a path, i.e., that `path' exists, that
|
||||
the paths referenced by it exists, and in the case of an output
|
||||
@@ -193,6 +192,20 @@ public:
|
||||
|
||||
void clearFailedPaths(const PathSet & paths);
|
||||
|
||||
void vacuumDB();
|
||||
|
||||
/* Repair the contents of the given path by redownloading it using
|
||||
a substituter (if available). */
|
||||
void repairPath(const Path & path);
|
||||
|
||||
/* Check whether the given valid path exists and has the right
|
||||
contents. */
|
||||
bool pathContentsGood(const Path & path);
|
||||
|
||||
void markContentsGood(const Path & path);
|
||||
|
||||
void setSubstituterEnv();
|
||||
|
||||
private:
|
||||
|
||||
Path schemaPath;
|
||||
@@ -220,53 +233,69 @@ private:
|
||||
SQLiteStmt stmtQueryDerivationOutputs;
|
||||
SQLiteStmt stmtQueryPathFromHashPart;
|
||||
|
||||
/* Cache for pathContentsGood(). */
|
||||
std::map<Path, bool> pathContentsGoodCache;
|
||||
|
||||
bool didSetSubstituterEnv;
|
||||
|
||||
int getSchema();
|
||||
|
||||
void openDB(bool create);
|
||||
|
||||
void makeStoreWritable();
|
||||
|
||||
unsigned long long queryValidPathId(const Path & path);
|
||||
|
||||
unsigned long long addValidPath(const ValidPathInfo & info, bool checkOutputs = true);
|
||||
|
||||
|
||||
void addReference(unsigned long long referrer, unsigned long long reference);
|
||||
|
||||
|
||||
void appendReferrer(const Path & from, const Path & to, bool lock);
|
||||
|
||||
|
||||
void rewriteReferrers(const Path & path, bool purge, PathSet referrers);
|
||||
|
||||
void invalidatePath(const Path & path);
|
||||
|
||||
/* Delete a path from the Nix store. */
|
||||
void invalidatePathChecked(const Path & path);
|
||||
|
||||
|
||||
void verifyPath(const Path & path, const PathSet & store,
|
||||
PathSet & done, PathSet & validPaths);
|
||||
PathSet & done, PathSet & validPaths, bool repair, bool & errors);
|
||||
|
||||
void updatePathInfo(const ValidPathInfo & info);
|
||||
|
||||
void upgradeStore6();
|
||||
void upgradeStore7();
|
||||
PathSet queryValidPathsOld();
|
||||
ValidPathInfo queryPathInfoOld(const Path & path);
|
||||
|
||||
struct GCState;
|
||||
|
||||
void deleteGarbage(GCState & state, const Path & path);
|
||||
|
||||
bool tryToDelete(GCState & state, const Path & path);
|
||||
|
||||
|
||||
void tryToDelete(GCState & state, const Path & path);
|
||||
|
||||
bool canReachRoot(GCState & state, PathSet & visited, const Path & path);
|
||||
|
||||
void deletePathRecursive(GCState & state, const Path & path);
|
||||
|
||||
bool isActiveTempFile(const GCState & state,
|
||||
const Path & path, const string & suffix);
|
||||
|
||||
|
||||
int openGCLock(LockType lockType);
|
||||
|
||||
|
||||
void removeUnusedLinks(const GCState & state);
|
||||
|
||||
void startSubstituter(const Path & substituter,
|
||||
RunningSubstituter & runningSubstituter);
|
||||
|
||||
Path createTempDirInStore();
|
||||
|
||||
Path importPath(bool requireSignature, Source & source);
|
||||
|
||||
|
||||
void checkDerivationOutputs(const Path & drvPath, const Derivation & drv);
|
||||
|
||||
void optimisePath_(OptimiseStats & stats, const Path & path);
|
||||
};
|
||||
|
||||
|
||||
@@ -278,15 +307,12 @@ private:
|
||||
without execute permission; setuid bits etc. are cleared)
|
||||
- the owner and group are set to the Nix user and group, if we're
|
||||
in a setuid Nix installation. */
|
||||
void canonicalisePathMetaData(const Path & path);
|
||||
void canonicalisePathMetaData(const Path & path, uid_t fromUid);
|
||||
|
||||
void canonicalisePathMetaData(const Path & path, bool recurse);
|
||||
void canonicaliseTimestampAndPermissions(const Path & path);
|
||||
|
||||
MakeError(PathInUse, Error);
|
||||
|
||||
/* Whether we are in build users mode. */
|
||||
bool haveBuildUsers();
|
||||
|
||||
/* Whether we are root. */
|
||||
bool amPrivileged();
|
||||
|
||||
@@ -295,9 +321,8 @@ void getOwnership(const Path & path);
|
||||
|
||||
/* Like deletePath(), but changes the ownership of `path' using the
|
||||
setuid wrapper if necessary (and possible). */
|
||||
void deletePathWrapped(const Path & path,
|
||||
unsigned long long & bytesFreed, unsigned long long & blocksFreed);
|
||||
void deletePathWrapped(const Path & path, unsigned long long & bytesFreed);
|
||||
|
||||
void deletePathWrapped(const Path & path);
|
||||
|
||||
|
||||
}
|
||||
|
||||
+134
-53
@@ -15,27 +15,47 @@ Derivation derivationFromPath(StoreAPI & store, const Path & drvPath)
|
||||
}
|
||||
|
||||
|
||||
void computeFSClosure(StoreAPI & store, const Path & storePath,
|
||||
PathSet & paths, bool flipDirection, bool includeOutputs)
|
||||
void computeFSClosure(StoreAPI & store, const Path & path,
|
||||
PathSet & paths, bool flipDirection, bool includeOutputs, bool includeDerivers)
|
||||
{
|
||||
if (paths.find(storePath) != paths.end()) return;
|
||||
paths.insert(storePath);
|
||||
if (paths.find(path) != paths.end()) return;
|
||||
paths.insert(path);
|
||||
|
||||
PathSet references;
|
||||
if (flipDirection)
|
||||
store.queryReferrers(storePath, references);
|
||||
else
|
||||
store.queryReferences(storePath, references);
|
||||
PathSet edges;
|
||||
|
||||
if (includeOutputs && isDerivation(storePath)) {
|
||||
PathSet outputs = store.queryDerivationOutputs(storePath);
|
||||
foreach (PathSet::iterator, i, outputs)
|
||||
if (store.isValidPath(*i))
|
||||
computeFSClosure(store, *i, paths, flipDirection, true);
|
||||
if (flipDirection) {
|
||||
store.queryReferrers(path, edges);
|
||||
|
||||
if (includeOutputs) {
|
||||
PathSet derivers = store.queryValidDerivers(path);
|
||||
foreach (PathSet::iterator, i, derivers)
|
||||
edges.insert(*i);
|
||||
}
|
||||
|
||||
if (includeDerivers && isDerivation(path)) {
|
||||
PathSet outputs = store.queryDerivationOutputs(path);
|
||||
foreach (PathSet::iterator, i, outputs)
|
||||
if (store.isValidPath(*i) && store.queryDeriver(*i) == path)
|
||||
edges.insert(*i);
|
||||
}
|
||||
|
||||
} else {
|
||||
store.queryReferences(path, edges);
|
||||
|
||||
if (includeOutputs && isDerivation(path)) {
|
||||
PathSet outputs = store.queryDerivationOutputs(path);
|
||||
foreach (PathSet::iterator, i, outputs)
|
||||
if (store.isValidPath(*i)) edges.insert(*i);
|
||||
}
|
||||
|
||||
if (includeDerivers) {
|
||||
Path deriver = store.queryDeriver(path);
|
||||
if (store.isValidPath(deriver)) edges.insert(deriver);
|
||||
}
|
||||
}
|
||||
|
||||
foreach (PathSet::iterator, i, references)
|
||||
computeFSClosure(store, *i, paths, flipDirection, includeOutputs);
|
||||
foreach (PathSet::iterator, i, edges)
|
||||
computeFSClosure(store, *i, paths, flipDirection, includeOutputs, includeDerivers);
|
||||
}
|
||||
|
||||
|
||||
@@ -52,68 +72,129 @@ void queryMissing(StoreAPI & store, const PathSet & targets,
|
||||
unsigned long long & downloadSize, unsigned long long & narSize)
|
||||
{
|
||||
downloadSize = narSize = 0;
|
||||
|
||||
|
||||
PathSet todo(targets.begin(), targets.end()), done;
|
||||
|
||||
/* Getting substitute info has high latency when using the binary
|
||||
cache substituter. Thus it's essential to do substitute
|
||||
queries in parallel as much as possible. To accomplish this
|
||||
we do the following:
|
||||
|
||||
- For all paths still to be processed (‘todo’), we add all
|
||||
paths for which we need info to the set ‘query’. For an
|
||||
unbuilt derivation this is the output paths; otherwise, it's
|
||||
the path itself.
|
||||
|
||||
- We get info about all paths in ‘query’ in parallel.
|
||||
|
||||
- We process the results and add new items to ‘todo’ if
|
||||
necessary. E.g. if a path is substitutable, then we need to
|
||||
get info on its references.
|
||||
|
||||
- Repeat until ‘todo’ is empty.
|
||||
*/
|
||||
|
||||
while (!todo.empty()) {
|
||||
Path p = *(todo.begin());
|
||||
todo.erase(p);
|
||||
if (done.find(p) != done.end()) continue;
|
||||
done.insert(p);
|
||||
|
||||
if (isDerivation(p)) {
|
||||
if (!store.isValidPath(p)) {
|
||||
unknown.insert(p);
|
||||
continue;
|
||||
PathSet query, todoDrv, todoNonDrv;
|
||||
|
||||
foreach (PathSet::iterator, i, todo) {
|
||||
if (done.find(*i) != done.end()) continue;
|
||||
done.insert(*i);
|
||||
|
||||
DrvPathWithOutputs i2 = parseDrvPathWithOutputs(*i);
|
||||
|
||||
if (isDerivation(i2.first)) {
|
||||
if (!store.isValidPath(i2.first)) {
|
||||
// FIXME: we could try to substitute p.
|
||||
unknown.insert(*i);
|
||||
continue;
|
||||
}
|
||||
Derivation drv = derivationFromPath(store, i2.first);
|
||||
|
||||
PathSet invalid;
|
||||
foreach (DerivationOutputs::iterator, j, drv.outputs)
|
||||
if (wantOutput(j->first, i2.second)
|
||||
&& !store.isValidPath(j->second.path))
|
||||
invalid.insert(j->second.path);
|
||||
if (invalid.empty()) continue;
|
||||
|
||||
todoDrv.insert(*i);
|
||||
if (settings.useSubstitutes) query.insert(invalid.begin(), invalid.end());
|
||||
}
|
||||
Derivation drv = derivationFromPath(store, p);
|
||||
|
||||
bool mustBuild = false;
|
||||
foreach (DerivationOutputs::iterator, i, drv.outputs)
|
||||
if (!store.isValidPath(i->second.path) &&
|
||||
!(queryBoolSetting("build-use-substitutes", true) && store.hasSubstitutes(i->second.path)))
|
||||
mustBuild = true;
|
||||
|
||||
if (mustBuild) {
|
||||
willBuild.insert(p);
|
||||
todo.insert(drv.inputSrcs.begin(), drv.inputSrcs.end());
|
||||
foreach (DerivationInputs::iterator, i, drv.inputDrvs)
|
||||
todo.insert(i->first);
|
||||
} else
|
||||
foreach (DerivationOutputs::iterator, i, drv.outputs)
|
||||
todo.insert(i->second.path);
|
||||
else {
|
||||
if (store.isValidPath(*i)) continue;
|
||||
query.insert(*i);
|
||||
todoNonDrv.insert(*i);
|
||||
}
|
||||
}
|
||||
|
||||
else {
|
||||
if (store.isValidPath(p)) continue;
|
||||
SubstitutablePathInfo info;
|
||||
if (store.querySubstitutablePathInfo(p, info)) {
|
||||
willSubstitute.insert(p);
|
||||
downloadSize += info.downloadSize;
|
||||
narSize += info.narSize;
|
||||
todo.insert(info.references.begin(), info.references.end());
|
||||
todo.clear();
|
||||
|
||||
SubstitutablePathInfos infos;
|
||||
store.querySubstitutablePathInfos(query, infos);
|
||||
|
||||
foreach (PathSet::iterator, i, todoDrv) {
|
||||
DrvPathWithOutputs i2 = parseDrvPathWithOutputs(*i);
|
||||
|
||||
// FIXME: cache this
|
||||
Derivation drv = derivationFromPath(store, i2.first);
|
||||
|
||||
PathSet outputs;
|
||||
bool mustBuild = false;
|
||||
if (settings.useSubstitutes) {
|
||||
foreach (DerivationOutputs::iterator, j, drv.outputs) {
|
||||
if (!wantOutput(j->first, i2.second)) continue;
|
||||
if (!store.isValidPath(j->second.path)) {
|
||||
if (infos.find(j->second.path) == infos.end())
|
||||
mustBuild = true;
|
||||
else
|
||||
outputs.insert(j->second.path);
|
||||
}
|
||||
}
|
||||
} else
|
||||
unknown.insert(p);
|
||||
mustBuild = true;
|
||||
|
||||
if (mustBuild) {
|
||||
willBuild.insert(i2.first);
|
||||
todo.insert(drv.inputSrcs.begin(), drv.inputSrcs.end());
|
||||
foreach (DerivationInputs::iterator, j, drv.inputDrvs)
|
||||
todo.insert(makeDrvPathWithOutputs(j->first, j->second));
|
||||
} else
|
||||
todoNonDrv.insert(outputs.begin(), outputs.end());
|
||||
}
|
||||
|
||||
foreach (PathSet::iterator, i, todoNonDrv) {
|
||||
done.insert(*i);
|
||||
SubstitutablePathInfos::iterator info = infos.find(*i);
|
||||
if (info != infos.end()) {
|
||||
willSubstitute.insert(*i);
|
||||
downloadSize += info->second.downloadSize;
|
||||
narSize += info->second.narSize;
|
||||
todo.insert(info->second.references.begin(), info->second.references.end());
|
||||
} else
|
||||
unknown.insert(*i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
static void dfsVisit(StoreAPI & store, const PathSet & paths,
|
||||
const Path & path, PathSet & visited, Paths & sorted,
|
||||
PathSet & parents)
|
||||
{
|
||||
if (parents.find(path) != parents.end())
|
||||
throw BuildError(format("cycle detected in the references of `%1%'") % path);
|
||||
|
||||
|
||||
if (visited.find(path) != visited.end()) return;
|
||||
visited.insert(path);
|
||||
parents.insert(path);
|
||||
|
||||
|
||||
PathSet references;
|
||||
if (store.isValidPath(path))
|
||||
store.queryReferences(path, references);
|
||||
|
||||
|
||||
foreach (PathSet::iterator, i, references)
|
||||
/* Don't traverse into paths that don't exist. That can
|
||||
happen due to substitutes for non-existent paths. */
|
||||
|
||||
@@ -17,9 +17,9 @@ Derivation derivationFromPath(StoreAPI & store, const Path & drvPath);
|
||||
`storePath' is returned; that is, the closures under the
|
||||
`referrers' relation instead of the `references' relation is
|
||||
returned. */
|
||||
void computeFSClosure(StoreAPI & store, const Path & storePath,
|
||||
void computeFSClosure(StoreAPI & store, const Path & path,
|
||||
PathSet & paths, bool flipDirection = false,
|
||||
bool includeOutputs = false);
|
||||
bool includeOutputs = false, bool includeDerivers = false);
|
||||
|
||||
/* Return the path corresponding to the output identifier `id' in the
|
||||
given derivation. */
|
||||
|
||||
+112
-124
@@ -1,6 +1,8 @@
|
||||
#include "config.h"
|
||||
|
||||
#include "util.hh"
|
||||
#include "local-store.hh"
|
||||
#include "immutable.hh"
|
||||
#include "globals.hh"
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
@@ -12,15 +14,11 @@
|
||||
namespace nix {
|
||||
|
||||
|
||||
typedef std::map<Hash, std::pair<Path, ino_t> > HashToPath;
|
||||
|
||||
|
||||
static void makeWritable(const Path & path)
|
||||
{
|
||||
struct stat st;
|
||||
if (lstat(path.c_str(), &st))
|
||||
throw SysError(format("getting attributes of path `%1%'") % path);
|
||||
if (S_ISDIR(st.st_mode) || S_ISREG(st.st_mode)) makeMutable(path);
|
||||
throw SysError(format("getting attributes of path `%1%'") % path);
|
||||
if (chmod(path.c_str(), st.st_mode | S_IWUSR) == -1)
|
||||
throw SysError(format("changing writability of `%1%'") % path);
|
||||
}
|
||||
@@ -33,9 +31,8 @@ struct MakeReadOnly
|
||||
~MakeReadOnly()
|
||||
{
|
||||
try {
|
||||
/* This will make the path read-only (and restore the
|
||||
immutable bit on platforms that support it). */
|
||||
if (path != "") canonicalisePathMetaData(path, false);
|
||||
/* This will make the path read-only. */
|
||||
if (path != "") canonicaliseTimestampAndPermissions(path);
|
||||
} catch (...) {
|
||||
ignoreException();
|
||||
}
|
||||
@@ -43,150 +40,141 @@ struct MakeReadOnly
|
||||
};
|
||||
|
||||
|
||||
struct MakeImmutable
|
||||
{
|
||||
Path path;
|
||||
MakeImmutable(const Path & path) : path(path) { }
|
||||
~MakeImmutable() { makeImmutable(path); }
|
||||
};
|
||||
|
||||
|
||||
static void hashAndLink(bool dryRun, HashToPath & hashToPath,
|
||||
OptimiseStats & stats, const Path & path)
|
||||
void LocalStore::optimisePath_(OptimiseStats & stats, const Path & path)
|
||||
{
|
||||
checkInterrupt();
|
||||
|
||||
struct stat st;
|
||||
if (lstat(path.c_str(), &st))
|
||||
throw SysError(format("getting attributes of path `%1%'") % path);
|
||||
throw SysError(format("getting attributes of path `%1%'") % path);
|
||||
|
||||
if (S_ISDIR(st.st_mode)) {
|
||||
Strings names = readDirectory(path);
|
||||
foreach (Strings::iterator, i, names)
|
||||
optimisePath_(stats, path + "/" + *i);
|
||||
return;
|
||||
}
|
||||
|
||||
/* We can hard link regular files and maybe symlinks. */
|
||||
if (!S_ISREG(st.st_mode)
|
||||
#if CAN_LINK_SYMLINK
|
||||
&& !S_ISLNK(st.st_mode)
|
||||
#endif
|
||||
) return;
|
||||
|
||||
/* Sometimes SNAFUs can cause files in the Nix store to be
|
||||
modified, in particular when running programs as root under
|
||||
NixOS (example: $fontconfig/var/cache being modified). Skip
|
||||
those files. */
|
||||
those files. FIXME: check the modification time. */
|
||||
if (S_ISREG(st.st_mode) && (st.st_mode & S_IWUSR)) {
|
||||
printMsg(lvlError, format("skipping suspicious writable file `%1%'") % path);
|
||||
return;
|
||||
}
|
||||
|
||||
/* We can hard link regular files and symlinks. */
|
||||
if (S_ISREG(st.st_mode) || S_ISLNK(st.st_mode)) {
|
||||
/* Hash the file. Note that hashPath() returns the hash over the
|
||||
NAR serialisation, which includes the execute bit on the file.
|
||||
Thus, executable and non-executable files with the same
|
||||
contents *won't* be linked (which is good because otherwise the
|
||||
permissions would be screwed up).
|
||||
|
||||
/* Hash the file. Note that hashPath() returns the hash over
|
||||
the NAR serialisation, which includes the execute bit on
|
||||
the file. Thus, executable and non-executable files with
|
||||
the same contents *won't* be linked (which is good because
|
||||
otherwise the permissions would be screwed up).
|
||||
Also note that if `path' is a symlink, then we're hashing the
|
||||
contents of the symlink (i.e. the result of readlink()), not
|
||||
the contents of the target (which may not even exist). */
|
||||
Hash hash = hashPath(htSHA256, path).first;
|
||||
stats.totalFiles++;
|
||||
printMsg(lvlDebug, format("`%1%' has hash `%2%'") % path % printHash(hash));
|
||||
|
||||
Also note that if `path' is a symlink, then we're hashing
|
||||
the contents of the symlink (i.e. the result of
|
||||
readlink()), not the contents of the target (which may not
|
||||
even exist). */
|
||||
Hash hash = hashPath(htSHA256, path).first;
|
||||
stats.totalFiles++;
|
||||
printMsg(lvlDebug, format("`%1%' has hash `%2%'") % path % printHash(hash));
|
||||
/* Check if this is a known hash. */
|
||||
Path linkPath = linksDir + "/" + printHash32(hash);
|
||||
|
||||
std::pair<Path, ino_t> prevPath = hashToPath[hash];
|
||||
|
||||
if (prevPath.first == "") {
|
||||
hashToPath[hash] = std::pair<Path, ino_t>(path, st.st_ino);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Yes! We've seen a file with the same contents. Replace
|
||||
the current file with a hard link to that file. */
|
||||
stats.sameContents++;
|
||||
if (prevPath.second == st.st_ino) {
|
||||
printMsg(lvlDebug, format("`%1%' is already linked to `%2%'") % path % prevPath.first);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!dryRun) {
|
||||
|
||||
printMsg(lvlTalkative, format("linking `%1%' to `%2%'") % path % prevPath.first);
|
||||
|
||||
Path tempLink = (format("%1%.tmp-%2%-%3%")
|
||||
% path % getpid() % rand()).str();
|
||||
|
||||
/* Make the containing directory writable, but only if
|
||||
it's not the store itself (we don't want or need to
|
||||
mess with its permissions). */
|
||||
bool mustToggle = !isStorePath(path);
|
||||
if (mustToggle) makeWritable(dirOf(path));
|
||||
|
||||
/* When we're done, make the directory read-only again and
|
||||
reset its timestamp back to 0. */
|
||||
MakeReadOnly makeReadOnly(mustToggle ? dirOf(path) : "");
|
||||
|
||||
/* If ‘prevPath’ is immutable, we can't create hard links
|
||||
to it, so make it mutable first (and make it immutable
|
||||
again when we're done). We also have to make ‘path’
|
||||
mutable, otherwise rename() will fail to delete it. */
|
||||
makeMutable(prevPath.first);
|
||||
MakeImmutable mk1(prevPath.first);
|
||||
|
||||
makeMutable(path);
|
||||
MakeImmutable mk2(path);
|
||||
|
||||
if (link(prevPath.first.c_str(), tempLink.c_str()) == -1) {
|
||||
if (errno == EMLINK) {
|
||||
/* Too many links to the same file (>= 32000 on
|
||||
most file systems). This is likely to happen
|
||||
with empty files. Just start over, creating
|
||||
links to the current file. */
|
||||
printMsg(lvlInfo, format("`%1%' has maximum number of links") % prevPath.first);
|
||||
hashToPath[hash] = std::pair<Path, ino_t>(path, st.st_ino);
|
||||
return;
|
||||
}
|
||||
throw SysError(format("cannot link `%1%' to `%2%'")
|
||||
% tempLink % prevPath.first);
|
||||
}
|
||||
|
||||
/* Atomically replace the old file with the new hard link. */
|
||||
if (rename(tempLink.c_str(), path.c_str()) == -1) {
|
||||
if (errno == EMLINK) {
|
||||
/* Some filesystems generate too many links on the
|
||||
rename, rather than on the original link.
|
||||
(Probably it temporarily increases the st_nlink
|
||||
field before decreasing it again.) */
|
||||
printMsg(lvlInfo, format("`%1%' has maximum number of links") % prevPath.first);
|
||||
hashToPath[hash] = std::pair<Path, ino_t>(path, st.st_ino);
|
||||
|
||||
/* Unlink the temp link. */
|
||||
if (unlink(tempLink.c_str()) == -1)
|
||||
printMsg(lvlError, format("unable to unlink `%1%'") % tempLink);
|
||||
return;
|
||||
}
|
||||
throw SysError(format("cannot rename `%1%' to `%2%'")
|
||||
% tempLink % path);
|
||||
}
|
||||
} else
|
||||
printMsg(lvlTalkative, format("would link `%1%' to `%2%'") % path % prevPath.first);
|
||||
|
||||
stats.filesLinked++;
|
||||
stats.bytesFreed += st.st_size;
|
||||
stats.blocksFreed += st.st_blocks;
|
||||
if (!pathExists(linkPath)) {
|
||||
/* Nope, create a hard link in the links directory. */
|
||||
if (link(path.c_str(), linkPath.c_str()) == 0) return;
|
||||
if (errno != EEXIST)
|
||||
throw SysError(format("cannot link `%1%' to `%2%'") % linkPath % path);
|
||||
/* Fall through if another process created ‘linkPath’ before
|
||||
we did. */
|
||||
}
|
||||
|
||||
if (S_ISDIR(st.st_mode)) {
|
||||
Strings names = readDirectory(path);
|
||||
foreach (Strings::iterator, i, names)
|
||||
hashAndLink(dryRun, hashToPath, stats, path + "/" + *i);
|
||||
/* Yes! We've seen a file with the same contents. Replace the
|
||||
current file with a hard link to that file. */
|
||||
struct stat stLink;
|
||||
if (lstat(linkPath.c_str(), &stLink))
|
||||
throw SysError(format("getting attributes of path `%1%'") % linkPath);
|
||||
|
||||
stats.sameContents++;
|
||||
if (st.st_ino == stLink.st_ino) {
|
||||
printMsg(lvlDebug, format("`%1%' is already linked to `%2%'") % path % linkPath);
|
||||
return;
|
||||
}
|
||||
|
||||
printMsg(lvlTalkative, format("linking `%1%' to `%2%'") % path % linkPath);
|
||||
|
||||
/* Make the containing directory writable, but only if it's not
|
||||
the store itself (we don't want or need to mess with its
|
||||
permissions). */
|
||||
bool mustToggle = !isStorePath(path);
|
||||
if (mustToggle) makeWritable(dirOf(path));
|
||||
|
||||
/* When we're done, make the directory read-only again and reset
|
||||
its timestamp back to 0. */
|
||||
MakeReadOnly makeReadOnly(mustToggle ? dirOf(path) : "");
|
||||
|
||||
Path tempLink = (format("%1%/.tmp-link-%2%-%3%")
|
||||
% settings.nixStore % getpid() % rand()).str();
|
||||
|
||||
if (link(linkPath.c_str(), tempLink.c_str()) == -1) {
|
||||
if (errno == EMLINK) {
|
||||
/* Too many links to the same file (>= 32000 on most file
|
||||
systems). This is likely to happen with empty files.
|
||||
Just shrug and ignore. */
|
||||
if (st.st_size)
|
||||
printMsg(lvlInfo, format("`%1%' has maximum number of links") % linkPath);
|
||||
return;
|
||||
}
|
||||
throw SysError(format("cannot link `%1%' to `%2%'") % tempLink % linkPath);
|
||||
}
|
||||
|
||||
/* Atomically replace the old file with the new hard link. */
|
||||
if (rename(tempLink.c_str(), path.c_str()) == -1) {
|
||||
if (unlink(tempLink.c_str()) == -1)
|
||||
printMsg(lvlError, format("unable to unlink `%1%'") % tempLink);
|
||||
if (errno == EMLINK) {
|
||||
/* Some filesystems generate too many links on the rename,
|
||||
rather than on the original link. (Probably it
|
||||
temporarily increases the st_nlink field before
|
||||
decreasing it again.) */
|
||||
if (st.st_size)
|
||||
printMsg(lvlInfo, format("`%1%' has maximum number of links") % linkPath);
|
||||
return;
|
||||
}
|
||||
throw SysError(format("cannot rename `%1%' to `%2%'") % tempLink % path);
|
||||
}
|
||||
|
||||
stats.filesLinked++;
|
||||
stats.bytesFreed += st.st_size;
|
||||
stats.blocksFreed += st.st_blocks;
|
||||
}
|
||||
|
||||
|
||||
void LocalStore::optimiseStore(bool dryRun, OptimiseStats & stats)
|
||||
void LocalStore::optimiseStore(OptimiseStats & stats)
|
||||
{
|
||||
HashToPath hashToPath;
|
||||
|
||||
PathSet paths = queryValidPaths();
|
||||
PathSet paths = queryAllValidPaths();
|
||||
|
||||
foreach (PathSet::iterator, i, paths) {
|
||||
addTempRoot(*i);
|
||||
if (!isValidPath(*i)) continue; /* path was GC'ed, probably */
|
||||
startNest(nest, lvlChatty, format("hashing files in `%1%'") % *i);
|
||||
hashAndLink(dryRun, hashToPath, stats, *i);
|
||||
optimisePath_(stats, *i);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void LocalStore::optimisePath(const Path & path)
|
||||
{
|
||||
OptimiseStats stats;
|
||||
if (settings.autoOptimiseStore) optimisePath_(stats, path);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
+142
-105
@@ -50,17 +50,13 @@ void RemoteStore::openConnection(bool reserveSpace)
|
||||
|
||||
string remoteMode = getEnv("NIX_REMOTE");
|
||||
|
||||
if (remoteMode == "slave")
|
||||
/* Fork off a setuid worker to do the privileged work. */
|
||||
forkSlave();
|
||||
else if (remoteMode == "daemon")
|
||||
if (remoteMode == "daemon")
|
||||
/* Connect to a daemon that does the privileged work for
|
||||
us. */
|
||||
connectToDaemon();
|
||||
connectToDaemon();
|
||||
else
|
||||
throw Error(format("invalid setting for NIX_REMOTE, `%1%'")
|
||||
% remoteMode);
|
||||
|
||||
throw Error(format("invalid setting for NIX_REMOTE, `%1%'") % remoteMode);
|
||||
|
||||
from.fd = fdSocket;
|
||||
to.fd = fdSocket;
|
||||
|
||||
@@ -88,61 +84,14 @@ void RemoteStore::openConnection(bool reserveSpace)
|
||||
}
|
||||
|
||||
|
||||
void RemoteStore::forkSlave()
|
||||
{
|
||||
int sockets[2];
|
||||
if (socketpair(AF_UNIX, SOCK_STREAM, 0, sockets) == -1)
|
||||
throw SysError("cannot create sockets");
|
||||
|
||||
fdSocket = sockets[0];
|
||||
AutoCloseFD fdChild = sockets[1];
|
||||
|
||||
/* Start the worker. */
|
||||
Path worker = getEnv("NIX_WORKER");
|
||||
if (worker == "")
|
||||
worker = nixBinDir + "/nix-worker";
|
||||
|
||||
child = fork();
|
||||
|
||||
switch (child) {
|
||||
|
||||
case -1:
|
||||
throw SysError("unable to fork");
|
||||
|
||||
case 0:
|
||||
try { /* child */
|
||||
|
||||
if (dup2(fdChild, STDOUT_FILENO) == -1)
|
||||
throw SysError("dupping write side");
|
||||
|
||||
if (dup2(fdChild, STDIN_FILENO) == -1)
|
||||
throw SysError("dupping read side");
|
||||
|
||||
close(fdSocket);
|
||||
close(fdChild);
|
||||
|
||||
execlp(worker.c_str(), worker.c_str(), "--slave", NULL);
|
||||
|
||||
throw SysError(format("executing `%1%'") % worker);
|
||||
|
||||
} catch (std::exception & e) {
|
||||
std::cerr << format("child error: %1%\n") % e.what();
|
||||
}
|
||||
quickExit(1);
|
||||
}
|
||||
|
||||
fdChild.close();
|
||||
|
||||
}
|
||||
|
||||
|
||||
void RemoteStore::connectToDaemon()
|
||||
{
|
||||
fdSocket = socket(PF_UNIX, SOCK_STREAM, 0);
|
||||
if (fdSocket == -1)
|
||||
throw SysError("cannot create Unix domain socket");
|
||||
closeOnExec(fdSocket);
|
||||
|
||||
string socketPath = nixStateDir + DEFAULT_SOCKET_PATH;
|
||||
string socketPath = settings.nixDaemonSocketFile;
|
||||
|
||||
/* Urgh, sockaddr_un allows path names of only 108 characters. So
|
||||
chdir to the socket directory so that we can pass a relative
|
||||
@@ -150,16 +99,16 @@ void RemoteStore::connectToDaemon()
|
||||
applications... */
|
||||
AutoCloseFD fdPrevDir = open(".", O_RDONLY);
|
||||
if (fdPrevDir == -1) throw SysError("couldn't open current directory");
|
||||
chdir(dirOf(socketPath).c_str());
|
||||
chdir(dirOf(socketPath).c_str());
|
||||
Path socketPathRel = "./" + baseNameOf(socketPath);
|
||||
|
||||
|
||||
struct sockaddr_un addr;
|
||||
addr.sun_family = AF_UNIX;
|
||||
if (socketPathRel.size() >= sizeof(addr.sun_path))
|
||||
throw Error(format("socket path `%1%' is too long") % socketPathRel);
|
||||
using namespace std;
|
||||
strcpy(addr.sun_path, socketPathRel.c_str());
|
||||
|
||||
|
||||
if (connect(fdSocket, (struct sockaddr *) &addr, sizeof(addr)) == -1)
|
||||
throw SysError(format("cannot connect to daemon at `%1%'") % socketPath);
|
||||
|
||||
@@ -184,24 +133,34 @@ RemoteStore::~RemoteStore()
|
||||
void RemoteStore::setOptions()
|
||||
{
|
||||
writeInt(wopSetOptions, to);
|
||||
writeInt(keepFailed, to);
|
||||
writeInt(keepGoing, to);
|
||||
writeInt(tryFallback, to);
|
||||
|
||||
writeInt(settings.keepFailed, to);
|
||||
writeInt(settings.keepGoing, to);
|
||||
writeInt(settings.tryFallback, to);
|
||||
writeInt(verbosity, to);
|
||||
writeInt(maxBuildJobs, to);
|
||||
writeInt(maxSilentTime, to);
|
||||
writeInt(settings.maxBuildJobs, to);
|
||||
writeInt(settings.maxSilentTime, to);
|
||||
if (GET_PROTOCOL_MINOR(daemonVersion) >= 2)
|
||||
writeInt(useBuildHook, to);
|
||||
writeInt(settings.useBuildHook, to);
|
||||
if (GET_PROTOCOL_MINOR(daemonVersion) >= 4) {
|
||||
writeInt(buildVerbosity, to);
|
||||
writeInt(settings.buildVerbosity, to);
|
||||
writeInt(logType, to);
|
||||
writeInt(printBuildTrace, to);
|
||||
writeInt(settings.printBuildTrace, to);
|
||||
}
|
||||
if (GET_PROTOCOL_MINOR(daemonVersion) >= 6)
|
||||
writeInt(buildCores, to);
|
||||
if (GET_PROTOCOL_MINOR(daemonVersion) >= 10)
|
||||
writeInt(queryBoolSetting("build-use-substitutes", true), to);
|
||||
|
||||
writeInt(settings.buildCores, to);
|
||||
if (GET_PROTOCOL_MINOR(daemonVersion) >= 10)
|
||||
writeInt(settings.useSubstitutes, to);
|
||||
|
||||
if (GET_PROTOCOL_MINOR(daemonVersion) >= 12) {
|
||||
Settings::SettingsMap overrides = settings.getOverrides();
|
||||
writeInt(overrides.size(), to);
|
||||
foreach (Settings::SettingsMap::iterator, i, overrides) {
|
||||
writeString(i->first, to);
|
||||
writeString(i->second, to);
|
||||
}
|
||||
}
|
||||
|
||||
processStderr();
|
||||
}
|
||||
|
||||
@@ -217,42 +176,96 @@ bool RemoteStore::isValidPath(const Path & path)
|
||||
}
|
||||
|
||||
|
||||
PathSet RemoteStore::queryValidPaths()
|
||||
PathSet RemoteStore::queryValidPaths(const PathSet & paths)
|
||||
{
|
||||
openConnection();
|
||||
writeInt(wopQueryValidPaths, to);
|
||||
if (GET_PROTOCOL_MINOR(daemonVersion) < 12) {
|
||||
PathSet res;
|
||||
foreach (PathSet::const_iterator, i, paths)
|
||||
if (isValidPath(*i)) res.insert(*i);
|
||||
return res;
|
||||
} else {
|
||||
writeInt(wopQueryValidPaths, to);
|
||||
writeStrings(paths, to);
|
||||
processStderr();
|
||||
return readStorePaths<PathSet>(from);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
PathSet RemoteStore::queryAllValidPaths()
|
||||
{
|
||||
openConnection();
|
||||
writeInt(wopQueryAllValidPaths, to);
|
||||
processStderr();
|
||||
return readStorePaths<PathSet>(from);
|
||||
}
|
||||
|
||||
|
||||
bool RemoteStore::hasSubstitutes(const Path & path)
|
||||
PathSet RemoteStore::querySubstitutablePaths(const PathSet & paths)
|
||||
{
|
||||
openConnection();
|
||||
writeInt(wopHasSubstitutes, to);
|
||||
writeString(path, to);
|
||||
processStderr();
|
||||
unsigned int reply = readInt(from);
|
||||
return reply != 0;
|
||||
if (GET_PROTOCOL_MINOR(daemonVersion) < 12) {
|
||||
PathSet res;
|
||||
foreach (PathSet::const_iterator, i, paths) {
|
||||
writeInt(wopHasSubstitutes, to);
|
||||
writeString(*i, to);
|
||||
processStderr();
|
||||
if (readInt(from)) res.insert(*i);
|
||||
}
|
||||
return res;
|
||||
} else {
|
||||
writeInt(wopQuerySubstitutablePaths, to);
|
||||
writeStrings(paths, to);
|
||||
processStderr();
|
||||
return readStorePaths<PathSet>(from);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
bool RemoteStore::querySubstitutablePathInfo(const Path & path,
|
||||
SubstitutablePathInfo & info)
|
||||
void RemoteStore::querySubstitutablePathInfos(const PathSet & paths,
|
||||
SubstitutablePathInfos & infos)
|
||||
{
|
||||
if (paths.empty()) return;
|
||||
|
||||
openConnection();
|
||||
if (GET_PROTOCOL_MINOR(daemonVersion) < 3) return false;
|
||||
writeInt(wopQuerySubstitutablePathInfo, to);
|
||||
writeString(path, to);
|
||||
processStderr();
|
||||
unsigned int reply = readInt(from);
|
||||
if (reply == 0) return false;
|
||||
info.deriver = readString(from);
|
||||
if (info.deriver != "") assertStorePath(info.deriver);
|
||||
info.references = readStorePaths<PathSet>(from);
|
||||
info.downloadSize = readLongLong(from);
|
||||
info.narSize = GET_PROTOCOL_MINOR(daemonVersion) >= 7 ? readLongLong(from) : 0;
|
||||
return true;
|
||||
|
||||
if (GET_PROTOCOL_MINOR(daemonVersion) < 3) return;
|
||||
|
||||
if (GET_PROTOCOL_MINOR(daemonVersion) < 12) {
|
||||
|
||||
foreach (PathSet::const_iterator, i, paths) {
|
||||
SubstitutablePathInfo info;
|
||||
writeInt(wopQuerySubstitutablePathInfo, to);
|
||||
writeString(*i, to);
|
||||
processStderr();
|
||||
unsigned int reply = readInt(from);
|
||||
if (reply == 0) continue;
|
||||
info.deriver = readString(from);
|
||||
if (info.deriver != "") assertStorePath(info.deriver);
|
||||
info.references = readStorePaths<PathSet>(from);
|
||||
info.downloadSize = readLongLong(from);
|
||||
info.narSize = GET_PROTOCOL_MINOR(daemonVersion) >= 7 ? readLongLong(from) : 0;
|
||||
infos[*i] = info;
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
writeInt(wopQuerySubstitutablePathInfos, to);
|
||||
writeStrings(paths, to);
|
||||
processStderr();
|
||||
unsigned int count = readInt(from);
|
||||
for (unsigned int n = 0; n < count; n++) {
|
||||
Path path = readStorePath(from);
|
||||
SubstitutablePathInfo & info(infos[path]);
|
||||
info.deriver = readString(from);
|
||||
if (info.deriver != "") assertStorePath(info.deriver);
|
||||
info.references = readStorePaths<PathSet>(from);
|
||||
info.downloadSize = readLongLong(from);
|
||||
info.narSize = readLongLong(from);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -321,6 +334,16 @@ Path RemoteStore::queryDeriver(const Path & path)
|
||||
}
|
||||
|
||||
|
||||
PathSet RemoteStore::queryValidDerivers(const Path & path)
|
||||
{
|
||||
openConnection();
|
||||
writeInt(wopQueryValidDerivers, to);
|
||||
writeString(path, to);
|
||||
processStderr();
|
||||
return readStorePaths<PathSet>(from);
|
||||
}
|
||||
|
||||
|
||||
PathSet RemoteStore::queryDerivationOutputs(const Path & path)
|
||||
{
|
||||
openConnection();
|
||||
@@ -354,10 +377,12 @@ Path RemoteStore::queryPathFromHashPart(const string & hashPart)
|
||||
|
||||
|
||||
Path RemoteStore::addToStore(const Path & _srcPath,
|
||||
bool recursive, HashType hashAlgo, PathFilter & filter)
|
||||
bool recursive, HashType hashAlgo, PathFilter & filter, bool repair)
|
||||
{
|
||||
if (repair) throw Error("repairing is not supported when building through the Nix daemon");
|
||||
|
||||
openConnection();
|
||||
|
||||
|
||||
Path srcPath(absPath(_srcPath));
|
||||
|
||||
writeInt(wopAddToStore, to);
|
||||
@@ -373,14 +398,16 @@ Path RemoteStore::addToStore(const Path & _srcPath,
|
||||
|
||||
|
||||
Path RemoteStore::addTextToStore(const string & name, const string & s,
|
||||
const PathSet & references)
|
||||
const PathSet & references, bool repair)
|
||||
{
|
||||
if (repair) throw Error("repairing is not supported when building through the Nix daemon");
|
||||
|
||||
openConnection();
|
||||
writeInt(wopAddTextToStore, to);
|
||||
writeString(name, to);
|
||||
writeString(s, to);
|
||||
writeStrings(references, to);
|
||||
|
||||
|
||||
processStderr();
|
||||
return readStorePath(from);
|
||||
}
|
||||
@@ -409,11 +436,21 @@ Paths RemoteStore::importPaths(bool requireSignature, Source & source)
|
||||
}
|
||||
|
||||
|
||||
void RemoteStore::buildPaths(const PathSet & drvPaths)
|
||||
void RemoteStore::buildPaths(const PathSet & drvPaths, bool repair)
|
||||
{
|
||||
if (repair) throw Error("repairing is not supported when building through the Nix daemon");
|
||||
openConnection();
|
||||
writeInt(wopBuildPaths, to);
|
||||
writeStrings(drvPaths, to);
|
||||
if (GET_PROTOCOL_MINOR(daemonVersion) >= 13)
|
||||
writeStrings(drvPaths, to);
|
||||
else {
|
||||
/* For backwards compatibility with old daemons, strip output
|
||||
identifiers. */
|
||||
PathSet drvPaths2;
|
||||
foreach (PathSet::const_iterator, i, drvPaths)
|
||||
drvPaths2.insert(string(*i, 0, i->find('!')));
|
||||
writeStrings(drvPaths2, to);
|
||||
}
|
||||
processStderr();
|
||||
readInt(from);
|
||||
}
|
||||
@@ -477,7 +514,7 @@ Roots RemoteStore::findRoots()
|
||||
void RemoteStore::collectGarbage(const GCOptions & options, GCResults & results)
|
||||
{
|
||||
openConnection(false);
|
||||
|
||||
|
||||
writeInt(wopCollectGarbage, to);
|
||||
writeInt(options.action, to);
|
||||
writeStrings(options.pathsToDelete, to);
|
||||
@@ -489,12 +526,12 @@ void RemoteStore::collectGarbage(const GCOptions & options, GCResults & results)
|
||||
writeInt(0, to);
|
||||
writeInt(0, to);
|
||||
}
|
||||
|
||||
|
||||
processStderr();
|
||||
|
||||
|
||||
results.paths = readStrings<PathSet>(from);
|
||||
results.bytesFreed = readLongLong(from);
|
||||
results.blocksFreed = readLongLong(from);
|
||||
readLongLong(from); // obsolete
|
||||
}
|
||||
|
||||
|
||||
@@ -538,7 +575,7 @@ void RemoteStore::processStderr(Sink * sink, Source * source)
|
||||
}
|
||||
else {
|
||||
string s = readString(from);
|
||||
writeToStderr((const unsigned char *) s.data(), s.size());
|
||||
writeToStderr(s);
|
||||
}
|
||||
}
|
||||
if (msg == STDERR_ERROR) {
|
||||
|
||||
@@ -26,7 +26,9 @@ public:
|
||||
|
||||
bool isValidPath(const Path & path);
|
||||
|
||||
PathSet queryValidPaths();
|
||||
PathSet queryValidPaths(const PathSet & paths);
|
||||
|
||||
PathSet queryAllValidPaths();
|
||||
|
||||
ValidPathInfo queryPathInfo(const Path & path);
|
||||
|
||||
@@ -38,30 +40,32 @@ public:
|
||||
|
||||
Path queryDeriver(const Path & path);
|
||||
|
||||
PathSet queryValidDerivers(const Path & path);
|
||||
|
||||
PathSet queryDerivationOutputs(const Path & path);
|
||||
|
||||
StringSet queryDerivationOutputNames(const Path & path);
|
||||
|
||||
Path queryPathFromHashPart(const string & hashPart);
|
||||
|
||||
bool hasSubstitutes(const Path & path);
|
||||
PathSet querySubstitutablePaths(const PathSet & paths);
|
||||
|
||||
bool querySubstitutablePathInfo(const Path & path,
|
||||
SubstitutablePathInfo & info);
|
||||
void querySubstitutablePathInfos(const PathSet & paths,
|
||||
SubstitutablePathInfos & infos);
|
||||
|
||||
Path addToStore(const Path & srcPath,
|
||||
bool recursive = true, HashType hashAlgo = htSHA256,
|
||||
PathFilter & filter = defaultPathFilter);
|
||||
PathFilter & filter = defaultPathFilter, bool repair = false);
|
||||
|
||||
Path addTextToStore(const string & name, const string & s,
|
||||
const PathSet & references);
|
||||
const PathSet & references, bool repair = false);
|
||||
|
||||
void exportPath(const Path & path, bool sign,
|
||||
Sink & sink);
|
||||
|
||||
Paths importPaths(bool requireSignature, Source & source);
|
||||
|
||||
void buildPaths(const PathSet & paths);
|
||||
void buildPaths(const PathSet & paths, bool repair = false);
|
||||
|
||||
void ensurePath(const Path & path);
|
||||
|
||||
@@ -91,8 +95,6 @@ private:
|
||||
|
||||
void processStderr(Sink * sink = 0, Source * source = 0);
|
||||
|
||||
void forkSlave();
|
||||
|
||||
void connectToDaemon();
|
||||
|
||||
void setOptions();
|
||||
|
||||
+10
-10
@@ -2,7 +2,7 @@
|
||||
#include "globals.hh"
|
||||
#include "util.hh"
|
||||
|
||||
#include <limits.h>
|
||||
#include <climits>
|
||||
|
||||
|
||||
namespace nix {
|
||||
@@ -12,23 +12,23 @@ GCOptions::GCOptions()
|
||||
{
|
||||
action = gcDeleteDead;
|
||||
ignoreLiveness = false;
|
||||
maxFreed = 0;
|
||||
maxFreed = ULLONG_MAX;
|
||||
}
|
||||
|
||||
|
||||
bool isInStore(const Path & path)
|
||||
{
|
||||
return path[0] == '/'
|
||||
&& string(path, 0, nixStore.size()) == nixStore
|
||||
&& path.size() >= nixStore.size() + 2
|
||||
&& path[nixStore.size()] == '/';
|
||||
&& string(path, 0, settings.nixStore.size()) == settings.nixStore
|
||||
&& path.size() >= settings.nixStore.size() + 2
|
||||
&& path[settings.nixStore.size()] == '/';
|
||||
}
|
||||
|
||||
|
||||
bool isStorePath(const Path & path)
|
||||
{
|
||||
return isInStore(path)
|
||||
&& path.find('/', nixStore.size() + 1) == Path::npos;
|
||||
&& path.find('/', settings.nixStore.size() + 1) == Path::npos;
|
||||
}
|
||||
|
||||
|
||||
@@ -43,7 +43,7 @@ Path toStorePath(const Path & path)
|
||||
{
|
||||
if (!isInStore(path))
|
||||
throw Error(format("path `%1%' is not in the Nix store") % path);
|
||||
Path::size_type slash = path.find('/', nixStore.size() + 1);
|
||||
Path::size_type slash = path.find('/', settings.nixStore.size() + 1);
|
||||
if (slash == Path::npos)
|
||||
return path;
|
||||
else
|
||||
@@ -74,7 +74,7 @@ Path followLinksToStorePath(const Path & path)
|
||||
string storePathToName(const Path & path)
|
||||
{
|
||||
assertStorePath(path);
|
||||
return string(path, nixStore.size() + 34);
|
||||
return string(path, settings.nixStore.size() + 34);
|
||||
}
|
||||
|
||||
|
||||
@@ -173,11 +173,11 @@ Path makeStorePath(const string & type,
|
||||
{
|
||||
/* e.g., "source:sha256:1abc...:/nix/store:foo.tar.gz" */
|
||||
string s = type + ":sha256:" + printHash(hash) + ":"
|
||||
+ nixStore + ":" + name;
|
||||
+ settings.nixStore + ":" + name;
|
||||
|
||||
checkStoreName(name);
|
||||
|
||||
return nixStore + "/"
|
||||
return settings.nixStore + "/"
|
||||
+ printHash32(compressHash(hashString(htSHA256, s), 20))
|
||||
+ "-" + name;
|
||||
}
|
||||
|
||||
+28
-21
@@ -48,8 +48,7 @@ struct GCOptions
|
||||
/* For `gcDeleteSpecific', the paths to delete. */
|
||||
PathSet pathsToDelete;
|
||||
|
||||
/* Stop after at least `maxFreed' bytes have been freed. 0 means
|
||||
no limit. */
|
||||
/* Stop after at least `maxFreed' bytes have been freed. */
|
||||
unsigned long long maxFreed;
|
||||
|
||||
GCOptions();
|
||||
@@ -66,13 +65,9 @@ struct GCResults
|
||||
number of bytes that would be or was freed. */
|
||||
unsigned long long bytesFreed;
|
||||
|
||||
/* The number of file system blocks that would be or was freed. */
|
||||
unsigned long long blocksFreed;
|
||||
|
||||
GCResults()
|
||||
{
|
||||
bytesFreed = 0;
|
||||
blocksFreed = 0;
|
||||
}
|
||||
};
|
||||
|
||||
@@ -85,6 +80,8 @@ struct SubstitutablePathInfo
|
||||
unsigned long long narSize; /* 0 = unknown */
|
||||
};
|
||||
|
||||
typedef std::map<Path, SubstitutablePathInfo> SubstitutablePathInfos;
|
||||
|
||||
|
||||
struct ValidPathInfo
|
||||
{
|
||||
@@ -107,20 +104,23 @@ public:
|
||||
|
||||
virtual ~StoreAPI() { }
|
||||
|
||||
/* Checks whether a path is valid. */
|
||||
/* Check whether a path is valid. */
|
||||
virtual bool isValidPath(const Path & path) = 0;
|
||||
|
||||
/* Query the set of valid paths. */
|
||||
virtual PathSet queryValidPaths() = 0;
|
||||
/* Query which of the given paths is valid. */
|
||||
virtual PathSet queryValidPaths(const PathSet & paths) = 0;
|
||||
|
||||
/* Query the set of all valid paths. */
|
||||
virtual PathSet queryAllValidPaths() = 0;
|
||||
|
||||
/* Query information about a valid path. */
|
||||
virtual ValidPathInfo queryPathInfo(const Path & path) = 0;
|
||||
|
||||
/* Queries the hash of a valid path. */
|
||||
/* Query the hash of a valid path. */
|
||||
virtual Hash queryPathHash(const Path & path) = 0;
|
||||
|
||||
/* Queries the set of outgoing FS references for a store path.
|
||||
The result is not cleared. */
|
||||
/* Query the set of outgoing FS references for a store path. The
|
||||
result is not cleared. */
|
||||
virtual void queryReferences(const Path & path,
|
||||
PathSet & references) = 0;
|
||||
|
||||
@@ -133,6 +133,12 @@ public:
|
||||
no deriver has been set. */
|
||||
virtual Path queryDeriver(const Path & path) = 0;
|
||||
|
||||
/* Return all currently valid derivations that have `path' as an
|
||||
output. (Note that the result of `queryDeriver()' is the
|
||||
derivation that was actually used to produce `path', which may
|
||||
not exist anymore.) */
|
||||
virtual PathSet queryValidDerivers(const Path & path) = 0;
|
||||
|
||||
/* Query the outputs of the derivation denoted by `path'. */
|
||||
virtual PathSet queryDerivationOutputs(const Path & path) = 0;
|
||||
|
||||
@@ -143,13 +149,14 @@ public:
|
||||
path, or "" if the path doesn't exist. */
|
||||
virtual Path queryPathFromHashPart(const string & hashPart) = 0;
|
||||
|
||||
/* Query whether a path has substitutes. */
|
||||
virtual bool hasSubstitutes(const Path & path) = 0;
|
||||
/* Query which of the given paths have substitutes. */
|
||||
virtual PathSet querySubstitutablePaths(const PathSet & paths) = 0;
|
||||
|
||||
/* Query the references, deriver and download size of a
|
||||
substitutable path. */
|
||||
virtual bool querySubstitutablePathInfo(const Path & path,
|
||||
SubstitutablePathInfo & info) = 0;
|
||||
/* Query substitute info (i.e. references, derivers and download
|
||||
sizes) of a set of paths. If a path does not have substitute
|
||||
info, it's omitted from the resulting ‘infos’ map. */
|
||||
virtual void querySubstitutablePathInfos(const PathSet & paths,
|
||||
SubstitutablePathInfos & infos) = 0;
|
||||
|
||||
/* Copy the contents of a path to the store and register the
|
||||
validity the resulting path. The resulting path is returned.
|
||||
@@ -157,12 +164,12 @@ public:
|
||||
libutil/archive.hh). */
|
||||
virtual Path addToStore(const Path & srcPath,
|
||||
bool recursive = true, HashType hashAlgo = htSHA256,
|
||||
PathFilter & filter = defaultPathFilter) = 0;
|
||||
PathFilter & filter = defaultPathFilter, bool repair = false) = 0;
|
||||
|
||||
/* Like addToStore, but the contents written to the output path is
|
||||
a regular file containing the given string. */
|
||||
virtual Path addTextToStore(const string & name, const string & s,
|
||||
const PathSet & references) = 0;
|
||||
const PathSet & references, bool repair = false) = 0;
|
||||
|
||||
/* Export a store path, that is, create a NAR dump of the store
|
||||
path and append its references and its deriver. Optionally, a
|
||||
@@ -183,7 +190,7 @@ public:
|
||||
output paths can be created by running the builder, after
|
||||
recursively building any sub-derivations. For inputs that are
|
||||
not derivations, substitute them. */
|
||||
virtual void buildPaths(const PathSet & paths) = 0;
|
||||
virtual void buildPaths(const PathSet & paths, bool repair = false) = 0;
|
||||
|
||||
/* Ensure that a path is valid. If it is not currently valid, it
|
||||
may be made valid by running a substitute (if defined for the
|
||||
|
||||
@@ -6,7 +6,7 @@ namespace nix {
|
||||
#define WORKER_MAGIC_1 0x6e697863
|
||||
#define WORKER_MAGIC_2 0x6478696f
|
||||
|
||||
#define PROTOCOL_VERSION 0x10b
|
||||
#define PROTOCOL_VERSION 0x10d
|
||||
#define GET_PROTOCOL_MAJOR(x) ((x) & 0xff00)
|
||||
#define GET_PROTOCOL_MINOR(x) ((x) & 0x00ff)
|
||||
|
||||
@@ -32,13 +32,17 @@ typedef enum {
|
||||
wopCollectGarbage = 20,
|
||||
wopQuerySubstitutablePathInfo = 21,
|
||||
wopQueryDerivationOutputs = 22,
|
||||
wopQueryValidPaths = 23,
|
||||
wopQueryAllValidPaths = 23,
|
||||
wopQueryFailedPaths = 24,
|
||||
wopClearFailedPaths = 25,
|
||||
wopQueryPathInfo = 26,
|
||||
wopImportPaths = 27,
|
||||
wopQueryDerivationOutputNames = 28,
|
||||
wopQueryPathFromHashPart = 29,
|
||||
wopQuerySubstitutablePathInfos = 30,
|
||||
wopQueryValidPaths = 31,
|
||||
wopQuerySubstitutablePaths = 32,
|
||||
wopQueryValidDerivers = 33,
|
||||
} WorkerOp;
|
||||
|
||||
|
||||
@@ -49,16 +53,8 @@ typedef enum {
|
||||
#define STDERR_ERROR 0x63787470
|
||||
|
||||
|
||||
/* The default location of the daemon socket, relative to nixStateDir.
|
||||
The socket is in a directory to allow you to control access to the
|
||||
Nix daemon by setting the mode/ownership of the directory
|
||||
appropriately. (This wouldn't work on the socket itself since it
|
||||
must be deleted and recreated on startup.) */
|
||||
#define DEFAULT_SOCKET_PATH "/daemon-socket/socket"
|
||||
|
||||
|
||||
Path readStorePath(Source & from);
|
||||
template<class T> T readStorePaths(Source & from);
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
pkglib_LTLIBRARIES = libutil.la
|
||||
|
||||
libutil_la_SOURCES = util.cc hash.cc serialise.cc \
|
||||
archive.cc xml-writer.cc immutable.cc
|
||||
archive.cc xml-writer.cc
|
||||
|
||||
libutil_la_LIBADD = ../boost/format/libformat.la
|
||||
|
||||
pkginclude_HEADERS = util.hh hash.hh serialise.hh \
|
||||
archive.hh xml-writer.hh types.hh immutable.hh
|
||||
archive.hh xml-writer.hh types.hh
|
||||
|
||||
if !HAVE_OPENSSL
|
||||
libutil_la_SOURCES += \
|
||||
|
||||
@@ -153,6 +153,12 @@ string printHash32(const Hash & hash)
|
||||
}
|
||||
|
||||
|
||||
string printHash16or32(const Hash & hash)
|
||||
{
|
||||
return hash.type == htMD5 ? printHash(hash) : printHash32(hash);
|
||||
}
|
||||
|
||||
|
||||
static bool mul(unsigned char * bytes, unsigned char y, int maxSize)
|
||||
{
|
||||
unsigned char carry = 0;
|
||||
|
||||
@@ -54,6 +54,9 @@ unsigned int hashLength32(const Hash & hash);
|
||||
/* Convert a hash to a base-32 representation. */
|
||||
string printHash32(const Hash & hash);
|
||||
|
||||
/* Print a hash in base-16 if it's MD5, or base-32 otherwise. */
|
||||
string printHash16or32(const Hash & hash);
|
||||
|
||||
/* Parse a base-32 representation of a hash code. */
|
||||
Hash parseHash32(HashType ht, const string & s);
|
||||
|
||||
|
||||
@@ -1,67 +0,0 @@
|
||||
#include "config.h"
|
||||
|
||||
#include "immutable.hh"
|
||||
#include "util.hh"
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <fcntl.h>
|
||||
|
||||
#if HAVE_LINUX_FS_H
|
||||
#include <linux/fs.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <errno.h>
|
||||
#endif
|
||||
|
||||
namespace nix {
|
||||
|
||||
|
||||
void changeMutable(const Path & path, bool mut)
|
||||
{
|
||||
#if defined(FS_IOC_SETFLAGS) && defined(FS_IOC_GETFLAGS) && defined(FS_IMMUTABLE_FL)
|
||||
|
||||
/* Don't even try if we're not root. One day we should support
|
||||
the CAP_LINUX_IMMUTABLE capability. */
|
||||
if (getuid() != 0) return;
|
||||
|
||||
/* The O_NOFOLLOW is important to prevent us from changing the
|
||||
mutable bit on the target of a symlink (which would be a
|
||||
security hole). */
|
||||
AutoCloseFD fd = open(path.c_str(), O_RDONLY | O_NOFOLLOW);
|
||||
if (fd == -1) {
|
||||
if (errno == ELOOP) return; // it's a symlink
|
||||
throw SysError(format("opening file `%1%'") % path);
|
||||
}
|
||||
|
||||
unsigned int flags = 0, old;
|
||||
|
||||
/* Silently ignore errors getting/setting the immutable flag so
|
||||
that we work correctly on filesystems that don't support it. */
|
||||
if (ioctl(fd, FS_IOC_GETFLAGS, &flags)) return;
|
||||
|
||||
old = flags;
|
||||
|
||||
if (mut) flags &= ~FS_IMMUTABLE_FL;
|
||||
else flags |= FS_IMMUTABLE_FL;
|
||||
|
||||
if (old == flags) return;
|
||||
|
||||
if (ioctl(fd, FS_IOC_SETFLAGS, &flags)) return;
|
||||
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
void makeImmutable(const Path & path)
|
||||
{
|
||||
changeMutable(path, false);
|
||||
}
|
||||
|
||||
|
||||
void makeMutable(const Path & path)
|
||||
{
|
||||
changeMutable(path, true);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -1,16 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <types.hh>
|
||||
|
||||
namespace nix {
|
||||
|
||||
/* Make the given path immutable, i.e., prevent it from being modified
|
||||
in any way, even by root. This is a no-op on platforms that do not
|
||||
support this, or if the calling user is not privileged. On Linux,
|
||||
this is implemented by doing the equivalent of ‘chattr +i path’. */
|
||||
void makeImmutable(const Path & path);
|
||||
|
||||
/* Make the given path mutable. */
|
||||
void makeMutable(const Path & path);
|
||||
|
||||
}
|
||||
@@ -1,5 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <string>
|
||||
#include <list>
|
||||
#include <set>
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user