Compare commits
158 Commits
sharing-ha
...
state
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6af66436f9 | ||
|
|
b6659d4425 | ||
|
|
ab649814fc | ||
|
|
bffb03eae1 | ||
|
|
2bf4fcb7cd | ||
|
|
a34a198006 | ||
|
|
55b07d65b1 | ||
|
|
4e11da960c | ||
|
|
1164d6a389 | ||
|
|
c28742f633 | ||
|
|
7e0dcc5dcb | ||
|
|
c0dceea9f0 | ||
|
|
588356c30a | ||
|
|
1747d649c5 | ||
|
|
53e31381fa | ||
|
|
2d84c9e50c | ||
|
|
a699c6b330 | ||
|
|
84d00db70b | ||
|
|
138973a6d5 | ||
|
|
d8e9dc2775 | ||
|
|
ef37776094 | ||
|
|
60a32fcbf3 | ||
|
|
0ee803935e | ||
|
|
65ba1f3008 | ||
|
|
16410fc714 | ||
|
|
7d82fd16e9 | ||
|
|
8b31968c61 | ||
|
|
67022b7cca | ||
|
|
a94ea0fd61 | ||
|
|
ca3d96222a | ||
|
|
13b632ca57 | ||
|
|
dacf2e0e87 | ||
|
|
00602dd20c | ||
|
|
546ca6e8bc | ||
|
|
854e155b2c | ||
|
|
8e9c7d9338 | ||
|
|
3800f55b54 | ||
|
|
d69dd855d5 | ||
|
|
43d93e5e64 | ||
|
|
d0458acb7c | ||
|
|
86f0fd8341 | ||
|
|
4c32f38047 | ||
|
|
f435abcdb6 | ||
|
|
51cff21c92 | ||
|
|
315cd18337 | ||
|
|
e80c7bda4c | ||
|
|
ed55982085 | ||
|
|
35e239af33 | ||
|
|
89ab441fd2 | ||
|
|
68cb244c90 | ||
|
|
094c69ad19 | ||
|
|
ec7b0afb08 | ||
|
|
30cf65af26 | ||
|
|
627afcc1aa | ||
|
|
2e7539bd27 | ||
|
|
bdcce95a39 | ||
|
|
53a6b9aaa5 | ||
|
|
05297240ea | ||
|
|
53c907ca09 | ||
|
|
5a9cfdeb6e | ||
|
|
4089bd5f19 | ||
|
|
7424d72098 | ||
|
|
13f321e397 | ||
|
|
696f1fd5e2 | ||
|
|
af8c5697be | ||
|
|
bd25de8d88 | ||
|
|
5e0716bbbb | ||
|
|
7d91f62b71 | ||
|
|
4fb9070fbd | ||
|
|
83ec65edf5 | ||
|
|
856251df03 | ||
|
|
0fc5accd86 | ||
|
|
dc4395b737 | ||
|
|
a07ba681cc | ||
|
|
45bb1ae6a5 | ||
|
|
e3034da88b | ||
|
|
00f39f88f7 | ||
|
|
7f2140d17f | ||
|
|
b46db4dea7 | ||
|
|
c0bd494865 | ||
|
|
b6974f2ae6 | ||
|
|
6392da5f90 | ||
|
|
e33a1e4e74 | ||
|
|
f3dabd6206 | ||
|
|
7bfed0c104 | ||
|
|
96a62bb7e6 | ||
|
|
36b79c7135 | ||
|
|
ba437f451e | ||
|
|
b378df6484 | ||
|
|
fdc2686460 | ||
|
|
b7654ab716 | ||
|
|
9257f16c85 | ||
|
|
bc2fbabc12 | ||
|
|
afb445957d | ||
|
|
9f00b42f38 | ||
|
|
cce4156232 | ||
|
|
ca5fc7c582 | ||
|
|
cc7d4c8bd7 | ||
|
|
40161d0be1 | ||
|
|
0a4a3a1b68 | ||
|
|
4f483aad0f | ||
|
|
eb1f179eac | ||
|
|
9d7438db9f | ||
|
|
c65c296ce0 | ||
|
|
ad2b815b5e | ||
|
|
1c3ec86c39 | ||
|
|
c370c9f535 | ||
|
|
7eb2f61797 | ||
|
|
b32691da2b | ||
|
|
04dd3fdf34 | ||
|
|
22473597ec | ||
|
|
b9fe3f00c1 | ||
|
|
729933062b | ||
|
|
1c0b052243 | ||
|
|
3d22bd50b3 | ||
|
|
c0dcfed3c3 | ||
|
|
6351b7e728 | ||
|
|
51fad07fbd | ||
|
|
0e41b191bf | ||
|
|
a4fda31ad5 | ||
|
|
235c91dd7f | ||
|
|
5164a77aab | ||
|
|
b1cc9e9a45 | ||
|
|
bdecf3bdbc | ||
|
|
5e59387d40 | ||
|
|
df43c1e5b9 | ||
|
|
184443d18d | ||
|
|
bc0af4449a | ||
|
|
76f5c8ba07 | ||
|
|
b909d57f5d | ||
|
|
fe04276aef | ||
|
|
95ce7e04b7 | ||
|
|
267ccc589d | ||
|
|
fd2b8271e4 | ||
|
|
255bf5f04b | ||
|
|
01062b0563 | ||
|
|
79d5604780 | ||
|
|
7166ad8eba | ||
|
|
bcf9d3ab2f | ||
|
|
9c46444641 | ||
|
|
986a50ac78 | ||
|
|
25117fd165 | ||
|
|
653e557e81 | ||
|
|
cbd0d39583 | ||
|
|
fbd1b78a9d | ||
|
|
0a303ea2c0 | ||
|
|
c9e78a973a | ||
|
|
97eb8c32a0 | ||
|
|
86b053dd80 | ||
|
|
73995157e3 | ||
|
|
09b8b7efbc | ||
|
|
802d7f40bd | ||
|
|
5cac336820 | ||
|
|
3fc0b0da58 | ||
|
|
8a7874d77d | ||
|
|
4c63f18dcc | ||
|
|
b712f0f019 | ||
|
|
1a793c60ce |
@@ -46,9 +46,15 @@ else
|
||||
init-state:
|
||||
endif
|
||||
|
||||
init-ext3cow-header-hack:
|
||||
@echo "Symlinking ext3cow header file into src"
|
||||
ln -sf $(ext3cowheader) src/libext3cow/
|
||||
|
||||
svn-revision:
|
||||
svnversion . > svn-revision
|
||||
|
||||
all: init-ext3cow-header-hack
|
||||
|
||||
all-local: NEWS
|
||||
|
||||
NEWS: doc/manual/NEWS.txt
|
||||
|
||||
3
TODO
Normal file
3
TODO
Normal file
@@ -0,0 +1,3 @@
|
||||
- runtimeStateArgs now must be set to someting (or it will see it as a hardcoded path)
|
||||
- import and export of state paths
|
||||
-
|
||||
@@ -1,6 +1,6 @@
|
||||
#! /bin/sh -e
|
||||
mkdir -p config
|
||||
libtoolize --copy
|
||||
libtoolize --force --copy
|
||||
aclocal
|
||||
autoheader
|
||||
automake --add-missing --copy
|
||||
|
||||
40
configure.ac
40
configure.ac
@@ -1,4 +1,4 @@
|
||||
AC_INIT(nix, 0.11)
|
||||
AC_INIT(nix, 0.12)
|
||||
AC_CONFIG_SRCDIR(README)
|
||||
AC_CONFIG_AUX_DIR(config)
|
||||
AM_INIT_AUTOMAKE([dist-bzip2 foreign])
|
||||
@@ -99,9 +99,19 @@ static char buf[1024];]],
|
||||
AC_LANG_POP(C++)
|
||||
|
||||
|
||||
# Check for chroot support (requires chroot() and bind mounts).
|
||||
AC_CHECK_FUNCS([chroot])
|
||||
AC_CHECK_HEADERS([sys/param.h], [], [], [])
|
||||
AC_CHECK_HEADERS([sys/mount.h], [], [],
|
||||
[#ifdef HAVE_SYS_PARAM_H
|
||||
# include <sys/param.h>
|
||||
# endif
|
||||
])
|
||||
|
||||
|
||||
# Check for <locale>
|
||||
AC_LANG_PUSH(C++)
|
||||
AC_CHECK_HEADERS([locale])
|
||||
AC_CHECK_HEADERS([locale], [], [], [])
|
||||
AC_LANG_POP(C++)
|
||||
|
||||
|
||||
@@ -114,7 +124,7 @@ fi
|
||||
])
|
||||
|
||||
NEED_PROG(curl, curl)
|
||||
NEED_PROG(shell, sh)
|
||||
NEED_PROG(shell, bash)
|
||||
NEED_PROG(patch, patch)
|
||||
AC_PATH_PROG(xmllint, xmllint, false)
|
||||
AC_PATH_PROG(xsltproc, xsltproc, false)
|
||||
@@ -125,6 +135,7 @@ AC_PATH_PROG(bison, bison, false)
|
||||
NEED_PROG(perl, perl)
|
||||
NEED_PROG(tar, tar)
|
||||
AC_PATH_PROG(dot, dot)
|
||||
AC_PATH_PROG(dblatex, dblatex)
|
||||
|
||||
AC_PATH_PROG(openssl_prog, openssl, openssl) # if not found, call openssl in $PATH
|
||||
AC_SUBST(openssl_prog)
|
||||
@@ -165,6 +176,11 @@ AC_ARG_WITH(store-dir, AC_HELP_STRING([--with-store-dir=PATH],
|
||||
storedir=$withval, storedir='${prefix}/store')
|
||||
AC_SUBST(storedir)
|
||||
|
||||
AC_ARG_WITH(store-state-dir, AC_HELP_STRING([--with-store-state-dir=PATH],
|
||||
[path of the Nix state store]),
|
||||
storestatedir=$withval, storestatedir='${prefix}/state')
|
||||
AC_SUBST(storestatedir)
|
||||
|
||||
AC_ARG_WITH(bdb, AC_HELP_STRING([--with-bdb=PATH],
|
||||
[prefix of Berkeley DB]),
|
||||
bdb=$withval, bdb=)
|
||||
@@ -179,6 +195,18 @@ fi
|
||||
AC_SUBST(bdb_lib)
|
||||
AC_SUBST(bdb_include)
|
||||
|
||||
AC_ARG_WITH(ext3cow-header, AC_HELP_STRING([--with-ext3cow-header=PATH],
|
||||
[path of the ext3cow header ext3cow_fs.h]),
|
||||
ext3cowheader=$withval, ext3cowheader=)
|
||||
AC_SUBST(ext3cowheader)
|
||||
AC_CHECK_HEADER(${ext3cowheader})
|
||||
|
||||
NEED_PROG(rsync, rsync)
|
||||
AC_ARG_WITH(rsync, AC_HELP_STRING([--with-rsync=PATH],
|
||||
[path to the rsync binary.]),
|
||||
rsync=$withval)
|
||||
AC_SUBST(rsync)
|
||||
|
||||
AC_ARG_WITH(aterm, AC_HELP_STRING([--with-aterm=PATH],
|
||||
[prefix of CWI ATerm library]),
|
||||
aterm=$withval, aterm=)
|
||||
@@ -245,6 +273,10 @@ AM_CONDITIONAL(INIT_STATE, test "$init_state" = "yes")
|
||||
AC_CHECK_FUNCS([setresuid setreuid lchown])
|
||||
|
||||
|
||||
# Nice to have, but not essential.
|
||||
AC_CHECK_FUNCS([strsignal])
|
||||
|
||||
|
||||
# This is needed if ATerm, Berkeley DB or bzip2 are static libraries,
|
||||
# and the Nix libraries are dynamic.
|
||||
if test "$(uname)" = "Darwin"; then
|
||||
@@ -262,7 +294,9 @@ AC_CONFIG_FILES([Makefile
|
||||
src/libutil/Makefile
|
||||
src/libstore/Makefile
|
||||
src/libmain/Makefile
|
||||
src/libext3cow/Makefile
|
||||
src/nix-store/Makefile
|
||||
src/nix-state/Makefile
|
||||
src/nix-hash/Makefile
|
||||
src/libexpr/Makefile
|
||||
src/nix-instantiate/Makefile
|
||||
|
||||
@@ -3,24 +3,39 @@
|
||||
use strict;
|
||||
use Cwd;
|
||||
use IO::Handle;
|
||||
use Fcntl;
|
||||
|
||||
STDOUT->autoflush(1);
|
||||
|
||||
my $out = $ENV{"out"};
|
||||
mkdir "$out", 0755 || die "error creating $out";
|
||||
|
||||
sub readlink_or_StateWrapper;
|
||||
|
||||
my $symlinks = 0;
|
||||
my %path_state_identifier = ();
|
||||
|
||||
my %priorities;
|
||||
|
||||
my $nixBinDir = $ENV{"nixBinDir"};
|
||||
my $nixStore = $ENV{"nixStore"};
|
||||
|
||||
# For each activated package, create symlinks.
|
||||
|
||||
sub createLinks {
|
||||
my $srcDir = shift;
|
||||
|
||||
#Lookup each $stateIdentifiers in $path_state_identifier
|
||||
#we strip $srcDir to its rootdir e.g. /nix/store/......./
|
||||
my @srcDirParts = split /\// , substr($srcDir, length ($nixStore), length ($srcDir));
|
||||
my $srcDirRoot = $nixStore . "/" . $srcDirParts[1];
|
||||
# print "srcDirRoot $srcDirRoot \n";
|
||||
|
||||
my $dstDir = shift;
|
||||
my $priority = shift;
|
||||
my $pkgStateIdentifier = $path_state_identifier{$srcDirRoot}; # We have to look it up each time since recursion can change the $srcDir, but not the identifier
|
||||
|
||||
#print "createLinks $srcDir to $dstDir with iden $pkgStateIdentifier \n";
|
||||
|
||||
my @srcFiles = glob("$srcDir/*");
|
||||
|
||||
@@ -29,20 +44,21 @@ sub createLinks {
|
||||
$baseName =~ s/^.*\///g; # strip directory
|
||||
my $dstFile = "$dstDir/$baseName";
|
||||
|
||||
# Urgh, hacky...
|
||||
if ($srcFile =~ /\/propagated-build-inputs$/ ||
|
||||
# Urgh, hacky...
|
||||
if ($srcFile =~ /\/propagated-build-inputs$/ ||
|
||||
$srcFile =~ /\/nix-support$/ ||
|
||||
$srcFile =~ /\/perllocal.pod$/ ||
|
||||
$srcFile =~ /\/info\/dir$/ ||
|
||||
$srcFile =~ /\/log$/)
|
||||
{
|
||||
# Do nothing.
|
||||
}
|
||||
}
|
||||
|
||||
elsif (-d $srcFile) {
|
||||
|
||||
lstat $dstFile;
|
||||
|
||||
#go recursive on directorys
|
||||
if (-d _) {
|
||||
createLinks($srcFile, $dstFile, $priority);
|
||||
}
|
||||
@@ -53,13 +69,15 @@ sub createLinks {
|
||||
die "collission between directory `$srcFile' and non-directory `$target'";
|
||||
}
|
||||
unlink $dstFile or die "error unlinking `$dstFile': $!";
|
||||
mkdir $dstFile, 0755 ||
|
||||
die "error creating directory `$dstFile': $!";
|
||||
mkdir $dstFile, 0755 || die "error creating directory `$dstFile': $!";
|
||||
createLinks($target, $dstFile, $priorities{$dstFile});
|
||||
createLinks($srcFile, $dstFile, $priority);
|
||||
}
|
||||
|
||||
else {
|
||||
|
||||
#print "1ST DIR LINK $srcFile to $dstFile with iden $pkgStateIdentifier \n";
|
||||
|
||||
symlink($srcFile, $dstFile) ||
|
||||
die "error creating link `$dstFile': $!";
|
||||
$priorities{$dstFile} = $priority;
|
||||
@@ -69,23 +87,67 @@ sub createLinks {
|
||||
|
||||
else {
|
||||
|
||||
if (-l $dstFile) {
|
||||
my $target = readlink $dstFile;
|
||||
my $prevPriority = $priorities{$dstFile};
|
||||
die ( "Collission between `$srcFile' and `$target'. "
|
||||
. "Suggested solution: use `nix-env --set-flag "
|
||||
. "priority NUMBER PKGNAME' to change the priority of "
|
||||
. "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;
|
||||
$symlinks++;
|
||||
}
|
||||
# print "ELSE LINK $srcFile to $dstFile with iden $pkgStateIdentifier \n";
|
||||
|
||||
# if we have a state component with a identifier different then ""
|
||||
if($pkgStateIdentifier ne "__NOSTATE__" && $pkgStateIdentifier ne ""){
|
||||
|
||||
my @pathparts = split /\// , $srcFile;
|
||||
my $parentDir = $pathparts[scalar(@pathparts) - 2];
|
||||
|
||||
if( $parentDir eq "bin" || $parentDir eq "sbin"){ #hacky....
|
||||
|
||||
print "STATELINK $srcFile to $dstFile - $pkgStateIdentifier \n";
|
||||
|
||||
my $new_dstFile;
|
||||
my $new_stateIdentifier;
|
||||
if($pkgStateIdentifier eq "__EMTPY__"){
|
||||
$new_dstFile = $dstFile;
|
||||
$new_stateIdentifier = "";
|
||||
}
|
||||
else{
|
||||
$new_dstFile = "$dstFile-$pkgStateIdentifier";
|
||||
$new_stateIdentifier = $pkgStateIdentifier;
|
||||
}
|
||||
|
||||
# We also check with -e if the wrapperscript-file exists, and if is it a symlink (with -l)
|
||||
if (-l $new_dstFile || -e $new_dstFile) {
|
||||
my $target = readlink_or_StateWrapper $new_dstFile;
|
||||
die "(state) collission between `$srcFile' and `$target' (over $new_dstFile)";
|
||||
}
|
||||
|
||||
sysopen (DSTFILEHANDLE, $new_dstFile, O_RDWR|O_EXCL|O_CREAT, 0755);
|
||||
printf DSTFILEHANDLE "#! @shell@ \n";
|
||||
printf DSTFILEHANDLE "$nixBinDir/nix-state --run --identifier=$new_stateIdentifier $srcFile \"\$@\" \n";
|
||||
close (DSTFILEHANDLE);
|
||||
}
|
||||
|
||||
}
|
||||
elsif($pkgStateIdentifier ne "__NOSTATE__" && $pkgStateIdentifier eq ""){ #TODO we now dont create symlinks for state packages with a empty identifier
|
||||
#TODO but we must do it if there is no normal non-state pacakge
|
||||
}
|
||||
|
||||
else {
|
||||
|
||||
if (-l $dstFile || -e $dstFile) {
|
||||
my $target = readlink_or_StateWrapper $dstFile;
|
||||
my $prevPriority = $priorities{$dstFile};
|
||||
die ( "Collission between `$srcFile' and `$target'. "
|
||||
. "Suggested solution: use `nix-env --set-flag "
|
||||
. "priority NUMBER PKGNAME' to change the priority of "
|
||||
. "one of the conflicting packages.\n" )
|
||||
if $prevPriority == $priority;
|
||||
next if $prevPriority < $priority;
|
||||
unlink $dstFile or die;
|
||||
}
|
||||
|
||||
# print "2ND LINK $srcFile to $dstFile with iden $pkgStateIdentifier \n";
|
||||
symlink($srcFile, $dstFile) ||
|
||||
die "error creating link `$dstFile': $!";
|
||||
$priorities{$dstFile} = $priority;
|
||||
$symlinks++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -116,6 +178,24 @@ sub addPkg {
|
||||
}
|
||||
}
|
||||
|
||||
sub readlink_or_StateWrapper {
|
||||
|
||||
my $src = shift;
|
||||
my $target;
|
||||
|
||||
if (-l $src)
|
||||
{ $target = readlink $src; }
|
||||
else{
|
||||
open(DAT, $src) || die("Could not open file!");
|
||||
my @raw_data=<DAT>;
|
||||
close(DAT);
|
||||
$target = $raw_data[1];
|
||||
}
|
||||
return $target
|
||||
}
|
||||
|
||||
my @stateIdentifiers = split ' ', $ENV{"stateIdentifiers"};
|
||||
my $si_counter = 0;
|
||||
|
||||
# Convert the stuff we get from the environment back into a coherent
|
||||
# data type.
|
||||
@@ -131,15 +211,23 @@ my %pkgs;
|
||||
for (my $n = 0; $n < scalar @paths; $n++) {
|
||||
$pkgs{$paths[$n]} =
|
||||
{ active => $active[$n]
|
||||
, priority => $priority[$n] };
|
||||
, priority => $priority[$n]
|
||||
, stateidentifier => $stateIdentifiers[$n]
|
||||
};
|
||||
|
||||
$path_state_identifier{$paths[$n]} = $stateIdentifiers[$n];
|
||||
}
|
||||
|
||||
|
||||
# Symlink to the packages that have been installed explicitly by the
|
||||
# user.
|
||||
foreach my $pkg (sort (keys %pkgs)) {
|
||||
#print $pkg, " ", $pkgs{$pkg}->{priority}, "\n";
|
||||
|
||||
#print "SP: $pkg \n";
|
||||
#print "SI: $pkgs{$pkg}->{stateidentifier} \n";
|
||||
#print "PR: $pkgs{$pkg}->{priority} \n";
|
||||
|
||||
addPkg($pkg, $pkgs{$pkg}->{priority}) if $pkgs{$pkg}->{active} ne "false";
|
||||
$si_counter++;
|
||||
}
|
||||
|
||||
|
||||
@@ -149,6 +237,7 @@ foreach my $pkg (sort (keys %pkgs)) {
|
||||
# priority in case of collisions.
|
||||
my $priorityCounter = 1000; # don't care about collisions
|
||||
while (scalar(keys %postponed) > 0) {
|
||||
|
||||
my @pkgDirs = keys %postponed;
|
||||
%postponed = ();
|
||||
foreach my $pkgDir (sort @pkgDirs) {
|
||||
@@ -156,8 +245,7 @@ while (scalar(keys %postponed) > 0) {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
print STDERR "created $symlinks symlinks in user environment\n";
|
||||
|
||||
|
||||
symlink($ENV{"manifest"}, "$out/manifest") or die "cannot create manifest";
|
||||
|
||||
|
||||
@@ -1,11 +1,13 @@
|
||||
{system, derivations, manifest}:
|
||||
{system, derivations, stateIdentifiers, manifest, nixBinDir, nixStore}:
|
||||
|
||||
derivation {
|
||||
name = "user-environment";
|
||||
system = system;
|
||||
builder = ./builder.pl;
|
||||
|
||||
stateIdentifiers = stateIdentifiers;
|
||||
manifest = manifest;
|
||||
inherit nixBinDir nixStore;
|
||||
|
||||
# !!! grmbl, need structured data for passing this in a clean way.
|
||||
paths = derivations;
|
||||
|
||||
21
createRelease.sh
Executable file
21
createRelease.sh
Executable file
@@ -0,0 +1,21 @@
|
||||
#! /bin/sh -e
|
||||
|
||||
dir1=releases
|
||||
dir2=nix-state
|
||||
mkdir -p $dir1
|
||||
cd $dir1
|
||||
rm -rf $dir2
|
||||
mkdir -p $dir2
|
||||
cd $dir2
|
||||
svn co https://svn.cs.uu.nl:12443/repos/trace/nix/branches/state ./
|
||||
revision=`svn info | grep ^Revision | sed 's/Revision: //g'`
|
||||
cd ..
|
||||
date=`date +%Y%m%d`
|
||||
tarfile=snix-${date}-rev${revision}.tar.gz
|
||||
tar -cvf $tarfile \
|
||||
--preserve-permissions \
|
||||
--atime-preserve \
|
||||
--gzip \
|
||||
--verbose \
|
||||
--no-ignore-command-error \
|
||||
$dir2/
|
||||
@@ -1 +1 @@
|
||||
SUBDIRS = manual
|
||||
SUBDIRS =
|
||||
|
||||
@@ -20,7 +20,7 @@ man1_MANS = nix-env.1 nix-build.1 nix-store.1 nix-instantiate.1 \
|
||||
FIGURES = figures/user-environments.png
|
||||
|
||||
MANUAL_SRCS = manual.xml introduction.xml installation.xml \
|
||||
package-management.xml writing-nix-expressions.xml \
|
||||
package-management.xml writing-nix-expressions.xml builtins.xml \
|
||||
build-farm.xml \
|
||||
$(man1_MANS:.1=.xml) \
|
||||
troubleshooting.xml bugs.xml opt-common.xml opt-common-syn.xml \
|
||||
@@ -47,6 +47,14 @@ manual.html: $(MANUAL_SRCS) manual.is-valid images
|
||||
$(XSLTPROC) --nonet --xinclude --output manual.html \
|
||||
$(docbookxsl)/html/docbook.xsl manual.xml
|
||||
|
||||
manual.pdf: $(MANUAL_SRCS) manual.is-valid images
|
||||
if test "$(dblatex)" != ""; then \
|
||||
$(dblatex) manual.xml; \
|
||||
else \
|
||||
echo "Please install dblatex and rerun configure."; \
|
||||
exit 1; \
|
||||
fi
|
||||
|
||||
|
||||
NEWS_OPTS = \
|
||||
--stringparam generate.toc "article nop" \
|
||||
|
||||
1
doc/manual/NEWS.txt
Normal file
1
doc/manual/NEWS.txt
Normal file
@@ -0,0 +1 @@
|
||||
New state nix version by wouter ...
|
||||
@@ -36,10 +36,10 @@ build farm, since:
|
||||
builds, and Nix expressions are self-contained.</para></listitem>
|
||||
|
||||
<listitem><para>Nix will only rebuild things that have actually
|
||||
changed. For instance, if the sources of a component haven't
|
||||
changed between runs of the build farm, the component won't be
|
||||
rebuild (unless it was garbage-collected). Also, dependencies
|
||||
typically don't change very often, so they only need to be built
|
||||
changed. For instance, if the sources of a package haven't changed
|
||||
between runs of the build farm, the package won't be rebuilt (unless
|
||||
it was garbage-collected). Also, dependencies typically don't
|
||||
change very often, so they only need to be built
|
||||
once.</para></listitem>
|
||||
|
||||
<listitem><para>The results of a Nix build farm can be made
|
||||
|
||||
760
doc/manual/builtins.xml
Normal file
760
doc/manual/builtins.xml
Normal file
@@ -0,0 +1,760 @@
|
||||
<section xmlns="http://docbook.org/ns/docbook"
|
||||
xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||
xml:id='ssec-builtins'>
|
||||
|
||||
<title>Built-in functions</title>
|
||||
|
||||
|
||||
<para>This section lists the functions and constants built into the
|
||||
Nix expression evaluator. (The built-in function
|
||||
<function>derivation</function> is discussed above.) Some built-ins,
|
||||
such as <function>derivation</function>, are always in scope of every
|
||||
Nix expression; you can just access them right away. But to prevent
|
||||
polluting the namespace too much, most built-ins are not in scope.
|
||||
Instead, you can access them through the <varname>builtins</varname>
|
||||
built-in value, which is an attribute set that contains all built-in
|
||||
functions and values. For instance, <function>derivation</function>
|
||||
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
|
||||
message <replaceable>s</replaceable>.</para></listitem>
|
||||
|
||||
</varlistentry>
|
||||
|
||||
|
||||
<varlistentry><term><function>builtins.add</function>
|
||||
<replaceable>e1</replaceable> <replaceable>e2</replaceable></term>
|
||||
|
||||
<listitem><para>Return the sum of the integers
|
||||
<replaceable>e1</replaceable> and
|
||||
<replaceable>e2</replaceable>.</para></listitem>
|
||||
|
||||
</varlistentry>
|
||||
|
||||
|
||||
<varlistentry><term><function>builtins.attrNames</function>
|
||||
<replaceable>attrs</replaceable></term>
|
||||
|
||||
<listitem><para>Return the names of the attributes in the
|
||||
attribute set <replaceable>attrs</replaceable> in a sorted list.
|
||||
For instance, <literal>builtins.attrNames {y = 1; x =
|
||||
"foo";}</literal> evaluates to <literal>["x" "y"]</literal>.
|
||||
There is no built-in function <function>attrValues</function>, but
|
||||
you can easily define it yourself:
|
||||
|
||||
<programlisting>
|
||||
attrValues = attrs: map (name: builtins.getAttr name attrs) (builtins.attrNames attrs);</programlisting>
|
||||
|
||||
</para></listitem>
|
||||
|
||||
</varlistentry>
|
||||
|
||||
|
||||
<varlistentry><term><function>baseNameOf</function> <replaceable>s</replaceable></term>
|
||||
|
||||
<listitem><para>Return the <emphasis>base name</emphasis> of the
|
||||
string <replaceable>s</replaceable>, that is, everything following
|
||||
the final slash in the string. This is similar to the GNU
|
||||
<command>basename</command> command.</para></listitem>
|
||||
|
||||
</varlistentry>
|
||||
|
||||
|
||||
<varlistentry><term><varname>builtins</varname></term>
|
||||
|
||||
<listitem><para>The attribute set <varname>builtins</varname>
|
||||
contains all the built-in functions and values. You can use
|
||||
<varname>builtins</varname> to test for the availability of
|
||||
features in the Nix installation, e.g.,
|
||||
|
||||
<programlisting>
|
||||
if builtins ? getEnv then builtins.getEnv "PATH" else ""</programlisting>
|
||||
|
||||
This allows a Nix expression to fall back gracefully on older Nix
|
||||
installations that don’t have the desired built-in function.
|
||||
However, in that case you should not write
|
||||
|
||||
<programlisting>
|
||||
if builtins ? getEnv then __getEnv "PATH" else ""</programlisting>
|
||||
|
||||
This Nix expression will trigger an “undefined variable” error on
|
||||
older Nix versions since <function>__getEnv</function> doesn’t
|
||||
exist. <literal>builtins.getEnv</literal>, on the other hand, is
|
||||
safe since <literal>builtins</literal> always exists and attribute
|
||||
selection is lazy, so it’s only performed if the test
|
||||
succeeds.</para></listitem>
|
||||
|
||||
</varlistentry>
|
||||
|
||||
|
||||
<varlistentry
|
||||
xml:id='builtin-currentSystem'><term><varname>builtins.currentSystem</varname></term>
|
||||
|
||||
<listitem><para>The built-in value <varname>currentSystem</varname>
|
||||
evaluates to the Nix platform identifier for the Nix installation
|
||||
on which the expression is being evaluated, such as
|
||||
<literal>"i686-linux"</literal> or
|
||||
<literal>"powerpc-darwin"</literal>.</para></listitem>
|
||||
|
||||
</varlistentry>
|
||||
|
||||
|
||||
<!--
|
||||
<varlistentry><term><function>currentTime</function></term>
|
||||
|
||||
<listitem><para>The built-in value <varname>currentTime</varname>
|
||||
returns the current system time in seconds since 00:00:00 1/1/1970
|
||||
UTC. Due to the evaluation model of Nix expressions
|
||||
(<emphasis>maximal laziness</emphasis>), it always yields the same
|
||||
value within an execution of Nix.</para></listitem>
|
||||
|
||||
</varlistentry>
|
||||
-->
|
||||
|
||||
|
||||
<!--
|
||||
<varlistentry><term><function>dependencyClosure</function></term>
|
||||
|
||||
<listitem><para>TODO</para></listitem>
|
||||
|
||||
</varlistentry>
|
||||
-->
|
||||
|
||||
|
||||
<varlistentry><term><function>derivation</function>
|
||||
<replaceable>attrs</replaceable></term>
|
||||
|
||||
<listitem><para><function>derivation</function> is described in
|
||||
<xref linkend='ssec-derivation' />.</para></listitem>
|
||||
|
||||
</varlistentry>
|
||||
|
||||
|
||||
<varlistentry><term><function>dirOf</function> <replaceable>s</replaceable></term>
|
||||
|
||||
<listitem><para>Return the directory part of the string
|
||||
<replaceable>s</replaceable>, that is, everything before the final
|
||||
slash in the string. This is similar to the GNU
|
||||
<command>dirname</command> command.</para></listitem>
|
||||
|
||||
</varlistentry>
|
||||
|
||||
|
||||
<varlistentry><term><function>builtins.filterSource</function>
|
||||
<replaceable>e1</replaceable> <replaceable>e2</replaceable></term>
|
||||
|
||||
<listitem>
|
||||
|
||||
<para>This function allows you to copy sources into the Nix
|
||||
store while filtering certain files. For instance, suppose that
|
||||
you want to use the directory <filename>source-dir</filename> as
|
||||
an input to a Nix expression, e.g.
|
||||
|
||||
<programlisting>
|
||||
stdenv.mkDerivation {
|
||||
...
|
||||
src = ./source-dir;
|
||||
}
|
||||
</programlisting>
|
||||
|
||||
However, if <filename>source-dir</filename> is a Subversion
|
||||
working copy, then all those annoying <filename>.svn</filename>
|
||||
subdirectories will also be copied to the store. Worse, the
|
||||
contents of those directories may change a lot, causing lots of
|
||||
spurious rebuilds. With <function>filterSource</function> you
|
||||
can filter out the <filename>.svn</filename> directories:
|
||||
|
||||
<programlisting>
|
||||
src = builtins.filterSource
|
||||
(path: type: type != "directory" || baseNameOf path != ".svn")
|
||||
./source-dir;
|
||||
</programlisting>
|
||||
|
||||
</para>
|
||||
|
||||
<para>Thus, the first argument <replaceable>e1</replaceable>
|
||||
must be a predicate function that is called for each regular
|
||||
file, directory or symlink in the source tree
|
||||
<replaceable>e2</replaceable>. If the function returns
|
||||
<literal>true</literal>, the file is copied to the Nix store,
|
||||
otherwise it is omitted. The function is called with two
|
||||
arguments. The first is the full path of the file. The second
|
||||
is a string that identifies the type of the file, which is
|
||||
either <literal>"regular"</literal>,
|
||||
<literal>"directory"</literal>, <literal>"symlink"</literal> or
|
||||
<literal>"unknown"</literal> (for other kinds of files such as
|
||||
device nodes or fifos — but note that those cannot be copied to
|
||||
the Nix store, so if the predicate returns
|
||||
<literal>true</literal> for them, the copy will fail).</para>
|
||||
|
||||
</listitem>
|
||||
|
||||
</varlistentry>
|
||||
|
||||
|
||||
<varlistentry><term><function>builtins.getAttr</function>
|
||||
<replaceable>s</replaceable> <replaceable>attrs</replaceable></term>
|
||||
|
||||
<listitem><para><function>getAttr</function> returns the attribute
|
||||
named <replaceable>s</replaceable> from the attribute set
|
||||
<replaceable>attrs</replaceable>. Evaluation aborts if the
|
||||
attribute doesn’t exist. This is a dynamic version of the
|
||||
<literal>.</literal> operator, since <replaceable>s</replaceable>
|
||||
is an expression rather than an identifier.</para></listitem>
|
||||
|
||||
</varlistentry>
|
||||
|
||||
|
||||
<varlistentry><term><function>builtins.getEnv</function>
|
||||
<replaceable>s</replaceable></term>
|
||||
|
||||
<listitem><para><function>getEnv</function> returns the value of
|
||||
the environment variable <replaceable>s</replaceable>, or an empty
|
||||
string if the variable doesn’t exist. This function should be
|
||||
used with care, as it can introduce all sorts of nasty environment
|
||||
dependencies in your Nix expression.</para>
|
||||
|
||||
<para><function>getEnv</function> is used in Nix Packages to
|
||||
locate the file <filename>~/.nixpkgs/config.nix</filename>, which
|
||||
contains user-local settings for Nix Packages. (That is, it does
|
||||
a <literal>getEnv "HOME"</literal> to locate the user’s home
|
||||
directory.)</para></listitem>
|
||||
|
||||
</varlistentry>
|
||||
|
||||
|
||||
<varlistentry><term><function>builtins.hasAttr</function>
|
||||
<replaceable>s</replaceable> <replaceable>attrs</replaceable></term>
|
||||
|
||||
<listitem><para><function>hasAttr</function> returns
|
||||
<literal>true</literal> if the attribute set
|
||||
<replaceable>attrs</replaceable> has an attribute named
|
||||
<replaceable>s</replaceable>, and <literal>false</literal>
|
||||
otherwise. This is a dynamic version of the <literal>?</literal>
|
||||
operator, since <replaceable>s</replaceable> is an expression
|
||||
rather than an identifier.</para></listitem>
|
||||
|
||||
</varlistentry>
|
||||
|
||||
|
||||
<varlistentry><term><function>builtins.head</function>
|
||||
<replaceable>list</replaceable></term>
|
||||
|
||||
<listitem><para>Return the first element of a list; abort
|
||||
evaluation if the argument isn’t a list or is an empty list. You
|
||||
can test whether a list is empty by comparing it with
|
||||
<literal>[]</literal>.</para></listitem>
|
||||
|
||||
</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
|
||||
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
|
||||
in other files.</para>
|
||||
|
||||
<para>A Nix expression loaded by <function>import</function> must
|
||||
not contain any <emphasis>free variables</emphasis> (identifiers
|
||||
that are not defined in the Nix expression itself and are not
|
||||
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;
|
||||
y = import ./foo.nix;
|
||||
}</programlisting>
|
||||
|
||||
then the following <filename>foo.nix</filename> will give an
|
||||
error:
|
||||
|
||||
<programlisting>
|
||||
x + 456</programlisting>
|
||||
|
||||
since <varname>x</varname> is not in scope in
|
||||
<filename>foo.nix</filename>. If you want <varname>x</varname>
|
||||
to be available in <filename>foo.nix</filename>, you should pass
|
||||
it as a function argument:
|
||||
|
||||
<programlisting>
|
||||
rec {
|
||||
x = 123;
|
||||
y = import ./foo.nix x;
|
||||
}</programlisting>
|
||||
|
||||
and
|
||||
|
||||
<programlisting>
|
||||
x: x + 456</programlisting>
|
||||
|
||||
(The function argument doesn’t have to be called
|
||||
<varname>x</varname> in <filename>foo.nix</filename>; any name
|
||||
would work.)</para></listitem>
|
||||
|
||||
</varlistentry>
|
||||
|
||||
|
||||
<varlistentry><term><function>builtins.isAttrs</function>
|
||||
<replaceable>e</replaceable></term>
|
||||
|
||||
<listitem><para>Return <literal>true</literal> if
|
||||
<replaceable>e</replaceable> evaluates to an attribute set, and
|
||||
<literal>false</literal> otherwise.</para></listitem>
|
||||
|
||||
</varlistentry>
|
||||
|
||||
|
||||
<varlistentry><term><function>builtins.isList</function>
|
||||
<replaceable>e</replaceable></term>
|
||||
|
||||
<listitem><para>Return <literal>true</literal> if
|
||||
<replaceable>e</replaceable> evaluates to a list, and
|
||||
<literal>false</literal> otherwise.</para></listitem>
|
||||
|
||||
</varlistentry>
|
||||
|
||||
|
||||
<varlistentry><term><function>builtins.isFunction</function>
|
||||
<replaceable>e</replaceable></term>
|
||||
|
||||
<listitem><para>Return <literal>true</literal> if
|
||||
<replaceable>e</replaceable> evaluates to a function, and
|
||||
<literal>false</literal> otherwise.</para></listitem>
|
||||
|
||||
</varlistentry>
|
||||
|
||||
|
||||
<varlistentry><term><function>isNull</function>
|
||||
<replaceable>e</replaceable></term>
|
||||
|
||||
<listitem><para>Return <literal>true</literal> if
|
||||
<replaceable>e</replaceable> evaluates to <literal>null</literal>,
|
||||
and <literal>false</literal> otherwise.</para>
|
||||
|
||||
<warning><para>This function is <emphasis>deprecated</emphasis>;
|
||||
just write <literal>e == null</literal> instead.</para></warning>
|
||||
|
||||
</listitem>
|
||||
|
||||
</varlistentry>
|
||||
|
||||
|
||||
<varlistentry><term><function>builtins.lessThan</function>
|
||||
<replaceable>e1</replaceable> <replaceable>e2</replaceable></term>
|
||||
|
||||
<listitem><para>Return <literal>true</literal> if the integer
|
||||
<replaceable>e1</replaceable> is less than the integer
|
||||
<replaceable>e2</replaceable>, and <literal>false</literal>
|
||||
otherwise. Evaluation aborts if either
|
||||
<replaceable>e1</replaceable> or <replaceable>e2</replaceable>
|
||||
does not evaluate to an integer.</para></listitem>
|
||||
|
||||
</varlistentry>
|
||||
|
||||
|
||||
<varlistentry><term><function>builtins.listToAttrs</function>
|
||||
<replaceable>e</replaceable></term>
|
||||
|
||||
<listitem><para>Construct an attribute set from a list specifying
|
||||
the names and values of each attribute. Each element of the list
|
||||
should be an attribute set consisting of a string-valued attribute
|
||||
<varname>name</varname> specifying the name of the attribute, and
|
||||
an attribute <varname>value</varname> specifying its value.
|
||||
Example:
|
||||
|
||||
<programlisting>
|
||||
builtins.listToAttrs [
|
||||
{name = "foo"; value = 123;}
|
||||
{name = "bar"; value = 456;}
|
||||
]
|
||||
</programlisting>
|
||||
|
||||
evaluates to
|
||||
|
||||
<programlisting>
|
||||
{ foo = 123; bar = 456; }
|
||||
</programlisting>
|
||||
|
||||
</para></listitem>
|
||||
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry><term><function>map</function>
|
||||
<replaceable>f</replaceable> <replaceable>list</replaceable></term>
|
||||
|
||||
<listitem><para>Apply the function <replaceable>f</replaceable> to
|
||||
each element in the list <replaceable>list</replaceable>. For
|
||||
example,
|
||||
|
||||
<programlisting>
|
||||
map (x: "foo" + x) ["bar" "bla" "abc"]</programlisting>
|
||||
|
||||
evaluates to <literal>["foobar" "foobla"
|
||||
"fooabc"]</literal>.</para></listitem>
|
||||
|
||||
</varlistentry>
|
||||
|
||||
|
||||
<varlistentry><term><function>builtins.pathExists</function>
|
||||
<replaceable>path</replaceable></term>
|
||||
|
||||
<listitem><para>Return <literal>true</literal> if the path
|
||||
<replaceable>path</replaceable> exists, and
|
||||
<literal>false</literal> otherwise. One application of this
|
||||
function is to conditionally include a Nix expression containing
|
||||
user configuration:
|
||||
|
||||
<programlisting>
|
||||
let
|
||||
fileName = builtins.getEnv "CONFIG_FILE";
|
||||
config =
|
||||
if fileName != "" && builtins.pathExists (builtins.toPath fileName)
|
||||
then import (builtins.toPath fileName)
|
||||
else { someSetting = false; }; <lineannotation># default configuration</lineannotation>
|
||||
in config.someSetting</programlisting>
|
||||
|
||||
(Note that <envar>CONFIG_FILE</envar> must be an absolute path for
|
||||
this to work.)</para></listitem>
|
||||
|
||||
</varlistentry>
|
||||
|
||||
|
||||
<!--
|
||||
<varlistentry><term><function>relativise</function></term>
|
||||
|
||||
<listitem><para>TODO</para></listitem>
|
||||
|
||||
</varlistentry>
|
||||
-->
|
||||
|
||||
|
||||
<varlistentry><term><function>builtins.readFile</function>
|
||||
<replaceable>path</replaceable></term>
|
||||
|
||||
<listitem><para>Return the contents of the file
|
||||
<replaceable>path</replaceable> as a string.</para></listitem>
|
||||
|
||||
</varlistentry>
|
||||
|
||||
|
||||
<varlistentry><term><function>removeAttrs</function>
|
||||
<replaceable>attrs</replaceable> <replaceable>list</replaceable></term>
|
||||
|
||||
<listitem><para>Remove the attributes listed in
|
||||
<replaceable>list</replaceable> from the attribute set
|
||||
<replaceable>attrs</replaceable>. The attributes don’t have to
|
||||
exist in <replaceable>attrs</replaceable>. For instance,
|
||||
|
||||
<screen>
|
||||
removeAttrs { x = 1; y = 2; z = 3; } ["a" "x" "z"]</screen>
|
||||
|
||||
evaluates to <literal>{y = 2;}</literal>.</para></listitem>
|
||||
|
||||
</varlistentry>
|
||||
|
||||
|
||||
<varlistentry><term><function>builtins.stringLength</function>
|
||||
<replaceable>e</replaceable></term>
|
||||
|
||||
<listitem><para>Return the length of the string
|
||||
<replaceable>e</replaceable>. If <replaceable>e</replaceable> is
|
||||
not a string, evaluation is aborted.</para></listitem>
|
||||
|
||||
</varlistentry>
|
||||
|
||||
|
||||
<varlistentry><term><function>builtins.sub</function>
|
||||
<replaceable>e1</replaceable> <replaceable>e2</replaceable></term>
|
||||
|
||||
<listitem><para>Return the difference between the integers
|
||||
<replaceable>e1</replaceable> and
|
||||
<replaceable>e2</replaceable>.</para></listitem>
|
||||
|
||||
</varlistentry>
|
||||
|
||||
|
||||
<varlistentry><term><function>builtins.substring</function>
|
||||
<replaceable>start</replaceable> <replaceable>len</replaceable>
|
||||
<replaceable>s</replaceable></term>
|
||||
|
||||
<listitem><para>Return the substring of
|
||||
<replaceable>s</replaceable> from character position
|
||||
<replaceable>start</replaceable> (zero-based) up to but not
|
||||
including <replaceable>start + len</replaceable>. If
|
||||
<replaceable>start</replaceable> is greater than the length of the
|
||||
string, an empty string is returned, and if <replaceable>start +
|
||||
len</replaceable> lies beyond the end of the string, only the
|
||||
substring up to the end of the string is returned.
|
||||
<replaceable>start</replaceable> must be
|
||||
non-negative.</para></listitem>
|
||||
|
||||
</varlistentry>
|
||||
|
||||
|
||||
<varlistentry><term><function>builtins.tail</function>
|
||||
<replaceable>list</replaceable></term>
|
||||
|
||||
<listitem><para>Return the second to last elements of a list;
|
||||
abort evaluation if the argument isn’t a list or is an empty
|
||||
list.</para></listitem>
|
||||
|
||||
</varlistentry>
|
||||
|
||||
|
||||
<varlistentry><term><function>throw</function>
|
||||
<replaceable>s</replaceable></term>
|
||||
|
||||
<listitem><para>Throw an error message
|
||||
<replaceable>s</replaceable>. This usually aborts Nix expression
|
||||
evaluation, but in <command>nix-env -qa</command> and other
|
||||
commands that try to evaluate a set of derivations to get
|
||||
information about those derivations, a derivation that throws an
|
||||
error is silently skipped (which is not the case for
|
||||
<function>abort</function>).</para></listitem>
|
||||
|
||||
</varlistentry>
|
||||
|
||||
|
||||
<varlistentry
|
||||
xml:id='builtin-toFile'><term><function>builtins.toFile</function>
|
||||
<replaceable>name</replaceable> <replaceable>s</replaceable></term>
|
||||
|
||||
<listitem><para>Store the string <replaceable>s</replaceable> in a
|
||||
file in the Nix store and return its path. The file has suffix
|
||||
<replaceable>name</replaceable>. This file can be used as an
|
||||
input to derivations. One application is to write builders
|
||||
“inline”. For instance, the following Nix expression combines
|
||||
<xref linkend='ex-hello-nix' /> and <xref
|
||||
linkend='ex-hello-builder' /> into one file:
|
||||
|
||||
<programlisting>
|
||||
{stdenv, fetchurl, perl}:
|
||||
|
||||
stdenv.mkDerivation {
|
||||
name = "hello-2.1.1";
|
||||
|
||||
builder = builtins.toFile "builder.sh" "
|
||||
source $stdenv/setup
|
||||
|
||||
PATH=$perl/bin:$PATH
|
||||
|
||||
tar xvfz $src
|
||||
cd hello-*
|
||||
./configure --prefix=$out
|
||||
make
|
||||
make install
|
||||
";
|
||||
|
||||
src = fetchurl {
|
||||
url = http://nix.cs.uu.nl/dist/tarballs/hello-2.1.1.tar.gz;
|
||||
md5 = "70c9ccf9fac07f762c24f2df2290784d";
|
||||
};
|
||||
inherit perl;
|
||||
}</programlisting>
|
||||
|
||||
</para>
|
||||
|
||||
<para>It is even possible for one file to refer to another, e.g.,
|
||||
|
||||
<programlisting>
|
||||
builder = let
|
||||
configFile = builtins.toFile "foo.conf" "
|
||||
# This is some dummy configuration file.
|
||||
<replaceable>...</replaceable>
|
||||
";
|
||||
in builtins.toFile "builder.sh" "
|
||||
source $stdenv/setup
|
||||
<replaceable>...</replaceable>
|
||||
cp ${configFile} $out/etc/foo.conf
|
||||
";</programlisting>
|
||||
|
||||
Note that <literal>${configFile}</literal> is an antiquotation
|
||||
(see <xref linkend='ssec-values' />), so the result of the
|
||||
expression <literal>configFile</literal> (i.e., a path like
|
||||
<filename>/nix/store/m7p7jfny445k...-foo.conf</filename>) will be
|
||||
spliced into the resulting string.</para>
|
||||
|
||||
<para>It is however <emphasis>not</emphasis> allowed to have files
|
||||
mutually referring to each other, like so:
|
||||
|
||||
<programlisting>
|
||||
let
|
||||
foo = builtins.toFile "foo" "...${bar}...";
|
||||
bar = builtins.toFile "bar" "...${foo}...";
|
||||
in foo</programlisting>
|
||||
|
||||
This is not allowed because it would cause a cyclic dependency in
|
||||
the computation of the cryptographic hashes for
|
||||
<varname>foo</varname> and <varname>bar</varname>.</para></listitem>
|
||||
|
||||
</varlistentry>
|
||||
|
||||
|
||||
<varlistentry><term><function>builtins.toPath</function> <replaceable>s</replaceable></term>
|
||||
|
||||
<listitem><para>Convert the string value
|
||||
<replaceable>s</replaceable> into a path value. The string
|
||||
<replaceable>s</replaceable> must represent an absolute path
|
||||
(i.e., must start with <literal>/</literal>). The path need not
|
||||
exist. The resulting path is canonicalised, e.g.,
|
||||
<literal>builtins.toPath "//foo/xyzzy/../bar/"</literal> returns
|
||||
<literal>/foo/bar</literal>.</para></listitem>
|
||||
|
||||
</varlistentry>
|
||||
|
||||
|
||||
<varlistentry><term><function>toString</function> <replaceable>e</replaceable></term>
|
||||
|
||||
<listitem><para>Convert the expression
|
||||
<replaceable>e</replaceable> to a string.
|
||||
<replaceable>e</replaceable> can be a string (in which case
|
||||
<function>toString</function> is a no-op) or a path (e.g.,
|
||||
<literal>toString /foo/bar</literal> yields
|
||||
<literal>"/foo/bar"</literal>.</para></listitem>
|
||||
|
||||
</varlistentry>
|
||||
|
||||
|
||||
<varlistentry xml:id='builtin-toXML'><term><function>builtins.toXML</function> <replaceable>e</replaceable></term>
|
||||
|
||||
<listitem><para>Return a string containing an XML representation
|
||||
of <replaceable>e</replaceable>. The main application for
|
||||
<function>toXML</function> is to communicate information with the
|
||||
builder in a more structured format than plain environment
|
||||
variables.</para>
|
||||
|
||||
<!-- TODO: more formally describe the schema of the XML
|
||||
representation -->
|
||||
|
||||
<para><xref linkend='ex-toxml' /> shows an example where this is
|
||||
the case. The builder is supposed to generate the configuration
|
||||
file for a <link xlink:href='http://jetty.mortbay.org/'>Jetty
|
||||
servlet container</link>. A servlet container contains a number
|
||||
of servlets (<filename>*.war</filename> files) each exported under
|
||||
a specific URI prefix. So the servlet configuration is a list of
|
||||
attribute sets containing the <varname>path</varname> and
|
||||
<varname>war</varname> of the servlet (<xref
|
||||
linkend='ex-toxml-co-servlets' />). This kind of information is
|
||||
difficult to communicate with the normal method of passing
|
||||
information through an environment variable, which just
|
||||
concatenates everything together into a string (which might just
|
||||
work in this case, but wouldn’t work if fields are optional or
|
||||
contain lists themselves). Instead the Nix expression is
|
||||
converted to an XML representation with
|
||||
<function>toXML</function>, which is unambiguous and can easily be
|
||||
processed with the appropriate tools. For instance, in the
|
||||
example an XSLT stylesheet (<xref linkend='ex-toxml-co-stylesheet'
|
||||
/>) is applied to it (<xref linkend='ex-toxml-co-apply' />) to
|
||||
generate the XML configuration file for the Jetty server. The XML
|
||||
representation produced from <xref linkend='ex-toxml-co-servlets'
|
||||
/> by <function>toXML</function> is shown in <xref
|
||||
linkend='ex-toxml-result' />.</para>
|
||||
|
||||
<para>Note that <xref linkend='ex-toxml' /> uses the <function
|
||||
linkend='builtin-toFile'>toFile</function> built-in to write the
|
||||
builder and the stylesheet “inline” in the Nix expression. The
|
||||
path of the stylesheet is spliced into the builder at
|
||||
<literal>xsltproc ${stylesheet}
|
||||
<replaceable>...</replaceable></literal>.</para>
|
||||
|
||||
<example xml:id='ex-toxml'><title>Passing information to a builder
|
||||
using <function>toXML</function></title>
|
||||
|
||||
<programlisting><![CDATA[
|
||||
{stdenv, fetchurl, libxslt, jira, uberwiki}:
|
||||
|
||||
stdenv.mkDerivation (rec {
|
||||
name = "web-server";
|
||||
|
||||
buildInputs = [libxslt];
|
||||
|
||||
builder = builtins.toFile "builder.sh" "
|
||||
source $stdenv/setup
|
||||
mkdir $out
|
||||
echo $servlets | xsltproc ${stylesheet} - > $out/server-conf.xml]]> <co xml:id='ex-toxml-co-apply' /> <![CDATA[
|
||||
";
|
||||
|
||||
stylesheet = builtins.toFile "stylesheet.xsl"]]> <co xml:id='ex-toxml-co-stylesheet' /> <![CDATA[
|
||||
"<?xml version='1.0' encoding='UTF-8'?>
|
||||
<xsl:stylesheet xmlns:xsl='http://www.w3.org/1999/XSL/Transform' version='1.0'>
|
||||
<xsl:template match='/'>
|
||||
<Configure>
|
||||
<xsl:for-each select='/expr/list/attrs'>
|
||||
<Call name='addWebApplication'>
|
||||
<Arg><xsl:value-of select=\"attr[@name = 'path']/string/@value\" /></Arg>
|
||||
<Arg><xsl:value-of select=\"attr[@name = 'war']/path/@value\" /></Arg>
|
||||
</Call>
|
||||
</xsl:for-each>
|
||||
</Configure>
|
||||
</xsl:template>
|
||||
</xsl:stylesheet>
|
||||
";
|
||||
|
||||
servlets = builtins.toXML []]> <co xml:id='ex-toxml-co-servlets' /> <![CDATA[
|
||||
{ path = "/bugtracker"; war = jira + "/lib/atlassian-jira.war"; }
|
||||
{ path = "/wiki"; war = uberwiki + "/uberwiki.war"; }
|
||||
];
|
||||
})]]></programlisting>
|
||||
|
||||
</example>
|
||||
|
||||
<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>
|
||||
<attrs>
|
||||
<attr name="path">
|
||||
<string value="/bugtracker" />
|
||||
</attr>
|
||||
<attr name="war">
|
||||
<path value="/nix/store/d1jh9pasa7k2...-jira/lib/atlassian-jira.war" />
|
||||
</attr>
|
||||
</attrs>
|
||||
<attrs>
|
||||
<attr name="path">
|
||||
<string value="/wiki" />
|
||||
</attr>
|
||||
<attr name="war">
|
||||
<path value="/nix/store/y6423b1yi4sx...-uberwiki/uberwiki.war" />
|
||||
</attr>
|
||||
</attrs>
|
||||
</list>
|
||||
</expr>]]></programlisting>
|
||||
|
||||
</example>
|
||||
|
||||
</listitem>
|
||||
|
||||
</varlistentry>
|
||||
|
||||
|
||||
<varlistentry><term><function>builtins.trace</function>
|
||||
<replaceable>e1</replaceable> <replaceable>e2</replaceable></term>
|
||||
|
||||
<listitem><para>Evaluate <replaceable>e1</replaceable> and print its
|
||||
abstract syntax representation on standard error. Then return
|
||||
<replaceable>e2</replaceable>. This function is useful for
|
||||
debugging.</para></listitem>
|
||||
|
||||
</varlistentry>
|
||||
|
||||
|
||||
</variablelist>
|
||||
|
||||
|
||||
</section>
|
||||
@@ -118,6 +118,123 @@ env-keep-derivations = false
|
||||
</varlistentry>
|
||||
|
||||
|
||||
<varlistentry xml:id="conf-build-max-silent-time"><term><literal>build-max-silent-time</literal></term>
|
||||
|
||||
<listitem>
|
||||
|
||||
<para>This option defines the maximum number of seconds that a
|
||||
builder can go without producing any data on standard output or
|
||||
standard error. This is useful (for instance in a automated
|
||||
build system) to catch builds that are stuck in an infinite
|
||||
loop, or to catch remote builds that are hanging due to network
|
||||
problems. It can be overriden using the <option
|
||||
linkend="opt-max-silent-time">--max-silent-time</option> command
|
||||
line switch.</para>
|
||||
|
||||
<para>The value <literal>0</literal> means that there is no
|
||||
timeout. This is also the default.</para>
|
||||
|
||||
</listitem>
|
||||
|
||||
</varlistentry>
|
||||
|
||||
|
||||
<varlistentry xml:id="conf-build-users-group"><term><literal>build-users-group</literal></term>
|
||||
|
||||
<listitem><para>This options specifies the Unix group containing
|
||||
the Nix build user accounts. In multi-user Nix installations,
|
||||
builds should not be performed by the Nix account since that would
|
||||
allow users to arbitrarily modify the Nix store and database by
|
||||
supplying specially crafted builders; and they cannot be performed
|
||||
by the calling user since that would allow him/her to influence
|
||||
the build result.</para>
|
||||
|
||||
<para>Therefore, if this option is non-empty and specifies a valid
|
||||
group, builds will be performed under the user accounts that are a
|
||||
member of the group specified here (as listed in
|
||||
<filename>/etc/group</filename>). Those user accounts should not
|
||||
be used for any other purpose!</para>
|
||||
|
||||
<para>Nix will never run two builds under the same user account at
|
||||
the same time. This is to prevent an obvious security hole: a
|
||||
malicious user writing a Nix expression that modifies the build
|
||||
result of a legitimate Nix expression being built by another user.
|
||||
Therefore it is good to have as many Nix build user accounts as
|
||||
you can spare. (Remember: uids are cheap.)</para>
|
||||
|
||||
<para>The build users should have permission to create files in
|
||||
the Nix store, but not delete them. Therefore,
|
||||
<filename>/nix/store</filename> should be owned by the Nix
|
||||
account, its group should be the group specified here, and its
|
||||
mode should be <literal>1775</literal>.</para>
|
||||
|
||||
<para>If the build users group is empty, builds will be performed
|
||||
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>
|
||||
|
||||
</listitem>
|
||||
|
||||
</varlistentry>
|
||||
|
||||
|
||||
<varlistentry><term><literal>build-use-chroot</literal></term>
|
||||
|
||||
<listitem><para>If set to <literal>true</literal>, builds will be
|
||||
performed in a <emphasis>chroot environment</emphasis>, i.e., the
|
||||
build will be isolated from the normal file system hierarchy and
|
||||
will only see the Nix store, the temporary build directory, and
|
||||
the directories configured with the <link
|
||||
linkend='conf-build-chroot-dirs'><literal>build-chroot-dirs</literal>
|
||||
option</link> (such as <filename>/proc</filename> and
|
||||
<filename>/dev</filename>). This is useful to prevent undeclared
|
||||
dependencies on files in directories such as
|
||||
<filename>/usr/bin</filename>.</para>
|
||||
|
||||
<para>The use of a chroot requires that Nix is run as root (but
|
||||
you can still use the <link
|
||||
linkend='conf-build-users-group'>“build users” feature</link> to
|
||||
perform builds under different users than root). Currently,
|
||||
chroot builds only work on Linux because Nix uses “bind mounts” to
|
||||
make the Nix store and other directories available inside the
|
||||
chroot.</para>
|
||||
|
||||
</listitem>
|
||||
|
||||
</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>
|
||||
|
||||
<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>
|
||||
|
||||
</varlistentry>
|
||||
|
||||
|
||||
<varlistentry><term><literal>system</literal></term>
|
||||
|
||||
<listitem><para>This option specifies the canonical Nix system
|
||||
|
||||
@@ -263,6 +263,17 @@ $ mount -o bind /mnt/otherdisk/nix /nix</screen>
|
||||
</varlistentry>
|
||||
|
||||
|
||||
<varlistentry xml:id="envar-remote"><term><envar>NIX_REMOTE</envar></term>
|
||||
|
||||
<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
|
||||
linkend="ssec-multi-user">multi-user Nix installations</link>.
|
||||
Otherwise, it should be left unset.</para></listitem>
|
||||
|
||||
</varlistentry>
|
||||
|
||||
|
||||
</variablelist>
|
||||
|
||||
|
||||
|
||||
@@ -74,9 +74,9 @@
|
||||
|
||||
<glossentry><glossterm>Nix expression</glossterm>
|
||||
|
||||
<glossdef><para>A high-level description of software components and
|
||||
<glossdef><para>A high-level description of software packages and
|
||||
compositions thereof. Deploying software using Nix entails writing
|
||||
Nix expressions for your components. Nix expressions are translated
|
||||
Nix expressions for your packages. Nix expressions are translated
|
||||
to derivations that are stored in the Nix store. These derivations
|
||||
can then be built.</para></glossdef>
|
||||
|
||||
|
||||
@@ -42,9 +42,8 @@ platforms as well.</para>
|
||||
<section><title>Obtaining Nix</title>
|
||||
|
||||
<para>The easiest way to obtain Nix is to download a <link
|
||||
xlink:href="http://www.cs.uu.nl/groups/ST/Trace/Nix">source
|
||||
distribution</link>. RPMs for Red Hat, SuSE, and Fedora Core are also
|
||||
available.</para>
|
||||
xlink:href="http://nix.cs.uu.nl/">source distribution</link>. RPMs
|
||||
for Red Hat, SuSE, and Fedora Core are also available.</para>
|
||||
|
||||
<para>Alternatively, the most recent sources of Nix can be obtained
|
||||
from its <link
|
||||
@@ -100,14 +99,16 @@ ubiquitous 2.5.4a won't. Note that these are only required if you
|
||||
modify the parser or when you are building from the Subversion
|
||||
repository.</para>
|
||||
|
||||
<para>Nix uses Sleepycat's Berkeley DB and CWI's ATerm library. These
|
||||
are included in the Nix source distribution. If you build from the
|
||||
Subversion repository, you must download them yourself and place them
|
||||
in the <filename>externals/</filename> directory. See
|
||||
<para>Nix uses Sleepycat's Berkeley DB, CWI's ATerm library and the
|
||||
bzip2 compressor (including the bzip2 library). These are included in
|
||||
the Nix source distribution. If you build from the Subversion
|
||||
repository, you must download them yourself and place them in the
|
||||
<filename>externals/</filename> directory. See
|
||||
<filename>externals/Makefile.am</filename> for the precise URLs of
|
||||
these packages. Alternatively, if you already have them installed,
|
||||
you can use <command>configure</command>'s <option>--with-bdb</option>
|
||||
and <option>--with-aterm</option> options to point to their respective
|
||||
you can use <command>configure</command>'s
|
||||
<option>--with-bdb</option>, <option>--with-aterm</option> and
|
||||
<option>--with-bzip2</option> options to point to their respective
|
||||
locations. Note that Berkeley DB <emphasis>must</emphasis> be version
|
||||
4.5; other versions may not have compatible database formats.</para>
|
||||
|
||||
@@ -118,19 +119,21 @@ locations. Note that Berkeley DB <emphasis>must</emphasis> be version
|
||||
|
||||
<para>After unpacking or checking out the Nix sources, issue the
|
||||
following commands:
|
||||
</para>
|
||||
|
||||
<screen>
|
||||
$ ./configure <replaceable>options...</replaceable>
|
||||
$ make
|
||||
$ make install</screen>
|
||||
|
||||
</para>
|
||||
|
||||
<para>When building from the Subversion repository, these should be
|
||||
preceded by the command:
|
||||
</para>
|
||||
|
||||
<screen>
|
||||
$ ./boostrap</screen>
|
||||
$ ./bootstrap</screen>
|
||||
|
||||
</para>
|
||||
|
||||
<para>The installation path can be specified by passing the
|
||||
<option>--prefix=<replaceable>prefix</replaceable></option> to
|
||||
@@ -157,28 +160,32 @@ options.</para>
|
||||
|
||||
<section><title>Installing from RPMs</title>
|
||||
|
||||
<para>RPM packages of Nix can be downloaded from <uri
|
||||
xlink:href="http://www.cs.uu.nl/groups/ST/Trace/Nix">http://www.cs.uu.nl/groups/ST/Trace/Nix</uri>.
|
||||
These RPMs should work for most fairly recent releases of SuSE and Red
|
||||
Hat Linux. They have been known to work work on SuSE Linux 8.1 and
|
||||
9.0, and Red Hat 9.0. In fact, it should work on any RPM-based Linux
|
||||
distribution based on <literal>glibc</literal> 2.3 or later.</para>
|
||||
<para>RPM packages of Nix can be downloaded from <link
|
||||
xlink:href="http://nix.cs.uu.nl/" />. These RPMs should work for most
|
||||
fairly recent releases of SuSE and Red Hat Linux. They have been
|
||||
known to work work on SuSE Linux 8.1 and 9.0, and Red Hat 9.0. In
|
||||
fact, it should work on any RPM-based Linux distribution based on
|
||||
<literal>glibc</literal> 2.3 or later.</para>
|
||||
|
||||
<para>Once downloaded, the RPMs can be installed or upgraded using
|
||||
<command>rpm -U</command>. For example,</para>
|
||||
<command>rpm -U</command>. For example,
|
||||
|
||||
<screen>
|
||||
$ rpm -U nix-0.5pre664-1.i386.rpm</screen>
|
||||
|
||||
</para>
|
||||
|
||||
<para>The RPMs install into the directory <filename>/nix</filename>.
|
||||
Nix can be uninstalled using <command>rpm -e nix</command>. After
|
||||
this it will be necessary to manually remove the Nix store and other
|
||||
auxiliary data:</para>
|
||||
auxiliary data:
|
||||
|
||||
<screen>
|
||||
$ rm -rf /nix/store
|
||||
$ rm -rf /nix/var</screen>
|
||||
|
||||
</para>
|
||||
|
||||
</section>
|
||||
|
||||
|
||||
@@ -187,7 +194,7 @@ $ rm -rf /nix/var</screen>
|
||||
<para>You can install the latest stable version of Nix through Nix
|
||||
itself by subscribing to the channel <link
|
||||
xlink:href="http://nix.cs.uu.nl/dist/nix/channels-v3/nix-stable" />,
|
||||
or the latest unstable version by subscribing to the channel<link
|
||||
or the latest unstable version by subscribing to the channel <link
|
||||
xlink:href="http://nix.cs.uu.nl/dist/nix/channels-v3/nix-unstable" />.
|
||||
You can also do a <link linkend="sec-one-click">one-click
|
||||
installation</link> by clicking on the package links at <link
|
||||
@@ -231,33 +238,215 @@ class="username">root</systemitem> all the time.</para>
|
||||
</section>
|
||||
|
||||
|
||||
<section><title>Multi-user mode</title>
|
||||
<section xml:id="ssec-multi-user"><title>Multi-user mode</title>
|
||||
|
||||
<para></para>
|
||||
<para>To allow a Nix store to be shared safely among multiple users,
|
||||
it is important that users are not able to run builders that modify
|
||||
the Nix store or database in arbitrary ways, or that interfere with
|
||||
builds started by other users. If they could do so, they could
|
||||
install a Trojan horse in some package and compromise the accounts of
|
||||
other users.</para>
|
||||
|
||||
|
||||
<!--
|
||||
|
||||
warning: the nix-builders group should contain *only* the Nix
|
||||
builders, and nothing else. If the Nix account is compromised, you
|
||||
can execute programs under the accounts in the nix-builders group, so
|
||||
it obviously shouldn’t contain any “real” user accounts. So don’t use
|
||||
an existing group like <literal>users</literal> — just create a new
|
||||
one.
|
||||
|
||||
-->
|
||||
<para>To prevent this, the Nix store and database are owned by some
|
||||
privileged user (usually <literal>root</literal>) and builders are
|
||||
executed under special user accounts (usually named
|
||||
<literal>nixbld1</literal>, <literal>nixbld2</literal>, etc.). When a
|
||||
unprivileged user runs a Nix command, actions that operate on the Nix
|
||||
store (such as builds) are forwarded to a <emphasis>Nix
|
||||
daemon</emphasis> running under the owner of the Nix store/database
|
||||
that performs the operation.</para>
|
||||
|
||||
<note><para>Multi-user mode has one important limitation: only
|
||||
<systemitem class="username">root</systemitem> can run <command
|
||||
linkend="sec-nix-pull">nix-pull</command> to register the availability
|
||||
of pre-built binaries. However, those registrations
|
||||
<emphasis>are</emphasis> used by all users to speed up
|
||||
builds.</para></note>
|
||||
of pre-built binaries. However, those registrations are shared by all
|
||||
users, so they still get the benefit from <command>nix-pull</command>s
|
||||
done by <systemitem class="username">root</systemitem>.</para></note>
|
||||
|
||||
|
||||
<section><title>Setting up the build users</title>
|
||||
|
||||
<para>The <emphasis>build users</emphasis> are the special UIDs under
|
||||
which builds are performed. They should all be members of the
|
||||
<emphasis>build users group</emphasis> (usually called
|
||||
<literal>nixbld</literal>). This group should have no other members.
|
||||
The build users should not be members of any other group.</para>
|
||||
|
||||
<para>Here is a typical <filename>/etc/group</filename> definition of
|
||||
the build users group with 10 build users:
|
||||
|
||||
<programlisting>
|
||||
nixbld:!:30000:nixbld1,nixbld2,nixbld3,nixbld4,nixbld5,nixbld6,nixbld7,nixbld8,nixbld9,nixbld10
|
||||
</programlisting>
|
||||
|
||||
In this example the <literal>nixbld</literal> group has UID 30000, but
|
||||
of course it can be anything that doesn’t collide with an existing
|
||||
group.</para>
|
||||
|
||||
<para>Here is the corresponding part of
|
||||
<filename>/etc/passwd</filename>:
|
||||
|
||||
<programlisting>
|
||||
nixbld1:x:30001:65534:Nix build user 1:/var/empty:/noshell
|
||||
nixbld2:x:30002:65534:Nix build user 2:/var/empty:/noshell
|
||||
nixbld3:x:30003:65534:Nix build user 3:/var/empty:/noshell
|
||||
...
|
||||
nixbld10:x:30010:65534:Nix build user 10:/var/empty:/noshell
|
||||
</programlisting>
|
||||
|
||||
The home directory of the build users should not exist or should be an
|
||||
empty directory to which they do not have write access.</para>
|
||||
|
||||
<para>The build users should have write access to the Nix store, but
|
||||
they should not have the right to delete files. Thus the Nix store’s
|
||||
group should be the build users group, and it should have the sticky
|
||||
bit turned on (like <filename>/tmp</filename>):
|
||||
|
||||
<screen>
|
||||
$ chgrp nixbld /nix/store
|
||||
$ chmod 1777 /nix/store
|
||||
</screen>
|
||||
|
||||
</para>
|
||||
|
||||
<para>Finally, you should tell Nix to use the build users by
|
||||
specifying the build users group in the <link
|
||||
linkend="conf-build-users-group"><literal>build-users-group</literal>
|
||||
option</link> in the <link linkend="sec-conf-file">Nix configuration
|
||||
file</link> (<literal>/nix/etc/nix/nix.conf</literal>):
|
||||
|
||||
<programlisting>
|
||||
build-users-group = nixbld
|
||||
</programlisting>
|
||||
|
||||
</para>
|
||||
|
||||
</section>
|
||||
|
||||
|
||||
</section> <!-- end of permissions section -->
|
||||
<section><title>Nix store/database owned by root</title>
|
||||
|
||||
<para>The simplest setup is to let <literal>root</literal> own the Nix
|
||||
store and database. I.e.,
|
||||
|
||||
<screen>
|
||||
$ chown -R root /nix/store /nix/var/nix</screen>
|
||||
|
||||
</para>
|
||||
|
||||
<para>The Nix daemon should be started as follows (as
|
||||
<literal>root</literal>):
|
||||
|
||||
<screen>
|
||||
$ nix-worker --daemon</screen>
|
||||
|
||||
You’ll want to put that line somewhere in your system’s boot
|
||||
scripts.</para>
|
||||
|
||||
<para>To let unprivileged users use the daemon, they should set the
|
||||
<link linkend="envar-remote"><envar>NIX_REMOTE</envar> environment
|
||||
variable</link> to <literal>daemon</literal>. So you should put a
|
||||
line like
|
||||
|
||||
<programlisting>
|
||||
export NIX_REMOTE=daemon</programlisting>
|
||||
|
||||
into the users’ login scripts.</para>
|
||||
|
||||
</section>
|
||||
|
||||
|
||||
<section><title>Nix store/database not owned by root</title>
|
||||
|
||||
<para>It is also possible to let the Nix store and database be owned
|
||||
by a non-root user, which should be more secure<footnote><para>Note
|
||||
however that even when the Nix daemon runs as root, not
|
||||
<emphasis>that</emphasis> much code is executed as root: Nix
|
||||
expression evaluation is performed by the calling (unprivileged) user,
|
||||
and builds are performed under the special build user accounts. So
|
||||
only the code that accesses the database and starts builds is executed
|
||||
as <literal>root</literal>.</para></footnote>. Typically, this user
|
||||
is a special account called <literal>nix</literal>, but it can be
|
||||
named anything. It should own the Nix store and database:
|
||||
|
||||
<screen>
|
||||
$ chown -R root /nix/store /nix/var/nix</screen>
|
||||
|
||||
and of course <command>nix-worker --daemon</command> should be started
|
||||
under that user, e.g.,
|
||||
|
||||
<screen>
|
||||
$ su - nix -c "exec /nix/bin/nix-worker --daemon"</screen>
|
||||
|
||||
</para>
|
||||
|
||||
<para>There is a catch, though: non-<literal>root</literal> users
|
||||
cannot start builds under the build user accounts, since the
|
||||
<function>setuid</function> system call is obviously privileged. To
|
||||
allow a non-<literal>root</literal> Nix daemon to use the build user
|
||||
feature, it calls a setuid-root helper program,
|
||||
<command>nix-setuid-helper</command>. This program is installed in
|
||||
<filename><replaceable>prefix</replaceable>/libexec/nix-setuid-helper</filename>.
|
||||
To set the permissions properly (Nix’s <command>make install</command>
|
||||
doesn’t do this, since we don’t want to ship setuid-root programs
|
||||
out-of-the-box):
|
||||
|
||||
<screen>
|
||||
$ chown root.root /nix/libexec/nix-setuid-helper
|
||||
$ chmod 4755 /nix/libexec/nix-setuid-helper
|
||||
</screen>
|
||||
|
||||
(This example assumes that the Nix binaries are installed in
|
||||
<filename>/nix</filename>.)</para>
|
||||
|
||||
<para>Of course, the <command>nix-setuid-helper</command> command
|
||||
should not be usable by just anybody, since then anybody could run
|
||||
commands under the Nix build user accounts. For that reason there is
|
||||
a configuration file <filename>/etc/nix-setuid.conf</filename> that
|
||||
restricts the use of the helper. This file should be a text file
|
||||
containing precisely two lines, the first being the Nix daemon user
|
||||
and the second being the build users group, e.g.,
|
||||
|
||||
<programlisting>
|
||||
nix
|
||||
nixbld
|
||||
</programlisting>
|
||||
|
||||
The setuid-helper barfs if it is called by a user other than the one
|
||||
specified on the first line, or if it is asked to execute a build
|
||||
under a user who is not a member of the group specified on the second
|
||||
line. The file <filename>/etc/nix-setuid.conf</filename> must be
|
||||
owned by root, and must not be group- or world-writable. The
|
||||
setuid-helper barfs if this is not the case.</para>
|
||||
|
||||
</section>
|
||||
|
||||
|
||||
<section><title>Restricting access</title>
|
||||
|
||||
<para>To limit which users can perform Nix operations, you can use the
|
||||
permissions on the directory
|
||||
<filename>/nix/var/nix/daemon-socket</filename>. For instance, if you
|
||||
want to restrict the use of Nix to the members of a group called
|
||||
<literal>nix-users</literal>, do
|
||||
|
||||
<screen>
|
||||
$ chgrp nix-users /nix/var/nix/daemon-socket
|
||||
$ chmod ug=rwx,o= /nix/var/nix/daemon-socket
|
||||
</screen>
|
||||
|
||||
This way, users who are not in the <literal>nix-users</literal> group
|
||||
cannot connect to the Unix domain socket
|
||||
<filename>/nix/var/nix/daemon-socket/socket</filename>, so they cannot
|
||||
perform Nix operations.</para>
|
||||
|
||||
</section>
|
||||
|
||||
|
||||
</section> <!-- end of multi-user -->
|
||||
|
||||
|
||||
</section> <!-- end of security -->
|
||||
|
||||
|
||||
<section><title>Using Nix</title>
|
||||
|
||||
@@ -1,135 +1,304 @@
|
||||
<chapter xmlns="http://docbook.org/ns/docbook"
|
||||
xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||
xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||
xml:id="chap-introduction">
|
||||
|
||||
<title>Introduction</title>
|
||||
|
||||
<para>Nix is a system for the deployment of software. Software
|
||||
deployment is concerned with the creation, distribution, and
|
||||
management of software components (<quote>packages</quote>). Its main
|
||||
features are:
|
||||
|
||||
<itemizedlist>
|
||||
<section><title>About Nix</title>
|
||||
|
||||
<listitem><para>It helps you make sure that dependency specifications
|
||||
are complete. In general in a deployment system you have to specify
|
||||
for each component what its dependencies are, but there are no
|
||||
guarantees that this specification is complete. If you forget a
|
||||
dependency, then the component will build and work correctly on
|
||||
<emphasis>your</emphasis> machine if you have the dependency
|
||||
installed, but not on the end user's machine if it's not
|
||||
there.</para></listitem>
|
||||
<para>Nix is a <emphasis>purely functional package manager</emphasis>.
|
||||
This means that it treats packages like values in purely functional
|
||||
programming languages such as Haskell — they are built by functions
|
||||
that don’t have side-effects, and they never change after they have
|
||||
been built. Nix stores packages in the <emphasis>Nix
|
||||
store</emphasis>, usually the directory
|
||||
<filename>/nix/store</filename>, where each package has its own unique
|
||||
subdirectory such as
|
||||
|
||||
<listitem><para>It is possible to have <emphasis>multiple versions or
|
||||
variants</emphasis> of a component installed at the same time. In
|
||||
contrast, in systems such as RPM different versions of the same
|
||||
package tend to install to the same location in the file system, so
|
||||
installing one version will remove the other. This is especially
|
||||
important if you want to use applications that have conflicting
|
||||
requirements on different versions of a component (e.g., application A
|
||||
requires version 1.0 of library X, while application B requires a
|
||||
non-backwards compatible version 1.1).</para></listitem>
|
||||
<programlisting>
|
||||
/nix/store/r8vvq9kq18pz08v249h8my6r9vs7s0n3-firefox-2.0.0.1/
|
||||
</programlisting>
|
||||
|
||||
<listitem><para>Users can have different <quote>views</quote>
|
||||
(<quote>profiles</quote> in Nix parlance) on the set of installed
|
||||
applications in a system. For instance, one user can have version 1.0
|
||||
of some package visible, while another is using version 1.1, and a
|
||||
third doesn't use it at all.</para></listitem>
|
||||
where <literal>r8vvq9kq…</literal> is a unique identifier for the
|
||||
package that captures all its dependencies (it’s a cryptographic hash
|
||||
of the package’s build dependency graph). This enables many powerful
|
||||
features.</para>
|
||||
|
||||
<listitem><para>It is possible to atomically
|
||||
<emphasis>upgrade</emphasis> software. I.e., there is no time window
|
||||
during an upgrade in which part of the old version and part of the new
|
||||
version are simultaneously visible (which might well cause the
|
||||
component to fail).</para></listitem>
|
||||
|
||||
<listitem><para>Likewise, it is possible to atomically roll back after
|
||||
an install, upgrade, or uninstall action. That is, in a fast (O(1))
|
||||
operation the previous configuration of the system can be restored.
|
||||
This is because upgrade or uninstall actions don't actually remove
|
||||
components from the system.</para></listitem>
|
||||
<simplesect><title>Multiple versions</title>
|
||||
|
||||
<listitem><para>Unused components can be
|
||||
<emphasis>garbage-collected</emphasis> automatically and safely: when
|
||||
you remove an application from a profile, its dependencies will be
|
||||
deleted by the garbage collector only if there are no other active
|
||||
applications using them.</para></listitem>
|
||||
<para>You can have multiple versions or variants of a package
|
||||
installed at the same time. This is especially important when
|
||||
different applications have dependencies on different versions of the
|
||||
same package — it prevents the “DLL hell”. Because of the hashing
|
||||
scheme, different versions of a package end up in different paths in
|
||||
the Nix store, so they don’t interfere with each other.</para>
|
||||
|
||||
<listitem><para>Nix supports both source-based deployment models
|
||||
(where you distribute <emphasis>Nix expressions</emphasis> that tell
|
||||
Nix how to build software from source) and binary-based deployment
|
||||
models. The latter is more-or-less transparent: installation of
|
||||
components is always based on Nix expressions, but if the expressions
|
||||
have been built before and Nix knows that the resulting binaries are
|
||||
available somewhere, it will use those instead.</para></listitem>
|
||||
<para>An important consequence is that operations like upgrading or
|
||||
uninstalling an application cannot break other applications, since
|
||||
these operations never “destructively” update or delete files that are
|
||||
used by other packages.</para>
|
||||
|
||||
<listitem><para>Nix is flexible in the deployment policies that it
|
||||
supports. There is a clear separation between the tools that
|
||||
implement basic Nix <emphasis>mechanisms</emphasis> (e.g., building
|
||||
Nix expressions), and the tools that implement various deployment
|
||||
<emphasis>policies</emphasis>. For instance, there is a concept of
|
||||
<quote>Nix channels</quote> that can be used to keep software
|
||||
installations up-to-date automatically from a network source. This is
|
||||
a policy that is implemented by a fairly short Perl script, which can
|
||||
be adapted easily to achieve similar policies.</para></listitem>
|
||||
</simplesect>
|
||||
|
||||
<listitem><para>Nix component builds aim to be <quote>pure</quote>;
|
||||
that is, unaffected by anything other than the declared dependencies.
|
||||
This means that if a component was built successfully once, it can be
|
||||
rebuilt again on another machine and the result will be the same. We
|
||||
cannot <emphasis>guarantee</emphasis> this (e.g., if the build depends
|
||||
on the time-of-day), but Nix (and the tools in the Nix Packages
|
||||
collection) takes special care to help achieve this.</para></listitem>
|
||||
|
||||
<listitem><para>Nix expressions (the things that tell Nix how to build
|
||||
components) are self-contained: they describe not just components but
|
||||
complete compositions. In other words, Nix expressions also describe
|
||||
how to build all the dependencies. This is in contrast to component
|
||||
specification languages like RPM spec files, which might say that a
|
||||
component X depends on some other component Y, but since it does not
|
||||
describe <emphasis>exactly</emphasis> what Y is, the result of
|
||||
building or running X might be different on different machines.
|
||||
Combined with purity, self-containedness ensures that a component that
|
||||
<quote>works</quote> on one machine also works on another, when
|
||||
deployed using Nix.</para></listitem>
|
||||
<simplesect><title>Complete dependencies</title>
|
||||
|
||||
<listitem><para>The Nix expression language makes it easy to describe
|
||||
variability in components (e.g., optional features or
|
||||
dependencies).</para></listitem>
|
||||
<para>Nix helps you make sure that package dependency specifications
|
||||
are complete. In general, when you’re making a package for a package
|
||||
management system like RPM, you have to specify for each package what
|
||||
its dependencies are, but there are no guarantees that this
|
||||
specification is complete. If you forget a dependency, then the
|
||||
package will build and work correctly on <emphasis>your</emphasis>
|
||||
machine if you have the dependency installed, but not on the end
|
||||
user's machine if it's not there.</para>
|
||||
|
||||
<listitem><para>Nix is ideal for building build farms that do
|
||||
continuous builds of software from a version management system, since
|
||||
it can take care of building all the dependencies as well. Also, Nix
|
||||
only rebuilds components that have changed, so there are no
|
||||
unnecessary builds. In addition, Nix can transparently distribute
|
||||
build jobs over different machines, including different
|
||||
platforms.</para></listitem>
|
||||
<para>Since Nix on the other hand doesn’t install packages in “global”
|
||||
locations like <filename>/usr/bin</filename> but in package-specific
|
||||
directories, the risk of incomplete dependencies is greatly reduced.
|
||||
This is because tools such as compilers don’t search in per-packages
|
||||
directories such as
|
||||
<filename>/nix/store/5lbfaxb722zp…-openssl-0.9.8d/include</filename>,
|
||||
so if a package builds correctly on your system, this is because you
|
||||
specified the dependency explicitly.</para>
|
||||
|
||||
<listitem><para>Nix can be used not only for software deployment, but
|
||||
also for <emphasis>service deployment</emphasis>, such as the
|
||||
deployment of a complete web server with all its configuration files,
|
||||
static pages, software dependencies, and so on. Nix's advantages for
|
||||
software deployment also apply here: for instance, the ability
|
||||
trivially to have multiple configurations at the same time, or the
|
||||
ability to do rollbacks.</para></listitem>
|
||||
<para>Runtime dependencies are found by scanning binaries for the hash
|
||||
parts of Nix store paths (such as <literal>r8vvq9kq…</literal>). This
|
||||
sounds risky, but it works extremely well.</para>
|
||||
|
||||
<listitem><para>Nix can efficiently upgrade between different versions
|
||||
of a component through <emphasis>binary patching</emphasis>. If
|
||||
patches are available on a server, and you try to install a new
|
||||
version of some component, Nix will automatically apply a patch (or
|
||||
sequence of patches), if available, to transform the installed
|
||||
component into the new version.</para></listitem>
|
||||
</simplesect>
|
||||
|
||||
</itemizedlist>
|
||||
|
||||
</para>
|
||||
<simplesect><title>Multi-user support</title>
|
||||
|
||||
<para>Starting at version 0.11, Nix has multi-user support. This
|
||||
means that non-privileged users can securely install software. Each
|
||||
user can have a different <emphasis>profile</emphasis>, a set of
|
||||
packages in the Nix store that appear in the user’s
|
||||
<envar>PATH</envar>. If a user installs a package that another user
|
||||
has already installed previously, the package won’t be built or
|
||||
downloaded a second time. At the same time, it is not possible for
|
||||
one user to inject a Trojan horse into a package that might be used by
|
||||
another user.</para>
|
||||
|
||||
<!--
|
||||
<para>More details can be found in Section 3 of our <a
|
||||
href="docs/papers.html#securesharing">ASE 2005 paper</a>.</para>
|
||||
-->
|
||||
|
||||
</simplesect>
|
||||
|
||||
|
||||
<simplesect><title>Atomic upgrades and rollbacks</title>
|
||||
|
||||
<para>Since package management operations never overwrite packages in
|
||||
the Nix store but just add new versions in different paths, they are
|
||||
<emphasis>atomic</emphasis>. So during a package upgrade, there is no
|
||||
time window in which the package has some files from the old version
|
||||
and some files from the new version — which would be bad because a
|
||||
program might well crash if it’s started during that period.</para>
|
||||
|
||||
<para>And since package aren’t overwritten, the old versions are still
|
||||
there after an upgrade. This means that you can <emphasis>roll
|
||||
back</emphasis> to the old version:</para>
|
||||
|
||||
<screen>
|
||||
$ nix-env --upgrade <replaceable>some-packages</replaceable>
|
||||
$ nix-env --rollback
|
||||
</screen>
|
||||
|
||||
</simplesect>
|
||||
|
||||
|
||||
<simplesect><title>Garbage collection</title>
|
||||
|
||||
<para>When you install a package like this…
|
||||
|
||||
<screen>
|
||||
$ nix-env --uninstall firefox
|
||||
</screen>
|
||||
|
||||
the package isn’t deleted from the system right away (after all, you
|
||||
might want to do a rollback, or it might be in the profiles of other
|
||||
users). Instead, unused packages can be deleted safely by running the
|
||||
<emphasis>garbage collector</emphasis>:
|
||||
|
||||
<screen>
|
||||
$ nix-collect-garbage
|
||||
</screen>
|
||||
|
||||
This deletes all packages that aren’t in use by any user profile or by
|
||||
a currently running program.</para>
|
||||
|
||||
</simplesect>
|
||||
|
||||
|
||||
<simplesect><title>Functional package language</title>
|
||||
|
||||
<para>Packages are built from <emphasis>Nix expressions</emphasis>,
|
||||
which is a simple functional language. A Nix expression describes
|
||||
everything that goes into a package build action (a “derivation”):
|
||||
other packages, sources, the build script, environment variables for
|
||||
the build script, etc. Nix tries very hard to ensure that Nix
|
||||
expressions are <emphasis>deterministic</emphasis>: building a Nix
|
||||
expression twice should yield the same result.</para>
|
||||
|
||||
<para>Because it’s a functional language, it’s easy to support
|
||||
building variants of a package: turn the Nix expression into a
|
||||
function and call it any number of times with the appropriate
|
||||
arguments. Due to the hashing scheme, variants don’t conflict with
|
||||
each other in the Nix store.</para>
|
||||
|
||||
</simplesect>
|
||||
|
||||
|
||||
<simplesect><title>Transparent source/binary deployment</title>
|
||||
|
||||
<para>Nix expressions generally describe how to build a package from
|
||||
source, so an installation action like
|
||||
|
||||
<screen>
|
||||
$ nix-env --install firefox
|
||||
</screen>
|
||||
|
||||
<emphasis>could</emphasis> cause quite a bit of build activity, as not
|
||||
only Firefox but also all its dependencies (all the way up to the C
|
||||
library and the compiler) would have to built, at least if they are
|
||||
not already in the Nix store. This is a <emphasis>source deployment
|
||||
model</emphasis>. For most users, building from source is not very
|
||||
pleasant as it takes far too long. However, Nix can automatically
|
||||
skip building from source and download a pre-built binary instead if
|
||||
it knows about it. <emphasis>Nix channels</emphasis> provide Nix
|
||||
expressions along with pre-built binaries.</para>
|
||||
|
||||
<!--
|
||||
<para>source deployment model (like <a
|
||||
href="http://www.gentoo.org/">Gentoo</a>) and a binary model (like
|
||||
RPM)</para>
|
||||
-->
|
||||
|
||||
</simplesect>
|
||||
|
||||
|
||||
<simplesect><title>Binary patching</title>
|
||||
|
||||
<para>In addition to downloading binaries automatically if they’re
|
||||
available, Nix can download binary deltas that patch an existing
|
||||
package in the Nix store into a new version. This speeds up
|
||||
upgrades.</para>
|
||||
|
||||
</simplesect>
|
||||
|
||||
|
||||
<simplesect><title>Nix Packages collection</title>
|
||||
|
||||
<para>We provide a large set of Nix expressions containing hundreds of
|
||||
existing Unix packages, the <emphasis>Nix Packages
|
||||
collection</emphasis> (Nixpkgs).</para>
|
||||
|
||||
</simplesect>
|
||||
|
||||
|
||||
<simplesect><title>Service deployment</title>
|
||||
|
||||
<para>Nix can be used not only for rolling out packages, but also
|
||||
complete <emphasis>configurations</emphasis> of services. This is
|
||||
done by treating all the static bits of a service (such as software
|
||||
packages, configuration files, control scripts, static web pages,
|
||||
etc.) as “packages” that can be built by Nix expressions. As a
|
||||
result, all the features above apply to services as well: for
|
||||
instance, you can roll back a web server configuration if a
|
||||
configuration change turns out to be undesirable, you can easily have
|
||||
multiple instances of a service (e.g., a test and production server),
|
||||
and because the whole service is built in a purely functional way from
|
||||
a Nix expression, it is repeatable so you can easily reproduce the
|
||||
service on another machine.</para>
|
||||
|
||||
<!--
|
||||
<para>You can read more about this in our <a
|
||||
href="docs/papers.html#servicecm">SCM-12 paper</a>.</para>
|
||||
-->
|
||||
|
||||
</simplesect>
|
||||
|
||||
|
||||
<simplesect><title>Portability</title>
|
||||
|
||||
<para>Nix should run on most Unix systems, including Linux, FreeBSD and
|
||||
Mac OS X. It is also supported on Windows using Cygwin.</para>
|
||||
|
||||
</simplesect>
|
||||
|
||||
|
||||
<simplesect><title>NixOS</title>
|
||||
|
||||
<para>NixOS is a Linux distribution based on Nix. It uses Nix not
|
||||
just for package management but also to manage the system
|
||||
configuration (e.g., to build configuration files in
|
||||
<filename>/etc</filename>). This means, among other things, that it’s
|
||||
possible to easily roll back the entire configuration of the system to
|
||||
an earlier state. Also, users can install software without root
|
||||
privileges. For more information and downloads, see the <link
|
||||
xlink:href="http://nix.cs.uu.nl/nixos/">NixOS homepage</link>.</para>
|
||||
|
||||
</simplesect>
|
||||
|
||||
|
||||
<!-- other features:
|
||||
|
||||
- build farms
|
||||
- reproducibility (Nix expressions allows whole configuration to be rebuilt)
|
||||
|
||||
-->
|
||||
|
||||
</section>
|
||||
|
||||
|
||||
<section><title>About us</title>
|
||||
|
||||
<para>Nix was developed at the <link
|
||||
xlink:href="http://www.cs.uu.nl/">Department of Information and
|
||||
Computing Sciences</link>, Utrecht University by the <link
|
||||
xlink:href="http://www.cs.uu.nl/wiki/Trace/WebHome">TraCE
|
||||
project</link>. The project is funded by the Software Engineering
|
||||
Research Program <link
|
||||
xlink:href="http://www.jacquard.nl/">Jacquard</link> to improve the
|
||||
support for variability in software systems.</para>
|
||||
|
||||
</section>
|
||||
|
||||
|
||||
<section><title>About this manual</title>
|
||||
|
||||
<para>This manual tells you how to install and use Nix and how to
|
||||
write Nix expressions for software not already in the Nix Packages
|
||||
collection. It also discusses some advanced topics, such as setting
|
||||
up a Nix-based build farm, and doing service deployment using
|
||||
Nix.</para>
|
||||
up a Nix-based build farm.</para>
|
||||
|
||||
<note><para>Some background information on Nix can be found in a
|
||||
number of papers. The ICSE 2004 paper <citetitle
|
||||
</section>
|
||||
|
||||
|
||||
<section><title>License</title>
|
||||
|
||||
<para>Nix is free software; you can redistribute it and/or modify it
|
||||
under the terms of the <link
|
||||
xlink:href="http://www.gnu.org/licenses/lgpl.html">GNU Lesser General
|
||||
Public License</link> as published by the <link
|
||||
xlink:href="http://www.fsf.org/">Free Software Foundation</link>;
|
||||
either version 2.1 of the License, or (at your option) any later
|
||||
version. Nix is distributed in the hope that it will be useful, but
|
||||
WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.</para>
|
||||
|
||||
</section>
|
||||
|
||||
|
||||
<section><title>More information</title>
|
||||
|
||||
<para>Some background information on Nix can be found in a number of
|
||||
papers. The ICSE 2004 paper <citetitle
|
||||
xlink:href='http://www.cs.uu.nl/~eelco/pubs/immdsd-icse2004-final.pdf'>Imposing
|
||||
a Memory Management Discipline on Software Deployment</citetitle>
|
||||
discusses the hashing mechanism used to ensure reliable dependency
|
||||
@@ -141,10 +310,27 @@ gives a more general discussion of Nix from a system-administration
|
||||
perspective. The CBSE 2005 paper <citetitle
|
||||
xlink:href='http://www.cs.uu.nl/~eelco/pubs/eupfcdm-cbse2005-final.pdf'>Efficient
|
||||
Upgrading in a Purely Functional Component Deployment Model
|
||||
</citetitle> is about transparent patch deployment in Nix. Finally,
|
||||
the SCM-12 paper <citetitle
|
||||
</citetitle> is about transparent patch deployment in Nix. The SCM-12
|
||||
paper <citetitle
|
||||
xlink:href='http://www.cs.uu.nl/~eelco/pubs/servicecm-scm12-final.pdf'>
|
||||
Service Configuration Management</citetitle> shows how services (e.g.,
|
||||
web servers) can be deployed and managed through Nix.</para></note>
|
||||
web servers) can be deployed and managed through Nix. A short
|
||||
overview of NixOS is given in the HotOS XI paper <citetitle
|
||||
xlink:href="http://www.cs.uu.nl/~eelco/pubs/hotos-final.pdf">Purely
|
||||
Functional System Configuration Management</citetitle>. The Nix
|
||||
homepage has <link
|
||||
xlink:href="http://nix.cs.uu.nl/docs/papers.html">an up-to-date list
|
||||
of Nix-related papers</link>.</para>
|
||||
|
||||
<para>Nix is the subject of Eelco Dolstra’s PhD thesis <citetitle
|
||||
xlink:href="http://igitur-archive.library.uu.nl/dissertations/2006-0118-200031/index.htm">The
|
||||
Purely Functional Software Deployment Model</citetitle>, which
|
||||
contains most of the papers listed above.</para>
|
||||
|
||||
<para>Nix has a homepage at <link
|
||||
xlink:href="http://nix.cs.uu.nl/"/>.</para>
|
||||
|
||||
</section>
|
||||
|
||||
|
||||
</chapter>
|
||||
|
||||
@@ -19,6 +19,7 @@
|
||||
<command>nix-build</command>
|
||||
<xi:include xmlns:xi="http://www.w3.org/2001/XInclude" href="opt-common-syn.xml#xmlns(db=http://docbook.org/ns/docbook)xpointer(/db:nop/*)" />
|
||||
<arg><option>--arg</option> <replaceable>name</replaceable> <replaceable>value</replaceable></arg>
|
||||
<arg><option>--argstr</option> <replaceable>name</replaceable> <replaceable>value</replaceable></arg>
|
||||
<arg>
|
||||
<group choice='req'>
|
||||
<arg choice='plain'><option>--attr</option></arg>
|
||||
|
||||
@@ -62,11 +62,11 @@ also <xref linkend="sec-channels" />.</para>
|
||||
<varlistentry><term><option>--update</option></term>
|
||||
|
||||
<listitem><para>Downloads the Nix expressions of all subscribed
|
||||
channels, makes the conjunction of these the default for
|
||||
<command>nix-env</command> operations (by calling <command>nix-env
|
||||
-I</command>), and performs a <command>nix-pull</command> on the
|
||||
manifests of all channels to make pre-built binaries
|
||||
available.</para></listitem>
|
||||
channels, makes them the default for <command>nix-env</command>
|
||||
operations (by symlinking them in the directory
|
||||
<filename>~/.nix-defexpr</filename>), and performs a
|
||||
<command>nix-pull</command> on the manifests of all channels to
|
||||
make pre-built binaries available.</para></listitem>
|
||||
|
||||
</varlistentry>
|
||||
|
||||
|
||||
@@ -19,13 +19,7 @@
|
||||
<command>nix-env</command>
|
||||
<xi:include xmlns:xi="http://www.w3.org/2001/XInclude" href="opt-common-syn.xml#xmlns(db=http://docbook.org/ns/docbook)xpointer(/db:nop/*)" />
|
||||
<arg><option>--arg</option> <replaceable>name</replaceable> <replaceable>value</replaceable></arg>
|
||||
<arg>
|
||||
<group choice='req'>
|
||||
<arg choice='plain'><option>--attr</option></arg>
|
||||
<arg choice='plain'><option>-A</option></arg>
|
||||
</group>
|
||||
<replaceable>attrPath</replaceable>
|
||||
</arg>
|
||||
<arg><option>--argstr</option> <replaceable>name</replaceable> <replaceable>value</replaceable></arg>
|
||||
<arg>
|
||||
<group choice='req'>
|
||||
<arg choice='plain'><option>--file</option></arg>
|
||||
@@ -45,9 +39,6 @@
|
||||
<replaceable>system</replaceable>
|
||||
</arg>
|
||||
<arg><option>--dry-run</option></arg>
|
||||
<arg><option>--from-expression</option></arg>
|
||||
<arg><option>-E</option></arg>
|
||||
<arg><option>--from-profile</option> <replaceable>path</replaceable></arg>
|
||||
<arg choice='plain'><replaceable>operation</replaceable></arg>
|
||||
<arg rep='repeat'><replaceable>options</replaceable></arg>
|
||||
<arg rep='repeat'><replaceable>arguments</replaceable></arg>
|
||||
@@ -58,7 +49,7 @@
|
||||
<refsection><title>Description</title>
|
||||
|
||||
<para>The command <command>nix-env</command> is used to manipulate Nix
|
||||
user environments. User environments are sets of software components
|
||||
user environments. User environments are sets of software packages
|
||||
available to a user at some point in time. In other words, they are a
|
||||
synthesised view of the programs available in the Nix store. There
|
||||
may be many user environments: different users can have different
|
||||
@@ -151,13 +142,33 @@ linkend="sec-common-options" />.</para>
|
||||
|
||||
<varlistentry><term><filename>~/.nix-defexpr</filename></term>
|
||||
|
||||
<!-- !!! .nix-defexpr can be a directory now -->
|
||||
|
||||
<listitem><para>The default Nix expression used by the
|
||||
<option>--install</option>, <option>--upgrade</option>, and
|
||||
<option>--query --available</option> operations to obtain
|
||||
derivations. The <option>--file</option> option may be used to
|
||||
override this default.</para></listitem>
|
||||
<listitem><para>A directory that contains the default Nix
|
||||
expressions used by the <option>--install</option>,
|
||||
<option>--upgrade</option>, and <option>--query
|
||||
--available</option> operations to obtain derivations. The
|
||||
<option>--file</option> option may be used to override this
|
||||
default.</para>
|
||||
|
||||
<para>The Nix expressions in this directory are combined into a
|
||||
single attribute set, with each file as an attribute that has the
|
||||
name of the file. Thus, if <filename>~/.nix-defexpr</filename>
|
||||
contains two files, <filename>foo</filename> and
|
||||
<filename>bar</filename>, then the default Nix expression will
|
||||
essentially be
|
||||
|
||||
<programlisting>
|
||||
{
|
||||
foo = import ~/.nix-defexpr/foo;
|
||||
bar = import ~/.nix-defexpr/bar;
|
||||
}</programlisting>
|
||||
|
||||
</para>
|
||||
|
||||
<para>The command <command>nix-channel</command> places symlinks
|
||||
to the downloaded Nix expressions from each subscribed channel in
|
||||
this directory.</para>
|
||||
|
||||
</listitem>
|
||||
|
||||
</varlistentry>
|
||||
|
||||
@@ -190,6 +201,7 @@ linkend="sec-common-options" />.</para>
|
||||
<arg choice='plain'><option>--install</option></arg>
|
||||
<arg choice='plain'><option>-i</option></arg>
|
||||
</group>
|
||||
<xi:include xmlns:xi="http://www.w3.org/2001/XInclude" href="opt-inst-syn.xml#xmlns(db=http://docbook.org/ns/docbook)xpointer(/db:nop/*)" />
|
||||
<group choice='opt'>
|
||||
<arg choice='plain'><option>--preserve-installed</option></arg>
|
||||
<arg choice='plain'><option>-P</option></arg>
|
||||
@@ -221,11 +233,21 @@ number of possible ways:
|
||||
<para>If there are multiple derivations matching a name in
|
||||
<replaceable>args</replaceable> that have the same name (e.g.,
|
||||
<literal>gcc-3.3.6</literal> and <literal>gcc-4.1.1</literal>), then
|
||||
only the highest version will be installed. You can force the
|
||||
installation of multiple derivations with the same name by being
|
||||
specific about the versions. For instance, <literal>nix-env -i
|
||||
gcc-3.3.6 gcc-4.1.1</literal> will install both version of GCC (and
|
||||
will probably cause a user environment conflict!).</para></listitem>
|
||||
the derivation with the highest <emphasis>priority</emphasis> is
|
||||
used. A derivation can define a priority by declaring the
|
||||
<varname>meta.priority</varname> attribute. This attribute should
|
||||
be a number, with a higher value denoting a lower priority. The
|
||||
default priority is <literal>0</literal>.</para>
|
||||
|
||||
<para>If there are multiple matching derivations with the same
|
||||
priority, then the derivation with the highest version will be
|
||||
installed.</para>
|
||||
|
||||
<para>You can force the installation of multiple derivations with
|
||||
the same name by being specific about the versions. For instance,
|
||||
<literal>nix-env -i gcc-3.3.6 gcc-4.1.1</literal> will install both
|
||||
version of GCC (and will probably cause a user environment
|
||||
conflict!).</para></listitem>
|
||||
|
||||
<listitem><para>If <link
|
||||
linkend='opt-attr'><option>--attr</option></link>
|
||||
@@ -272,6 +294,15 @@ number of possible ways:
|
||||
|
||||
<variablelist>
|
||||
|
||||
<varlistentry><term><option>--prebuild-only</option> / <option>-b</option></term>
|
||||
|
||||
<listitem><para>Use only derivations for which a substitute is
|
||||
registered, i.e., there is a pre-built binary available that can
|
||||
be downloaded in lieu of building the derivation. Thus, no
|
||||
packages will be built from source.</para></listitem>
|
||||
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry><term><option>--preserve-installed</option></term>
|
||||
<term><option>-P</option></term>
|
||||
|
||||
@@ -387,7 +418,7 @@ the following paths will be substituted:
|
||||
|
||||
<!--######################################################################-->
|
||||
|
||||
<refsection><title>Operation <option>--upgrade</option></title>
|
||||
<refsection xml:id="rsec-nix-env-upgrade"><title>Operation <option>--upgrade</option></title>
|
||||
|
||||
<refsection><title>Synopsis</title>
|
||||
|
||||
@@ -397,6 +428,7 @@ the following paths will be substituted:
|
||||
<arg choice='plain'><option>--upgrade</option></arg>
|
||||
<arg choice='plain'><option>-u</option></arg>
|
||||
</group>
|
||||
<xi:include xmlns:xi="http://www.w3.org/2001/XInclude" href="opt-inst-syn.xml#xmlns(db=http://docbook.org/ns/docbook)xpointer(/db:nop/*)" />
|
||||
<group choice='opt'>
|
||||
<arg choice='plain'><option>--lt</option></arg>
|
||||
<arg choice='plain'><option>--leq</option></arg>
|
||||
@@ -471,6 +503,9 @@ installed.</para>
|
||||
|
||||
</variablelist>
|
||||
|
||||
<para>For the other flags, see <option
|
||||
linkend="rsec-nix-env-install">--install</option>.</para>
|
||||
|
||||
</refsection>
|
||||
|
||||
<refsection><title>Examples</title>
|
||||
@@ -580,6 +615,111 @@ $ nix-env -e '*' <lineannotation>(remove everything)</lineannotation></screen>
|
||||
|
||||
|
||||
|
||||
<!--######################################################################-->
|
||||
|
||||
<refsection xml:id="rsec-nix-env-set-flag"><title>Operation <option>--set-flag</option></title>
|
||||
|
||||
<refsection><title>Synopsis</title>
|
||||
|
||||
<cmdsynopsis>
|
||||
<command>nix-env</command>
|
||||
<arg choice='plain'><option>--set-flag</option></arg>
|
||||
<arg choice='plain'><replaceable>name</replaceable></arg>
|
||||
<arg choice='plain'><replaceable>value</replaceable></arg>
|
||||
<arg choice='plain' rep='repeat'><replaceable>drvnames</replaceable></arg>
|
||||
</cmdsynopsis>
|
||||
</refsection>
|
||||
|
||||
<refsection><title>Description</title>
|
||||
|
||||
<para>The <option>--set-flag</option> operation allows meta attributes
|
||||
of installed packages to be modified. There are several attributes
|
||||
that can be usefully modified, because they affect the behaviour of
|
||||
<command>nix-env</command> or the user environment build
|
||||
script:
|
||||
|
||||
<itemizedlist>
|
||||
|
||||
<listitem><para><varname>priority</varname> can be changed to
|
||||
resolve filename clashes. The user environment build script uses
|
||||
the <varname>meta.priority</varname> attribute of derivations to
|
||||
resolve filename collisions between packages. Lower priority values
|
||||
denote a higher priority. For instance, the GCC wrapper package and
|
||||
the Binutils package in Nixpkgs both have a file
|
||||
<filename>bin/ld</filename>, so previously if you tried to install
|
||||
both you would get a collision. Now, on the other hand, the GCC
|
||||
wrapper declares a higher priority than Binutils, so the former’s
|
||||
<filename>bin/ld</filename> is symlinked in the user
|
||||
environment.</para></listitem>
|
||||
|
||||
<listitem><para><varname>keep</varname> can be set to
|
||||
<literal>true</literal> to prevent the package from being upgraded
|
||||
or replaced. This is useful if you want to hang on to an older
|
||||
version of a package.</para></listitem>
|
||||
|
||||
<listitem><para><varname>active</varname> can be set to
|
||||
<literal>false</literal> to “disable” the package. That is, no
|
||||
symlinks will be generated to the files of the package, but it
|
||||
remains part of the profile (so it won’t be garbage-collected). It
|
||||
can be set back to <literal>true</literal> to re-enable the
|
||||
package.</para></listitem>
|
||||
|
||||
</itemizedlist>
|
||||
|
||||
</para>
|
||||
|
||||
</refsection>
|
||||
|
||||
<refsection><title>Examples</title>
|
||||
|
||||
<para>To prevent the currently installed Firefox from being upgraded:
|
||||
|
||||
<screen>
|
||||
$ nix-env --set-flag keep true firefox</screen>
|
||||
|
||||
After this, <command>nix-env -u</command> will ignore Firefox.</para>
|
||||
|
||||
<para>To disable the currently installed Firefox, then install a new
|
||||
Firefox while the old remains part of the profile:
|
||||
|
||||
<screen>
|
||||
$ nix-env -q \*
|
||||
firefox-2.0.0.9 <lineannotation>(the current one)</lineannotation>
|
||||
|
||||
$ nix-env --preserve-installed -i firefox-2.0.0.11
|
||||
installing `firefox-2.0.0.11'
|
||||
building path(s) `/nix/store/myy0y59q3ig70dgq37jqwg1j0rsapzsl-user-environment'
|
||||
Collission between `/nix/store/<replaceable>...</replaceable>-firefox-2.0.0.11/bin/firefox'
|
||||
and `/nix/store/<replaceable>...</replaceable>-firefox-2.0.0.9/bin/firefox'.
|
||||
<lineannotation>(i.e., can’t have two active at the same time)</lineannotation>
|
||||
|
||||
$ nix-env --set-flag active false firefox
|
||||
setting flag on `firefox-2.0.0.9'
|
||||
|
||||
$ nix-env --preserve-installed -i firefox-2.0.0.11
|
||||
installing `firefox-2.0.0.11'
|
||||
|
||||
$ nix-env -q \*
|
||||
firefox-2.0.0.11 <lineannotation>(the enabled one)</lineannotation>
|
||||
firefox-2.0.0.9 <lineannotation>(the disabled one)</lineannotation></screen>
|
||||
|
||||
</para>
|
||||
|
||||
<para>To make files from <literal>binutils</literal> take precedence
|
||||
over files from <literal>gcc</literal>:
|
||||
|
||||
<screen>
|
||||
$ nix-env --set-flag priority 5 binutils
|
||||
$ nix-env --set-flag priority 10 gcc</screen>
|
||||
|
||||
</para>
|
||||
|
||||
</refsection>
|
||||
|
||||
</refsection>
|
||||
|
||||
|
||||
|
||||
<!--######################################################################-->
|
||||
|
||||
<refsection><title>Operation <option>--query</option></title>
|
||||
@@ -592,13 +732,14 @@ $ nix-env -e '*' <lineannotation>(remove everything)</lineannotation></screen>
|
||||
<arg choice='plain'><option>--query</option></arg>
|
||||
<arg choice='plain'><option>-q</option></arg>
|
||||
</group>
|
||||
<arg><option>--xml</option></arg>
|
||||
<group choice='opt'>
|
||||
<arg choice='plain'><option>--installed</option></arg>
|
||||
<arg choice='plain'><option>--available</option></arg>
|
||||
<arg choice='plain'><option>-a</option></arg>
|
||||
</group>
|
||||
|
||||
<sbr />
|
||||
|
||||
<arg>
|
||||
<group choice='req'>
|
||||
<arg choice='plain'><option>--status</option></arg>
|
||||
@@ -607,8 +748,8 @@ $ nix-env -e '*' <lineannotation>(remove everything)</lineannotation></screen>
|
||||
</arg>
|
||||
<arg>
|
||||
<group choice='req'>
|
||||
<arg choice='plain'><option>--attr</option></arg>
|
||||
<arg choice='plain'><option>-A</option></arg>
|
||||
<arg choice='plain'><option>--attr-path</option></arg>
|
||||
<arg choice='plain'><option>-P</option></arg>
|
||||
</group>
|
||||
</arg>
|
||||
<arg><option>--no-name</option></arg>
|
||||
@@ -622,6 +763,28 @@ $ nix-env -e '*' <lineannotation>(remove everything)</lineannotation></screen>
|
||||
<arg><option>--drv-path</option></arg>
|
||||
<arg><option>--out-path</option></arg>
|
||||
<arg><option>--description</option></arg>
|
||||
<arg><option>--meta</option></arg>
|
||||
|
||||
<sbr />
|
||||
|
||||
<arg><option>--xml</option></arg>
|
||||
<arg>
|
||||
<group choice='req'>
|
||||
<arg choice='plain'><option>--prebuilt-only</option></arg>
|
||||
<arg choice='plain'><option>-b</option></arg>
|
||||
</group>
|
||||
</arg>
|
||||
|
||||
<arg>
|
||||
<group choice='req'>
|
||||
<arg choice='plain'><option>--attr</option></arg>
|
||||
<arg choice='plain'><option>-A</option></arg>
|
||||
</group>
|
||||
<replaceable>attribute-path</replaceable>
|
||||
</arg>
|
||||
|
||||
<sbr />
|
||||
|
||||
<arg choice='plain' rep='repeat'><replaceable>names</replaceable></arg>
|
||||
</cmdsynopsis>
|
||||
|
||||
@@ -698,6 +861,16 @@ user environment elements, etc. -->
|
||||
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry><term><option>--prebuild-only</option> / <option>-b</option></term>
|
||||
|
||||
<listitem><para>Show only derivations for which a substitute is
|
||||
registered, i.e., there is a pre-built binary available that can
|
||||
be downloaded in lieu of building the derivation. Thus, this
|
||||
shows all packages that probably can be installed
|
||||
quickly.</para></listitem>
|
||||
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry><term><option>--status</option></term>
|
||||
<term><option>-s</option></term>
|
||||
|
||||
@@ -717,8 +890,8 @@ user environment elements, etc. -->
|
||||
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry><term><option>--attr</option></term>
|
||||
<term><option>-a</option></term>
|
||||
<varlistentry><term><option>--attr-path</option></term>
|
||||
<term><option>-P</option></term>
|
||||
|
||||
<listitem><para>Print the <emphasis>attribute path</emphasis> of
|
||||
the derivation, which can be used to unambiguously select it using
|
||||
@@ -741,35 +914,35 @@ user environment elements, etc. -->
|
||||
<listitem><para>Compare installed versions to available versions,
|
||||
or vice versa (if <option>--available</option> is given). This is
|
||||
useful for quickly seeing whether upgrades for installed
|
||||
components are available in a Nix expression. A column is added
|
||||
packages are available in a Nix expression. A column is added
|
||||
with the following meaning:
|
||||
|
||||
<variablelist>
|
||||
|
||||
<varlistentry><term><literal><</literal> <replaceable>version</replaceable></term>
|
||||
|
||||
<listitem><para>A newer version of the component is available
|
||||
<listitem><para>A newer version of the package is available
|
||||
or installed.</para></listitem>
|
||||
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry><term><literal>=</literal> <replaceable>version</replaceable></term>
|
||||
|
||||
<listitem><para>At most the same version of the component is
|
||||
<listitem><para>At most the same version of the package is
|
||||
available or installed.</para></listitem>
|
||||
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry><term><literal>></literal> <replaceable>version</replaceable></term>
|
||||
|
||||
<listitem><para>Only older versions of the component are
|
||||
<listitem><para>Only older versions of the package are
|
||||
available or installed.</para></listitem>
|
||||
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry><term><literal>- ?</literal></term>
|
||||
|
||||
<listitem><para>No version of the component is available or
|
||||
<listitem><para>No version of the package is available or
|
||||
installed.</para></listitem>
|
||||
|
||||
</varlistentry>
|
||||
@@ -810,6 +983,14 @@ user environment elements, etc. -->
|
||||
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry><term><option>--meta</option></term>
|
||||
|
||||
<listitem><para>Print all of the meta-attributes of the
|
||||
derivation. This option is only available with
|
||||
<option>--xml</option>.</para></listitem>
|
||||
|
||||
</varlistentry>
|
||||
|
||||
</variablelist>
|
||||
|
||||
</refsection>
|
||||
|
||||
@@ -779,4 +779,178 @@ archive is read from standard input.</para>
|
||||
</refsection>
|
||||
|
||||
|
||||
<!--######################################################################-->
|
||||
|
||||
<refsection xml:id='refsec-nix-store-export'><title>Operation <option>--export</option></title>
|
||||
|
||||
<refsection>
|
||||
<title>Synopsis</title>
|
||||
<cmdsynopsis>
|
||||
<command>nix-store</command>
|
||||
<arg choice='plain'><option>--export</option></arg>
|
||||
<arg choice='plain' rep='repeat'><replaceable>paths</replaceable></arg>
|
||||
</cmdsynopsis>
|
||||
</refsection>
|
||||
|
||||
<refsection><title>Description</title>
|
||||
|
||||
<para>The operation <option>--export</option> writes a serialisation
|
||||
of the specified store paths to standard output in a format that can
|
||||
be imported into another Nix store with <command
|
||||
linkend="refsec-nix-store-import">nix-store --import</command>. This
|
||||
is like <command linkend="refsec-nix-store-dump">nix-store
|
||||
--dump</command>, except that the NAR archive produced by that command
|
||||
doesn’t contain the necessary meta-information to allow it to be
|
||||
imported into another Nix store (namely, the set of references of the
|
||||
path).</para>
|
||||
|
||||
<para>This command does not produce a <emphasis>closure</emphasis> of
|
||||
the specified paths, so if a store path references other store paths
|
||||
that are missing in the target Nix store, the import will fail. To
|
||||
copy a whole closure, do something like
|
||||
|
||||
<screen>
|
||||
$ nix-store --export $(nix-store -qR <replaceable>paths</replaceable>) > out</screen>
|
||||
|
||||
</para>
|
||||
|
||||
<para>For an example of how <option>--export</option> and
|
||||
<option>--import</option> can be used, see the source of the <command
|
||||
linkend="sec-nix-copy-closure">nix-copy-closure</command>
|
||||
command.</para>
|
||||
|
||||
</refsection>
|
||||
|
||||
|
||||
</refsection>
|
||||
|
||||
|
||||
<!--######################################################################-->
|
||||
|
||||
<refsection xml:id='refsec-nix-store-import'><title>Operation <option>--import</option></title>
|
||||
|
||||
<refsection>
|
||||
<title>Synopsis</title>
|
||||
<cmdsynopsis>
|
||||
<command>nix-store</command>
|
||||
<arg choice='plain'><option>--import</option></arg>
|
||||
</cmdsynopsis>
|
||||
</refsection>
|
||||
|
||||
<refsection><title>Description</title>
|
||||
|
||||
<para>The operation <option>--export</option> reads a serialisation of
|
||||
a set of store paths produced by <command
|
||||
linkend="refsec-nix-store-export">nix-store --import</command> from
|
||||
standard input and adds those store paths to the Nix store. Paths
|
||||
that already exist in the Nix store are ignored. If a path refers to
|
||||
another path that doesn’t exist in the Nix store, the import
|
||||
fails.</para>
|
||||
|
||||
</refsection>
|
||||
|
||||
|
||||
</refsection>
|
||||
|
||||
|
||||
<!--######################################################################-->
|
||||
|
||||
<refsection><title>Operation <option>--optimise</option></title>
|
||||
|
||||
<refsection>
|
||||
<title>Synopsis</title>
|
||||
<cmdsynopsis>
|
||||
<command>nix-store</command>
|
||||
<arg choice='plain'><option>--optimise</option></arg>
|
||||
</cmdsynopsis>
|
||||
</refsection>
|
||||
|
||||
<refsection><title>Description</title>
|
||||
|
||||
<para>The operation <option>--optimise</option> reduces Nix store disk
|
||||
space usage by finding identical files in the store and hard-linking
|
||||
them to each other. It typically reduces the size of the store by
|
||||
something like 25-35%. Only regular files and symlinks are
|
||||
hard-linked in this manner. Files are considered identical when they
|
||||
have the same NAR archive serialisation: that is, regular files must
|
||||
have the same contents and permission (executable or non-executable),
|
||||
and symlinks must have the same contents.</para>
|
||||
|
||||
<para>After completion, or when the command is interrupted, a report
|
||||
on the achieved savings is printed on standard error.</para>
|
||||
|
||||
<para>Use <option>-vv</option> or <option>-vvv</option> to get some
|
||||
progress indication.</para>
|
||||
|
||||
</refsection>
|
||||
|
||||
<refsection><title>Example</title>
|
||||
|
||||
<screen>
|
||||
$ nix-store --optimise
|
||||
hashing files in `/nix/store/qhqx7l2f1kmwihc9bnxs7rc159hsxnf3-gcc-4.1.1'
|
||||
<replaceable>...</replaceable>
|
||||
541838819 bytes (516.74 MiB) freed by hard-linking 54143 files;
|
||||
there are 114486 files with equal contents out of 215894 files in total
|
||||
</screen>
|
||||
|
||||
</refsection>
|
||||
|
||||
|
||||
</refsection>
|
||||
|
||||
|
||||
<!--######################################################################-->
|
||||
|
||||
<refsection><title>Operation <option>--read-log</option></title>
|
||||
|
||||
<refsection>
|
||||
<title>Synopsis</title>
|
||||
<cmdsynopsis>
|
||||
<command>nix-store</command>
|
||||
<group choice='req'>
|
||||
<arg choice='plain'><option>--read-log</option></arg>
|
||||
<arg choice='plain'><option>-l</option></arg>
|
||||
</group>
|
||||
<arg choice='plain' rep='repeat'><replaceable>paths</replaceable></arg>
|
||||
</cmdsynopsis>
|
||||
</refsection>
|
||||
|
||||
<refsection><title>Description</title>
|
||||
|
||||
<para>The operation <option>--read-log</option> prints the build log
|
||||
of the specified store paths on standard output. The build log is
|
||||
whatever the builder of a derivation wrote to standard output and
|
||||
standard error. If a store path is not a derivation, the deriver of
|
||||
the store path is used.</para>
|
||||
|
||||
<para>Build logs are kept in
|
||||
<filename>/nix/var/log/nix/drvs</filename>. However, there is no
|
||||
guarantee that a build log is available for any particular store
|
||||
path. For instance, if the path was downloaded as a pre-built binary
|
||||
through a substitute, then the log is unavailable.</para>
|
||||
|
||||
</refsection>
|
||||
|
||||
<refsection><title>Example</title>
|
||||
|
||||
<screen>
|
||||
$ nix-store -l $(which ktorrent)
|
||||
building /nix/store/dhc73pvzpnzxhdgpimsd9sw39di66ph1-ktorrent-2.2.1
|
||||
unpacking sources
|
||||
unpacking source archive /nix/store/p8n1jpqs27mgkjw07pb5269717nzf5f8-ktorrent-2.2.1.tar.gz
|
||||
ktorrent-2.2.1/
|
||||
ktorrent-2.2.1/NEWS
|
||||
<replaceable>...</replaceable>
|
||||
</screen>
|
||||
|
||||
</refsection>
|
||||
|
||||
|
||||
</refsection>
|
||||
|
||||
|
||||
<!-- TODO: export, import operations -->
|
||||
|
||||
|
||||
</refentry>
|
||||
|
||||
@@ -13,6 +13,10 @@
|
||||
</group>
|
||||
<replaceable>number</replaceable>
|
||||
</arg>
|
||||
<arg>
|
||||
<arg><option>--max-silent-time</option></arg>
|
||||
<replaceable>number</replaceable>
|
||||
</arg>
|
||||
<arg><option>--keep-going</option></arg>
|
||||
<arg><option>-k</option></arg>
|
||||
<arg><option>--keep-failed</option></arg>
|
||||
|
||||
@@ -103,6 +103,17 @@
|
||||
</varlistentry>
|
||||
|
||||
|
||||
<varlistentry xml:id="opt-max-silent-time"><term><option>--max-silent-time</option></term>
|
||||
|
||||
<listitem><para>Sets the maximum number of seconds that a builder
|
||||
can go without producing any data on standard output or standard
|
||||
error. The default is specified by the <link
|
||||
linkend='conf-build-max-silent-time'><literal>build-max-silent-time</literal></link>
|
||||
configuration setting. <literal>0</literal> means no
|
||||
time-out.</para></listitem>
|
||||
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry><term><option>--keep-going</option></term>
|
||||
<term><option>-k</option></term>
|
||||
|
||||
@@ -257,6 +268,17 @@
|
||||
</varlistentry>
|
||||
|
||||
|
||||
<varlistentry><term><option>--argstr</option> <replaceable>name</replaceable> <replaceable>value</replaceable></term>
|
||||
|
||||
<listitem><para>This option is like <option>--arg</option>, only the
|
||||
value is not a Nix expression but a string. So instead of
|
||||
<literal>--arg system \"i686-linux\"</literal> (the outer quotes are
|
||||
to keep the shell happy) you can say <literal>--argstr system
|
||||
i686-linux</literal>.</para></listitem>
|
||||
|
||||
</varlistentry>
|
||||
|
||||
|
||||
<varlistentry xml:id="opt-attr"><term><option>--attr</option> / <option>-A</option>
|
||||
<replaceable>attrPath</replaceable></term>
|
||||
|
||||
|
||||
22
doc/manual/opt-inst-syn.xml
Normal file
22
doc/manual/opt-inst-syn.xml
Normal file
@@ -0,0 +1,22 @@
|
||||
<nop xmlns="http://docbook.org/ns/docbook">
|
||||
|
||||
<arg>
|
||||
<group choice='req'>
|
||||
<arg choice='plain'><option>--prebuilt-only</option></arg>
|
||||
<arg choice='plain'><option>-b</option></arg>
|
||||
</group>
|
||||
</arg>
|
||||
|
||||
<arg>
|
||||
<group choice='req'>
|
||||
<arg choice='plain'><option>--attr</option></arg>
|
||||
<arg choice='plain'><option>-A</option></arg>
|
||||
</group>
|
||||
</arg>
|
||||
|
||||
<arg><option>--from-expression</option></arg>
|
||||
<arg><option>-E</option></arg>
|
||||
|
||||
<arg><option>--from-profile</option> <replaceable>path</replaceable></arg>
|
||||
|
||||
</nop>
|
||||
@@ -6,9 +6,9 @@
|
||||
|
||||
|
||||
<para>This chapter discusses how to do package management with Nix,
|
||||
i.e., how to obtain, install, upgrade, and erase components. This is
|
||||
i.e., how to obtain, install, upgrade, and erase packages. This is
|
||||
the “user’s” perspective of the Nix system — people
|
||||
who want to <emphasis>create</emphasis> components should consult
|
||||
who want to <emphasis>create</emphasis> packages should consult
|
||||
<xref linkend='chap-writing-nix-expressions' />.</para>
|
||||
|
||||
|
||||
@@ -16,8 +16,8 @@ who want to <emphasis>create</emphasis> components should consult
|
||||
|
||||
<para>The main command for package management is <link
|
||||
linkend="sec-nix-env"><command>nix-env</command></link>. You can use
|
||||
it to install, upgrade, and erase components, and to query what
|
||||
components are installed or are available for installation.</para>
|
||||
it to install, upgrade, and erase packages, and to query what
|
||||
packages are installed or are available for installation.</para>
|
||||
|
||||
<para>In Nix, different users can have different “views”
|
||||
on the set of installed applications. That is, there might be lots of
|
||||
@@ -30,10 +30,10 @@ environment</emphasis>, which is just a directory tree consisting of
|
||||
symlinks to the files of the active applications. </para>
|
||||
|
||||
<para>Components are installed from a set of <emphasis>Nix
|
||||
expressions</emphasis> that tell Nix how to build those components,
|
||||
expressions</emphasis> that tell Nix how to build those packages,
|
||||
including, if necessary, their dependencies. There is a collection of
|
||||
Nix expressions called the Nix Package collection that contains
|
||||
components ranging from basic development stuff such as GCC and Glibc,
|
||||
packages ranging from basic development stuff such as GCC and Glibc,
|
||||
to end-user applications like Mozilla Firefox. (Nix is however not
|
||||
tied to the Nix Package collection; you could write your own Nix
|
||||
expressions based on it, or completely new ones.) You can download
|
||||
@@ -41,7 +41,7 @@ the latest version from <link
|
||||
xlink:href='http://nix.cs.uu.nl/dist/nix' />.</para>
|
||||
|
||||
<para>Assuming that you have downloaded and unpacked a release of Nix
|
||||
Packages, you can view the set of available components in the release:
|
||||
Packages, you can view the set of available packages in the release:
|
||||
|
||||
<screen>
|
||||
$ nix-env -qaf nixpkgs-<replaceable>version</replaceable> '*'
|
||||
@@ -74,7 +74,7 @@ gcc-4.1.1</screen>
|
||||
</para>
|
||||
|
||||
<para>It is also possible to see the <emphasis>status</emphasis> of
|
||||
available components, i.e., whether they are installed into the user
|
||||
available packages, i.e., whether they are installed into the user
|
||||
environment and/or present in the system:
|
||||
|
||||
<screen>
|
||||
@@ -86,24 +86,24 @@ IPS bison-1.875d
|
||||
...</screen>
|
||||
|
||||
The first character (<literal>I</literal>) indicates whether the
|
||||
component is installed in your current user environment. The second
|
||||
package is installed in your current user environment. The second
|
||||
(<literal>P</literal>) indicates whether it is present on your system
|
||||
(in which case installing it into your user environment would be a
|
||||
very quick operation). The last one (<literal>S</literal>) indicates
|
||||
whether there is a so-called <emphasis>substitute</emphasis> for the
|
||||
component, which is Nix’s mechanism for doing binary deployment. It
|
||||
just means that Nix knows that it can fetch a pre-built component from
|
||||
package, which is Nix’s mechanism for doing binary deployment. It
|
||||
just means that Nix knows that it can fetch a pre-built package from
|
||||
somewhere (typically a network server) instead of building it
|
||||
locally.</para>
|
||||
|
||||
<para>So now that we have a set of Nix expressions we can build the
|
||||
components contained in them. This is done using <literal>nix-env
|
||||
packages contained in them. This is done using <literal>nix-env
|
||||
-i</literal>. For instance,
|
||||
|
||||
<screen>
|
||||
$ nix-env -f nixpkgs-<replaceable>version</replaceable> -i subversion</screen>
|
||||
|
||||
will install the component called <literal>subversion</literal> (which
|
||||
will install the package called <literal>subversion</literal> (which
|
||||
is, of course, the <link
|
||||
xlink:href='http://subversion.tigris.org/'>Subversion version
|
||||
management system</link>).</para>
|
||||
@@ -112,7 +112,7 @@ management system</link>).</para>
|
||||
Subversion and all its dependencies. This will take quite a while —
|
||||
typically an hour or two on modern machines. Fortunately, there is a
|
||||
faster way (so do a Ctrl-C on that install operation!): you just need
|
||||
to tell Nix that pre-built binaries of all those components are
|
||||
to tell Nix that pre-built binaries of all those packages are
|
||||
available somewhere. This is done using the
|
||||
<command>nix-pull</command> command, which must be supplied with a URL
|
||||
containing a <emphasis>manifest</emphasis> describing what binaries
|
||||
@@ -153,7 +153,7 @@ expressions, use <parameter>-i</parameter> instead of
|
||||
<parameter>-u</parameter>; <parameter>-i</parameter> will remove
|
||||
whatever version is already installed.</para>
|
||||
|
||||
<para>You can also upgrade all components for which there are newer
|
||||
<para>You can also upgrade all packages for which there are newer
|
||||
versions:
|
||||
|
||||
<screen>
|
||||
@@ -199,19 +199,19 @@ set.</para></footnote></para>
|
||||
implementing the ability to allow different users to have different
|
||||
configurations, and to do atomic upgrades and rollbacks. To
|
||||
understand how they work, it’s useful to know a bit about how Nix
|
||||
works. In Nix, components are stored in unique locations in the
|
||||
works. In Nix, packages are stored in unique locations in the
|
||||
<emphasis>Nix store</emphasis> (typically,
|
||||
<filename>/nix/store</filename>). For instance, a particular version
|
||||
of the Subversion component might be stored in a directory
|
||||
of the Subversion package might be stored in a directory
|
||||
<filename>/nix/store/dpmvp969yhdqs7lm2r1a3gng7pyq6vy4-subversion-1.1.3/</filename>,
|
||||
while another version might be stored in
|
||||
<filename>/nix/store/5mq2jcn36ldlmh93yj1n8s9c95pj7c5s-subversion-1.1.2</filename>.
|
||||
The long strings prefixed to the directory names are cryptographic
|
||||
hashes<footnote><para>160-bit truncations of SHA-256 hashes encoded in
|
||||
a base-32 notation, to be precise.</para></footnote> of
|
||||
<emphasis>all</emphasis> inputs involved in building the component —
|
||||
<emphasis>all</emphasis> inputs involved in building the package —
|
||||
sources, dependencies, compiler flags, and so on. So if two
|
||||
components differ in any way, they end up in different locations in
|
||||
packages differ in any way, they end up in different locations in
|
||||
the file system, so they don’t interfere with each other. <xref
|
||||
linkend='fig-user-environments' /> shows a part of a typical Nix
|
||||
store.</para>
|
||||
@@ -231,12 +231,12 @@ $ /nix/store/dpmvp969yhdq...-subversion-1.1.3/bin/svn</screen>
|
||||
|
||||
every time you want to run Subversion. Of course we could set up the
|
||||
<envar>PATH</envar> environment variable to include the
|
||||
<filename>bin</filename> directory of every component we want to use,
|
||||
<filename>bin</filename> directory of every package we want to use,
|
||||
but this is not very convenient since changing <envar>PATH</envar>
|
||||
doesn’t take effect for already existing processes. The solution Nix
|
||||
uses is to create directory trees of symlinks to
|
||||
<emphasis>activated</emphasis> components. These are called
|
||||
<emphasis>user environments</emphasis> and they are components
|
||||
<emphasis>activated</emphasis> packages. These are called
|
||||
<emphasis>user environments</emphasis> and they are packages
|
||||
themselves (though automatically generated by
|
||||
<command>nix-env</command>), so they too reside in the Nix store. For
|
||||
instance, in <xref linkend='fig-user-environments' /> the user
|
||||
@@ -285,8 +285,8 @@ operation, a new user environment and generation link are created
|
||||
based on the current one, and finally the <filename>default</filename>
|
||||
symlink is made to point at the new generation. This last step is
|
||||
atomic on Unix, which explains how we can do atomic upgrades. (Note
|
||||
that the building/installing of new components doesn’t interfere in
|
||||
any way with old components, since they are stored in different
|
||||
that the building/installing of new packages doesn’t interfere in
|
||||
any way with old packages, since they are stored in different
|
||||
locations in the Nix store.)</para>
|
||||
|
||||
<para>If you find that you want to undo a <command>nix-env</command>
|
||||
@@ -352,18 +352,18 @@ This will <emphasis>not</emphasis> change the
|
||||
|
||||
<para><command>nix-env</command> operations such as upgrades
|
||||
(<option>-u</option>) and uninstall (<option>-e</option>) never
|
||||
actually delete components from the system. All they do (as shown
|
||||
actually delete packages from the system. All they do (as shown
|
||||
above) is to create a new user environment that no longer contains
|
||||
symlinks to the “deleted” components.</para>
|
||||
symlinks to the “deleted” packages.</para>
|
||||
|
||||
<para>Of course, since disk space is not infinite, unused components
|
||||
<para>Of course, since disk space is not infinite, unused packages
|
||||
should be removed at some point. You can do this by running the Nix
|
||||
garbage collector. It will remove from the Nix store any component
|
||||
garbage collector. It will remove from the Nix store any package
|
||||
not used (directly or indirectly) by any generation of any
|
||||
profile.</para>
|
||||
|
||||
<para>Note however that as long as old generations reference a
|
||||
component, it will not be deleted. After all, we wouldn’t be able to
|
||||
package, it will not be deleted. After all, we wouldn’t be able to
|
||||
do a rollback otherwise. So in order for garbage collection to be
|
||||
effective, you should also delete (some) old generations. Of course,
|
||||
this should only be done if you are certain that you will not need to
|
||||
@@ -486,7 +486,7 @@ makes the union of each channel’s Nix expressions the default for
|
||||
<screen>
|
||||
$ nix-env -u '*'</screen>
|
||||
|
||||
to upgrade all components in your profile to the latest versions
|
||||
to upgrade all packages in your profile to the latest versions
|
||||
available in the subscribed channels.</para>
|
||||
|
||||
</section>
|
||||
|
||||
@@ -11,7 +11,7 @@ to the following chapters.</para>
|
||||
<orderedlist>
|
||||
|
||||
<listitem><para>Download a source tarball or RPM from <link
|
||||
xlink:href='http://www.cs.uu.nl/groups/ST/Trace/Nix'/>. Build source
|
||||
xlink:href='http://nix.cs.uu.nl/'/>. Build source
|
||||
distributions using the regular sequence:
|
||||
|
||||
<screen>
|
||||
@@ -22,8 +22,9 @@ $ make install <lineannotation>(as root)</lineannotation></screen>
|
||||
|
||||
This will install Nix in <filename>/nix</filename>. You shouldn't
|
||||
change the prefix if at all possible since that will make it
|
||||
impossible to use our pre-built components. Alternatively, you could
|
||||
grab an RPM if you're on an RPM-based system. You should also add
|
||||
impossible to use pre-built binaries from the Nixpkgs channel and
|
||||
other channels. Alternatively, you could grab an RPM if you're on an
|
||||
RPM-based system. You should also add
|
||||
<filename>/nix/etc/profile.d/nix.sh</filename> to your
|
||||
<filename>~/.bashrc</filename> (or some other login
|
||||
file).</para></listitem>
|
||||
@@ -40,14 +41,14 @@ $ nix-channel --add \
|
||||
<screen>
|
||||
$ nix-channel --update</screen>
|
||||
|
||||
Note that this in itself doesn't download any components, it just
|
||||
Note that this in itself doesn't download any packages, it just
|
||||
downloads the Nix expressions that build them and stores them
|
||||
somewhere (under <filename>~/.nix-defexpr</filename>, in case you're
|
||||
curious). Also, it registers the fact that pre-built binaries are
|
||||
available remotely.</para></listitem>
|
||||
|
||||
<listitem><para>See what installable components are currently
|
||||
available in the channel:
|
||||
<listitem><para>See what installable packages are currently available
|
||||
in the channel:
|
||||
|
||||
<screen>
|
||||
$ nix-env -qa ’*’ <lineannotation>(mind the quotes!)</lineannotation>
|
||||
@@ -59,13 +60,13 @@ libxslt-1.1.0
|
||||
|
||||
</para></listitem>
|
||||
|
||||
<listitem><para>Install some components from the channel:
|
||||
<listitem><para>Install some packages from the channel:
|
||||
|
||||
<screen>
|
||||
$ nix-env -i hello firefox <replaceable>...</replaceable> </screen>
|
||||
|
||||
This should download the pre-built components; it should not build
|
||||
them locally (if it does, something went wrong).</para></listitem>
|
||||
This should download pre-built packages; it should not build them
|
||||
locally (if it does, something went wrong).</para></listitem>
|
||||
|
||||
<listitem><para>Test that they work:
|
||||
|
||||
@@ -92,8 +93,8 @@ $ nix-env -e hello</screen>
|
||||
$ nix-channel --update
|
||||
$ nix-env -u '*'</screen>
|
||||
|
||||
The latter command will upgrade each installed component for which
|
||||
there is a “newer” version (as determined by comparing the version
|
||||
The latter command will upgrade each installed package for which there
|
||||
is a “newer” version (as determined by comparing the version
|
||||
numbers).</para></listitem>
|
||||
|
||||
<listitem><para>You can also install specific packages directly from
|
||||
@@ -107,7 +108,7 @@ appear asking you whether it’s okay to install the package. Say
|
||||
installed.</para></listitem>
|
||||
|
||||
<listitem><para>If you're unhappy with the result of a
|
||||
<command>nix-env</command> action (e.g., an upgraded component turned
|
||||
<command>nix-env</command> action (e.g., an upgraded package turned
|
||||
out not to work properly), you can go back:
|
||||
|
||||
<screen>
|
||||
@@ -124,7 +125,7 @@ $ nix-collect-garbage -d</screen>
|
||||
|
||||
<!--
|
||||
The first command deletes old “generations” of your profile (making
|
||||
rollbacks impossible, but also making the components in those old
|
||||
rollbacks impossible, but also making the packages in those old
|
||||
generations available for garbage collection), while the second
|
||||
command actually deletes them.-->
|
||||
|
||||
|
||||
@@ -8,115 +8,271 @@
|
||||
|
||||
<!--==================================================================-->
|
||||
|
||||
<section xml:id="ssec-relnotes-0.11"><title>Release 0.11 (TBA)</title>
|
||||
<section xml:id="ssec-relnotes-0.12"><title>Release 0.12 (TBA)</title>
|
||||
|
||||
<itemizedlist>
|
||||
|
||||
<listitem><para><command>nix-store --dump-db / --load-db</command>.</para></listitem>
|
||||
|
||||
</itemizedlist>
|
||||
|
||||
</section>
|
||||
|
||||
|
||||
<!--==================================================================-->
|
||||
|
||||
<section xml:id="ssec-relnotes-0.11"><title>Release 0.11 (December 31,
|
||||
2007)</title>
|
||||
|
||||
<para>Nix 0.11 has many improvements over the previous stable release.
|
||||
The most important improvement is secure multi-user support. It also
|
||||
features many usability enhancements and language extensions, many of
|
||||
them prompted by NixOS, the purely functional Linux distribution based
|
||||
on Nix. Here is an (incomplete) list:</para>
|
||||
|
||||
|
||||
<itemizedlist>
|
||||
|
||||
|
||||
<listitem><para>TODO: multi-user support. The old setuid method for
|
||||
sharing a store between multiple users has been
|
||||
removed.</para></listitem>
|
||||
<listitem><para>Secure multi-user support. A single Nix store can
|
||||
now be shared between multiple (possible untrusted) users. This is
|
||||
an important feature for NixOS, where it allows non-root users to
|
||||
install software. The old setuid method for sharing a store between
|
||||
multiple users has been removed. Details for setting up a
|
||||
multi-user store can be found in the manual.</para></listitem>
|
||||
|
||||
|
||||
<listitem><para>The new command <command>nix-copy-closure</command>
|
||||
gives you an easy and efficient way to exchange software between
|
||||
machines. It copies the missing parts of the closure of a set of
|
||||
store path to or from a remote machine.</para></listitem>
|
||||
store path to or from a remote machine via
|
||||
<command>ssh</command>.</para></listitem>
|
||||
|
||||
|
||||
<listitem><para><command>nix-prefetch-url</command> now by default
|
||||
computes the SHA-256 hash of the file instead of the MD5 hash. In
|
||||
calls to <function>fetchurl</function> you should pass an
|
||||
<literal>sha256</literal> attribute instead of
|
||||
<literal>md5</literal>. You can pass either a hexadecimal or a
|
||||
base-32 encoding of the hash.</para></listitem>
|
||||
|
||||
|
||||
<listitem><para><command>nix-store</command> has a new operation
|
||||
<option>--read-log</option> (<option>-l</option>)
|
||||
<parameter>paths</parameter> that shows the build log of the given
|
||||
paths.</para></listitem>
|
||||
|
||||
|
||||
<listitem><para>TODO: <varname>allowedReferences</varname> for
|
||||
checking the set of references in the output of a
|
||||
derivation.</para></listitem>
|
||||
<listitem><para>A new kind of string literal: strings between double
|
||||
single-quotes (<literal>''</literal>) have indentation
|
||||
“intelligently” removed. This allows large strings (such as shell
|
||||
scripts or configuration file fragments in NixOS) to cleanly follow
|
||||
the indentation of the surrounding expression. It also requires
|
||||
much less escaping, since <literal>''</literal> is less common in
|
||||
most languages than <literal>"</literal>.</para></listitem>
|
||||
|
||||
|
||||
<listitem><para>TODO: semantic cleanups of string concatenation
|
||||
etc. (mostly in r6740).</para></listitem>
|
||||
|
||||
|
||||
<listitem><para>TODO: now using Berkeley DB 4.5.</para></listitem>
|
||||
|
||||
|
||||
<listitem><para>TODO: option <option>--reregister</option> in
|
||||
<command>nix-store --register-validity</command>.</para></listitem>
|
||||
|
||||
|
||||
<listitem><para>TODO: magic <varname>exportReferencesGraph</varname>
|
||||
attribute.</para></listitem>
|
||||
|
||||
|
||||
<listitem><para>TODO: option <option>--max-silent-time</option>,
|
||||
configuration setting
|
||||
<literal>build-max-silent-time</literal>.</para></listitem>
|
||||
|
||||
|
||||
<listitem><para>TODO: <command>nix-env</command>
|
||||
<option>--set</option>.</para></listitem>
|
||||
<listitem><para><command>nix-env</command> <option>--set</option>
|
||||
modifies the current generation of a profile so that it contains
|
||||
exactly the specified derivation, and nothing else. For example,
|
||||
<literal>nix-env -p /nix/var/nix/profiles/browser --set
|
||||
firefox</literal> lets the profile named
|
||||
<filename>browser</filename> contain just Firefox.</para></listitem>
|
||||
|
||||
|
||||
<listitem><para>TODO: <option>--argstr</option>.</para></listitem>
|
||||
|
||||
|
||||
<listitem><para>TODO: <command>nix-env</command> now maintains meta
|
||||
info about installed packages in user environments. <option>-q
|
||||
--xml --meta</option> to show all meta info.</para></listitem>
|
||||
<listitem><para><command>nix-env</command> now maintains
|
||||
meta-information about installed packages in profiles. The
|
||||
meta-information is the contents of the <varname>meta</varname>
|
||||
attribute of derivations, such as <varname>description</varname> or
|
||||
<varname>homepage</varname>. The command <literal>nix-env -q --xml
|
||||
--meta</literal> shows all meta-information.</para></listitem>
|
||||
|
||||
|
||||
<listitem><para>TODO: <command>nix-env</command>
|
||||
<option>--set-flag</option>. Specific flags:
|
||||
<literal>active</literal>, <literal>priority</literal>,
|
||||
<literal>keep</literal>.</para></listitem>
|
||||
<listitem><para><command>nix-env</command> now uses the
|
||||
<varname>meta.priority</varname> attribute of derivations to resolve
|
||||
filename collisions between packages. Lower priority values denote
|
||||
a higher priority. For instance, the GCC wrapper package and the
|
||||
Binutils package in Nixpkgs both have a file
|
||||
<filename>bin/ld</filename>, so previously if you tried to install
|
||||
both you would get a collision. Now, on the other hand, the GCC
|
||||
wrapper declares a higher priority than Binutils, so the former’s
|
||||
<filename>bin/ld</filename> is symlinked in the user
|
||||
environment.</para></listitem>
|
||||
|
||||
|
||||
<listitem><para><command>nix-env -i / -u</command>: instead of
|
||||
breaking package ties by version, break them by priority and version
|
||||
number. That is, if there are multiple packages with the same name,
|
||||
then pick the package with the highest priority, and only use the
|
||||
version if there are multiple packages with the same
|
||||
priority.</para>
|
||||
|
||||
<para>This makes it possible to mark specific versions/variant in
|
||||
Nixpkgs more or less desirable than others. A typical example would
|
||||
be a beta version of some package (e.g.,
|
||||
<literal>gcc-4.2.0rc1</literal>) which should not be installed even
|
||||
though it is the highest version, except when it is explicitly
|
||||
selected (e.g., <literal>nix-env -i
|
||||
gcc-4.2.0rc1</literal>).</para></listitem>
|
||||
|
||||
|
||||
<listitem><para><command>nix-env --set-flag</command> allows meta
|
||||
attributes of installed packages to be modified. There are several
|
||||
attributes that can be usefully modified, because they affect the
|
||||
behaviour of <command>nix-env</command> or the user environment
|
||||
build script:
|
||||
|
||||
<itemizedlist>
|
||||
|
||||
<listitem><para><varname>meta.priority</varname> can be changed
|
||||
to resolve filename clashes (see above).</para></listitem>
|
||||
|
||||
<listitem><para><varname>meta.keep</varname> can be set to
|
||||
<literal>true</literal> to prevent the package from being
|
||||
upgraded or replaced. Useful if you want to hang on to an older
|
||||
version of a package.</para></listitem>
|
||||
|
||||
<listitem><para><varname>meta.active</varname> can be set to
|
||||
<literal>false</literal> to “disable” the package. That is, no
|
||||
symlinks will be generated to the files of the package, but it
|
||||
remains part of the profile (so it won’t be garbage-collected).
|
||||
Set it back to <literal>true</literal> to re-enable the
|
||||
package.</para></listitem>
|
||||
|
||||
</itemizedlist>
|
||||
|
||||
</para></listitem>
|
||||
|
||||
|
||||
<listitem><para>TODO: <command>nix-env</command> <option>-i</option>
|
||||
/ <option>-u</option> take package priorities into
|
||||
account.</para></listitem>
|
||||
|
||||
|
||||
<listitem><para><command>nix-env -q</command> now has a flag
|
||||
<option>--prebuilt-only</option> (<option>-b</option>) that causes
|
||||
<command>nix-env</command> to show only those derivations whose
|
||||
output is already in the Nix store or that can be substituted (i.e.,
|
||||
downloaded from somewhere). In other words, it shows the packages
|
||||
that can be installed “quickly”, i.e., don’t need to be built from
|
||||
source.</para></listitem>
|
||||
source. The <option>-b</option> flag is also available in
|
||||
<command>nix-env -i</command> and <command>nix-env -u</command> to
|
||||
filter out derivations for which no pre-built binary is
|
||||
available.</para></listitem>
|
||||
|
||||
|
||||
<listitem><para>TODO: new built-ins
|
||||
<function>builtins.attrNames</function>,
|
||||
<function>builtins.filterSource</function>,
|
||||
<function>builtins.sub</function>,
|
||||
<function>builtins.stringLength</function>,
|
||||
<function>builtins.substring</function>.</para></listitem>
|
||||
<listitem><para>The new option <option>--argstr</option> (in
|
||||
<command>nix-env</command>, <command>nix-instantiate</command> and
|
||||
<command>nix-build</command>) is like <option>--arg</option>, except
|
||||
that the value is a string. For example, <literal>--argstr system
|
||||
i686-linux</literal> is equivalent to <literal>--arg system
|
||||
\"i686-linux\"</literal> (note that <option>--argstr</option>
|
||||
prevents annoying quoting around shell arguments).</para></listitem>
|
||||
|
||||
|
||||
<listitem><para>TODO: each subscribed channel is its own attribute
|
||||
in the top-level expression generated for the channel, this allows
|
||||
disambiguation (<command>nix-env -qaA</command>).</para></listitem>
|
||||
<listitem><para><command>nix-store</command> has a new operation
|
||||
<option>--read-log</option> (<option>-l</option>)
|
||||
<parameter>paths</parameter> that shows the build log of the given
|
||||
paths.</para></listitem>
|
||||
|
||||
|
||||
<!--
|
||||
<listitem><para>TODO: semantic cleanups of string concatenation
|
||||
etc. (mostly in r6740).</para></listitem>
|
||||
-->
|
||||
|
||||
|
||||
<listitem><para>TODO: substitutes table is gone, registering
|
||||
substitutes is now much faster.</para></listitem>
|
||||
<listitem><para>Nix now uses Berkeley DB 4.5. The database is
|
||||
upgraded automatically, but you should be careful not to use old
|
||||
versions of Nix that still use Berkeley DB 4.4.</para></listitem>
|
||||
|
||||
|
||||
<!-- foo
|
||||
<listitem><para>TODO: option <option>- -reregister</option> in
|
||||
<command>nix-store - -register-validity</command>.</para></listitem>
|
||||
-->
|
||||
|
||||
|
||||
<listitem><para>The option <option>--max-silent-time</option>
|
||||
(corresponding to the configuration setting
|
||||
<literal>build-max-silent-time</literal>) allows you to set a
|
||||
timeout on builds — if a build produces no output on
|
||||
<literal>stdout</literal> or <literal>stderr</literal> for the given
|
||||
number of seconds, it is terminated. This is useful for recovering
|
||||
automatically from builds that are stuck in an infinite
|
||||
loop.</para></listitem>
|
||||
|
||||
|
||||
<listitem><para><command>nix-channel</command>: each subscribed
|
||||
channel is its own attribute in the top-level expression generated
|
||||
for the channel. This allows disambiguation (e.g. <literal>nix-env
|
||||
-i -A nixpkgs_unstable.firefox</literal>).</para></listitem>
|
||||
|
||||
|
||||
<listitem><para>The substitutes table has been removed from the
|
||||
database. This makes operations such as <command>nix-pull</command>
|
||||
and <command>nix-channel --update</command> much, much
|
||||
faster.</para></listitem>
|
||||
|
||||
|
||||
<listitem><para><command>nix-pull</command> now supports
|
||||
bzip2-compressed manifests. This speeds up
|
||||
channels.</para></listitem>
|
||||
|
||||
|
||||
<listitem><para><command>nix-prefetch-url</command> now has a
|
||||
limited form of caching. This is used by
|
||||
<command>nix-channel</command> to prevent unnecessary downloads when
|
||||
the channel hasn’t changed.</para></listitem>
|
||||
|
||||
|
||||
<listitem><para><command>nix-prefetch-url</command> now by default
|
||||
computes the SHA-256 hash of the file instead of the MD5 hash. In
|
||||
calls to <function>fetchurl</function> you should pass the
|
||||
<literal>sha256</literal> attribute instead of
|
||||
<literal>md5</literal>. You can pass either a hexadecimal or a
|
||||
base-32 encoding of the hash.</para></listitem>
|
||||
|
||||
|
||||
<listitem><para>Nix can now perform builds in an automatically
|
||||
generated “chroot”. This prevents a builder from accessing stuff
|
||||
outside of the Nix store, and thus helps ensure purity. This is an
|
||||
experimental feature.</para></listitem>
|
||||
|
||||
|
||||
<listitem><para>The new command <command>nix-store
|
||||
--optimise</command> reduces Nix store disk space usage by finding
|
||||
identical files in the store and hard-linking them to each other.
|
||||
It typically reduces the size of the store by something like
|
||||
25-35%.</para></listitem>
|
||||
|
||||
|
||||
<listitem><para><filename>~/.nix-defexpr</filename> can now be a
|
||||
directory, in which case the Nix expressions in that directory are
|
||||
combined into an attribute set, with the file names used as the
|
||||
names of the attributes. The command <command>nix-env
|
||||
--import</command> (which set the
|
||||
<filename>~/.nix-defexpr</filename> symlink) is
|
||||
removed.</para></listitem>
|
||||
|
||||
|
||||
<listitem><para>Derivations can specify the new special attribute
|
||||
<varname>allowedReferences</varname> to enforce that the references
|
||||
in the output of a derivation are a subset of a declared set of
|
||||
paths. For example, if <varname>allowedReferences</varname> is an
|
||||
empty list, then the output must not have any references. This is
|
||||
used in NixOS to check that generated files such as initial ramdisks
|
||||
for booting Linux don’t have any dependencies.</para></listitem>
|
||||
|
||||
|
||||
<listitem><para>The new attribute
|
||||
<varname>exportReferencesGraph</varname> allows builders access to
|
||||
the references graph of their inputs. This is used in NixOS for
|
||||
tasks such as generating ISO-9660 images that contain a Nix store
|
||||
populated with the closure of certain paths.</para></listitem>
|
||||
|
||||
|
||||
<listitem><para>Fixed-output derivations (like
|
||||
<function>fetchurl</function>) can define the attribute
|
||||
<varname>impureEnvVars</varname> to allow external environment
|
||||
variables to be passed to builders. This is used in Nixpkgs to
|
||||
support proxy configuration, among other things.</para></listitem>
|
||||
|
||||
|
||||
<listitem><para>Several new built-in functions:
|
||||
<function>builtins.attrNames</function>,
|
||||
<function>builtins.filterSource</function>,
|
||||
<function>builtins.isAttrs</function>,
|
||||
<function>builtins.isFunction</function>,
|
||||
<function>builtins.listToAttrs</function>,
|
||||
<function>builtins.stringLength</function>,
|
||||
<function>builtins.sub</function>,
|
||||
<function>builtins.substring</function>,
|
||||
<function>throw</function>,
|
||||
<function>builtins.trace</function>,
|
||||
<function>builtins.readFile</function>.</para></listitem>
|
||||
|
||||
|
||||
</itemizedlist>
|
||||
|
||||
|
||||
@@ -46,6 +46,11 @@ h3 /* subsections */
|
||||
font-size: 125%;
|
||||
}
|
||||
|
||||
div.simplesect h2
|
||||
{
|
||||
font-size: 110%;
|
||||
}
|
||||
|
||||
div.appendix h3
|
||||
{
|
||||
font-size: 150%;
|
||||
|
||||
@@ -4,7 +4,9 @@
|
||||
<title>Troubleshooting</title>
|
||||
|
||||
|
||||
<para>This section provides solutions for some common problems.</para>
|
||||
<para>This section provides solutions for some common problems. See
|
||||
the <link xlink:href="https://bugs.cs.uu.nl/browse/NIX">Nix
|
||||
bug tracker</link> for a list of currently known issues.</para>
|
||||
|
||||
|
||||
<section><title>Berkeley DB: <quote>Cannot allocate memory</quote></title>
|
||||
@@ -77,6 +79,46 @@ $ nix-store --verify</screen>
|
||||
</section>
|
||||
|
||||
|
||||
<section><title>Berkeley DB out of locks</title>
|
||||
|
||||
<para>It is possible, especially in <command>nix-store
|
||||
--verify</command> or when running the garbage collector, to run out
|
||||
of Berkeley DB locks, like this:
|
||||
|
||||
<screen>
|
||||
$ nix-store --verify
|
||||
checking path existence
|
||||
checking path realisability
|
||||
checking the derivers table
|
||||
checking the references table
|
||||
Berkeley DB error: Lock table is out of available object entries
|
||||
error: Db::get: Cannot allocate memory</screen>
|
||||
|
||||
</para>
|
||||
|
||||
<para>A workaround is to increase the number of locks that Berkeley DB
|
||||
allocates. (The real solution would be for Nix to not use so many
|
||||
locks.) This can be done by putting the following in the file
|
||||
<filename>/nix/var/nix/db/<link
|
||||
xlink:href="http://www.oracle.com/technology/documentation/berkeley-db/db/ref/env/db_config.html">DB_CONFIG</link></filename>:
|
||||
|
||||
<programlisting>
|
||||
set_lk_max_locks 100000
|
||||
set_lk_max_lockers 100000
|
||||
set_lk_max_objects 100000
|
||||
</programlisting>
|
||||
|
||||
(Increase these numbers if necessary.) Then make sure that there are
|
||||
no running Nix processes and delete the Berkeley DB environment:
|
||||
|
||||
<screen>
|
||||
$ rm /nix/var/nix/db/__db.*</screen>
|
||||
|
||||
The Berkeley DB environment is automatically recreated with the new
|
||||
limits when you run any Nix command.</para>
|
||||
|
||||
</section>
|
||||
|
||||
|
||||
<section><title>Collisions in <command>nix-env</command></title>
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
67
install_full.sh
Executable file
67
install_full.sh
Executable file
@@ -0,0 +1,67 @@
|
||||
#! /bin/sh -e
|
||||
|
||||
make clean # comment this out when needed !!!
|
||||
|
||||
export nixstatepath=/nixstate2/nix
|
||||
export ACLOCAL_PATH=/home/wouterdb/.nix-profile/share/aclocal
|
||||
|
||||
if [ "$1" = "full" ]; then
|
||||
nix-env-all-pkgs.sh -i gcc
|
||||
nix-env-all-pkgs.sh -i gnum4
|
||||
nix-env-all-pkgs.sh -i autoconf
|
||||
nix-env-all-pkgs.sh -i automake
|
||||
nix-env-all-pkgs.sh -i gnused
|
||||
nix-env-all-pkgs.sh -i db4
|
||||
nix-env-all-pkgs.sh -i aterm
|
||||
nix-env-all-pkgs.sh -i bzip2
|
||||
nix-env-all-pkgs.sh -i flex
|
||||
nix-env-all-pkgs.sh -i bsdiff
|
||||
nix-env-all-pkgs.sh -i libtool
|
||||
nix-env-all-pkgs.sh -i docbook5
|
||||
nix-env-all-pkgs.sh -i docbook5-xsl
|
||||
nix-env-all-pkgs.sh -i bison
|
||||
nix-env-all-pkgs.sh -i gdb #optional for debugging
|
||||
nix-env-all-pkgs.sh -i gnupatch
|
||||
nix-env-all-pkgs.sh -i gnumake
|
||||
nix-env-all-pkgs.sh -i ext3cow-tools
|
||||
nix-env-all-pkgs.sh -i e3cfsprogs
|
||||
nix-env-all-pkgs.sh -i rsync
|
||||
fi
|
||||
|
||||
if [ "$1" = "full" ] || [ "$1" = "auto" ]; then
|
||||
export AUTOCONF=autoconf
|
||||
export AUTOHEADER=autoheader
|
||||
export AUTOMAKE=automake
|
||||
autoconf
|
||||
autoreconf -f
|
||||
aclocal
|
||||
autoheader
|
||||
automake
|
||||
fi
|
||||
|
||||
./bootstrap.sh
|
||||
|
||||
./configure --with-aterm=$HOME/.nix-profile \
|
||||
--with-bzip2=$HOME/.nix-profile \
|
||||
--with-bdb=$HOME/.nix-profile \
|
||||
--with-docbook-xsl=$HOME/.nix-profile \
|
||||
--with-docbook-rng=/home/wouterdb/.nix-profile/xml/rng/docbook \
|
||||
--with-docbook-xsl=/home/wouterdb/.nix-profile/xml/xsl/docbook \
|
||||
--prefix=$nixstatepath \
|
||||
--with-store-dir=/nix/store \
|
||||
--with-store-state-dir=/nix/state \
|
||||
--with-ext3cow-header=/nix/store/2sm0h2xd1zsm5had53q1pvzmnsn8fy8k-linux-2.6.21/lib/modules/2.6.21-ck1-default/build/include/linux/ext3cow_fs.h \
|
||||
--localstatedir=/nix/var
|
||||
|
||||
#Options from the nix expr
|
||||
#--disable-init-state
|
||||
#--with-store-dir=/nix/store
|
||||
#--localstatedir=/nix/var
|
||||
#--with-aterm=/nix/store/pkmzbb613wa8cwngx8jjb5jaic8yhyzs-aterm-2.4.2-fixes
|
||||
#--with-bdb=/nix/store/4yv4j1cd7i5j3mhs5wpc1kzlz1cj8n82-db4-4.5.20
|
||||
#--with-bzip2=/nix/store/dh0mdgkvhv3pwrf8zp58phpzn9rcm49r-bzip2-1.0.3
|
||||
#--disable-init-state
|
||||
|
||||
|
||||
echo "New state nix version by wouter ..." > doc/manual/NEWS.txt
|
||||
make
|
||||
16
install_install_d.sh
Executable file
16
install_install_d.sh
Executable file
@@ -0,0 +1,16 @@
|
||||
#! /bin/sh -e
|
||||
|
||||
if [ $(whoami) = "root" ]
|
||||
then
|
||||
|
||||
su - wouterdb -c "cd /home/wouterdb/dev/nix-state/; make"
|
||||
make install
|
||||
chown -R wouterdb.wouterdb /nixstate2/nix/
|
||||
|
||||
./restartDaemon.sh
|
||||
|
||||
else
|
||||
echo "You must be ROOT to run this script."
|
||||
exit 0
|
||||
fi
|
||||
|
||||
5
install_make.sh
Executable file
5
install_make.sh
Executable file
@@ -0,0 +1,5 @@
|
||||
#! /bin/sh -e
|
||||
|
||||
make
|
||||
|
||||
|
||||
36
mergeTrunkBackIn.sh
Executable file
36
mergeTrunkBackIn.sh
Executable file
@@ -0,0 +1,36 @@
|
||||
svn merge -r 10855:10943 https://svn.cs.uu.nl:12443/repos/trace/nix/trunk
|
||||
|
||||
#already done:
|
||||
# 8628
|
||||
# 8632
|
||||
# 8634
|
||||
# 8636
|
||||
# 8655
|
||||
# 8691
|
||||
# 8698
|
||||
# 8711
|
||||
# 8864
|
||||
# 9063
|
||||
# 9105
|
||||
# 9207
|
||||
# 9217
|
||||
# 9332
|
||||
# 9429
|
||||
# 9433
|
||||
# 9435
|
||||
# 9437
|
||||
# 9439
|
||||
# 9445
|
||||
# 9476
|
||||
# 9506
|
||||
# 9536
|
||||
# 9549
|
||||
# 9561
|
||||
# 9584
|
||||
# 9751
|
||||
# 10133
|
||||
# 10154
|
||||
# 10531
|
||||
# 10692
|
||||
# 10855
|
||||
# 10943
|
||||
35
misc/vim/syntax/nix.vim
Normal file
35
misc/vim/syntax/nix.vim
Normal file
@@ -0,0 +1,35 @@
|
||||
" Vim syntax file
|
||||
" Language: nix
|
||||
" Maintainer: Marc Weber <marco-oweber@gmx.de>
|
||||
" Modify and commit if you feel that way
|
||||
" Last Change: 2007 Dec
|
||||
|
||||
" Quit when a (custom) syntax file was already loaded
|
||||
if exists("b:current_syntax")
|
||||
finish
|
||||
endif
|
||||
|
||||
syn keyword nixKeyword let throw inherit import true false null with
|
||||
syn keyword nixConditional if else then
|
||||
syn keyword nixBrace ( ) { } =
|
||||
syn keyword nixBuiltin __currentSystem __currentTime __isFunction __getEnv __trace __toPath __pathExists
|
||||
\ __readFile __toXML __toFile __filterSource __attrNames __getAttr __hasAttr __isAttrs __listToAttrs __isList
|
||||
\ __head __tail __add __sub __lessThan __substring __stringLength
|
||||
|
||||
syn match nixAttr "\w\+\ze\s*="
|
||||
syn match nixFuncArg "\zs\w\+\ze\s*:"
|
||||
syn region nixStringParam start=+\${+ end=+}+
|
||||
syn region nixMultiLineComment start=+/\*+ skip=+\\"+ end=+\*/+
|
||||
syn match nixEndOfLineComment "#.*$"
|
||||
syn region nixString start=+"+ skip=+\\"+ end=+"+ contains=nixStringParam
|
||||
|
||||
hi def link nixKeyword Keyword
|
||||
hi def link nixConditional Conditional
|
||||
hi def link nixBrace Special
|
||||
hi def link nixString String
|
||||
hi def link nixBuiltin Special
|
||||
hi def link nixStringParam Macro
|
||||
hi def link nixMultiLineComment Comment
|
||||
hi def link nixEndOfLineComment Comment
|
||||
hi def link nixAttr Identifier
|
||||
hi def link nixFuncArg Identifier
|
||||
@@ -80,7 +80,7 @@
|
||||
|
||||
### Option `build-max-silent-time'
|
||||
#
|
||||
# This option defines the maximum number of seconds that builder can
|
||||
# This option defines the maximum number of seconds that a builder can
|
||||
# go without producing any data on standard output or standard error.
|
||||
# This is useful (for instance in a automated build system) to catch
|
||||
# builds that are stuck in an infinite loop, or to catch remote builds
|
||||
@@ -135,6 +135,44 @@
|
||||
#build-users-group =
|
||||
|
||||
|
||||
### Option `build-use-chroot'
|
||||
#
|
||||
# If set to `true', builds will be performed in a chroot environment,
|
||||
# i.e., the build will be isolated from the normal file system
|
||||
# hierarchy and will only see the Nix store, the temporary build
|
||||
# directory, and the directories configured with the
|
||||
# `build-chroot-dirs' option (such as /proc and /dev). This is useful
|
||||
# to prevent undeclared dependencies on files in directories such as
|
||||
# /usr/bin.
|
||||
#
|
||||
# The use of a chroot requires that Nix is run as root (but you can
|
||||
# still use the "build users" feature to perform builds under
|
||||
# different users than root). Currently, chroot builds only work on
|
||||
# Linux because Nix uses "bind mounts" to make the Nix store and other
|
||||
# directories available inside the chroot.
|
||||
#
|
||||
# The default is `false'.
|
||||
#
|
||||
# Example:
|
||||
# build-use-chroot = true
|
||||
#build-use-chroot = false
|
||||
|
||||
|
||||
### Option `build-chroot-dirs'
|
||||
#
|
||||
# When builds are performed in a chroot environment, Nix will mount
|
||||
# (using `mount --bind' on Linux) some directories from the normal
|
||||
# file system hierarchy inside the chroot. These are the Nix store,
|
||||
# the temporary build directory (usually /tmp/nix-<pid>-<number>) and
|
||||
# the directories listed here. The default is "/dev /proc". Files
|
||||
# in /dev (such as /dev/null) are needed by many builds, and some
|
||||
# files in /proc may also be needed occasionally.
|
||||
#
|
||||
# Example:
|
||||
# build-use-chroot = /dev /proc /bin
|
||||
#build-chroot-dirs = /dev /proc
|
||||
|
||||
|
||||
### Option `system'
|
||||
#
|
||||
# This option specifies the canonical Nix system name of the current
|
||||
|
||||
9
restartDaemon.sh
Executable file
9
restartDaemon.sh
Executable file
@@ -0,0 +1,9 @@
|
||||
#! /bin/sh
|
||||
|
||||
initctl stop nix-daemon
|
||||
killproc.sh nix-worker
|
||||
sleep 2
|
||||
|
||||
#/nixstate2/nix/bin/nix-worker --daemon > /dev/null 2>&1 &
|
||||
/nixstate2/nix/bin/nix-worker --daemon
|
||||
#gdb --args /nixstate2/nix/bin/nix-worker --daemon
|
||||
@@ -2,9 +2,10 @@ bin_SCRIPTS = nix-collect-garbage \
|
||||
nix-pull nix-push nix-prefetch-url \
|
||||
nix-install-package nix-channel nix-build \
|
||||
nix-pack-closure nix-unpack-closure \
|
||||
nix-copy-closure
|
||||
nix-copy-closure
|
||||
|
||||
noinst_SCRIPTS = nix-profile.sh generate-patches.pl find-runtime-roots.pl
|
||||
noinst_SCRIPTS = nix-profile.sh generate-patches.pl \
|
||||
find-runtime-roots.pl build-remote.pl nix-reduce-build
|
||||
|
||||
nix-pull nix-push: readmanifest.pm readconfig.pm download-using-manifests.pl
|
||||
|
||||
@@ -17,6 +18,7 @@ install-exec-local: readmanifest.pm download-using-manifests.pl find-runtime-roo
|
||||
$(INSTALL_PROGRAM) download-using-manifests.pl $(DESTDIR)$(libexecdir)/nix
|
||||
$(INSTALL_PROGRAM) find-runtime-roots.pl $(DESTDIR)$(libexecdir)/nix
|
||||
$(INSTALL_PROGRAM) generate-patches.pl $(DESTDIR)$(libexecdir)/nix
|
||||
$(INSTALL_PROGRAM) build-remote.pl $(DESTDIR)$(libexecdir)/nix
|
||||
$(INSTALL) -d $(DESTDIR)$(sysconfdir)/nix
|
||||
|
||||
include ../substitute.mk
|
||||
@@ -32,4 +34,6 @@ EXTRA_DIST = nix-collect-garbage.in \
|
||||
generate-patches.pl.in \
|
||||
nix-pack-closure.in nix-unpack-closure.in \
|
||||
nix-copy-closure.in \
|
||||
find-runtime-roots.pl.in
|
||||
find-runtime-roots.pl.in \
|
||||
build-remote.pl.in \
|
||||
nix-reduce-build.in
|
||||
|
||||
208
scripts/build-remote.pl.in
Executable file
208
scripts/build-remote.pl.in
Executable file
@@ -0,0 +1,208 @@
|
||||
#! @perl@ -w
|
||||
|
||||
use strict;
|
||||
use Fcntl ':flock';
|
||||
use English '-no_match_vars';
|
||||
|
||||
# General operation:
|
||||
#
|
||||
# Try to find a free machine of type $neededSystem. We do this as
|
||||
# follows:
|
||||
# - We acquire an exclusive lock on $currentLoad/main-lock.
|
||||
# - For each machine $machine of type $neededSystem and for each $slot
|
||||
# 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 release the exclusive lock on $currentLoad/main-lock.
|
||||
# - We perform the build on $neededSystem.
|
||||
# - We release the exclusive lock on $currentLoad/$machine-$slot.
|
||||
#
|
||||
# The nice thing about this scheme is that if we die prematurely, the
|
||||
# locks are released automatically.
|
||||
|
||||
my $loadIncreased = 0;
|
||||
|
||||
my $amWilling = shift @ARGV;
|
||||
my $localSystem = shift @ARGV;
|
||||
my $neededSystem = shift @ARGV;
|
||||
my $drvPath = shift @ARGV;
|
||||
|
||||
sub sendReply {
|
||||
my $reply = shift;
|
||||
open OUT, ">&3" or die;
|
||||
print OUT "$reply\n";
|
||||
close OUT;
|
||||
}
|
||||
|
||||
sub decline {
|
||||
sendReply "decline";
|
||||
exit 0;
|
||||
}
|
||||
|
||||
my $currentLoad = $ENV{"NIX_CURRENT_LOAD"};
|
||||
decline unless defined $currentLoad;
|
||||
mkdir $currentLoad, 0777 or die unless -d $currentLoad;
|
||||
|
||||
my $conf = $ENV{"NIX_REMOTE_SYSTEMS"};
|
||||
decline if !defined $conf || ! -e $conf;
|
||||
|
||||
# Decline if the local system can do the build.
|
||||
decline if $amWilling && ($localSystem eq $neededSystem);
|
||||
|
||||
|
||||
# Otherwise find a willing remote machine.
|
||||
my %machines;
|
||||
my %systemTypes;
|
||||
my %sshKeys;
|
||||
my %maxJobs;
|
||||
my %curJobs;
|
||||
|
||||
|
||||
# Read the list of machines.
|
||||
open CONF, "< $conf" or die;
|
||||
|
||||
while (<CONF>) {
|
||||
chomp;
|
||||
s/\#.*$//g;
|
||||
next if /^\s*$/;
|
||||
/^\s*(\S+)\s+(\S+)\s+(\S+)\s+(\d+)\s*$/ or die;
|
||||
$machines{$1} = "";
|
||||
$systemTypes{$1} = $2;
|
||||
$sshKeys{$1} = $3;
|
||||
$maxJobs{$1} = $4;
|
||||
}
|
||||
|
||||
close CONF;
|
||||
|
||||
|
||||
# Acquire the exclusive lock on $currentLoad/main-lock.
|
||||
my $mainLock = "$currentLoad/main-lock";
|
||||
open MAINLOCK, ">>$mainLock" or die;
|
||||
flock(MAINLOCK, LOCK_EX) or die;
|
||||
|
||||
|
||||
# Find a suitable system.
|
||||
my $rightType = 0;
|
||||
my $machine;
|
||||
LOOP: foreach my $cur (keys %machines) {
|
||||
if ($neededSystem eq $systemTypes{$cur}) {
|
||||
$rightType = 1;
|
||||
|
||||
# We have a machine of the right type. Try to get a lock on
|
||||
# one of the machine's lock files.
|
||||
my $slot = 0;
|
||||
while ($slot < $maxJobs{$cur}) {
|
||||
my $slotLock = "$currentLoad/$cur-$slot";
|
||||
open SLOTLOCK, ">>$slotLock" or die;
|
||||
if (flock(SLOTLOCK, LOCK_EX | LOCK_NB)) {
|
||||
$machine = $cur;
|
||||
last LOOP;
|
||||
}
|
||||
close SLOTLOCK;
|
||||
$slot++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
close MAINLOCK;
|
||||
|
||||
|
||||
# Didn't find one?
|
||||
if (!defined $machine) {
|
||||
if ($rightType) {
|
||||
sendReply "postpone";
|
||||
exit 0;
|
||||
} else {
|
||||
decline;
|
||||
}
|
||||
}
|
||||
|
||||
# Yes we did, accept.
|
||||
sendReply "accept";
|
||||
open IN, "<&4" or die;
|
||||
my $x = <IN>;
|
||||
chomp $x;
|
||||
#print "got $x\n";
|
||||
close IN;
|
||||
|
||||
if ($x ne "okay") {
|
||||
exit 0;
|
||||
}
|
||||
|
||||
|
||||
# Do the actual job.
|
||||
print "BUILDING REMOTE: $drvPath on $machine\n";
|
||||
|
||||
# Make sure that we don't get any SSH passphrase or host key popups -
|
||||
# if there is any problem it should fail, not do something
|
||||
# interactive.
|
||||
$ENV{"DISPLAY"} = "";
|
||||
$ENV{"SSH_PASSWORD_FILE="} = "";
|
||||
$ENV{"SSH_ASKPASS="} = "";
|
||||
|
||||
my $sshOpts = "-i $sshKeys{$machine} -x";
|
||||
|
||||
# Hack to support Cygwin: if we login without a password, we don't
|
||||
# have exactly the same right as when we do. This causes the
|
||||
# Microsoft C compiler to fail with certain flags:
|
||||
#
|
||||
# http://connect.microsoft.com/VisualStudio/feedback/ViewFeedback.aspx?FeedbackID=99676
|
||||
#
|
||||
# So as a workaround, we pass a verbatim password. ssh tries to makes
|
||||
# this very hard; the trick is to make it call SSH_ASKPASS to get the
|
||||
# password. (It only calls this command when there is no controlling
|
||||
# terminal, but Nix ensures that is is the case. When doing this
|
||||
# manually, use setsid(1).)
|
||||
if ($sshKeys{$machine} =~ /^password:/) {
|
||||
my $passwordFile = $sshKeys{$machine};
|
||||
$passwordFile =~ s/^password://;
|
||||
$sshOpts = "ssh -x";
|
||||
$ENV{"SSH_PASSWORD_FILE"} = $passwordFile;
|
||||
$ENV{"SSH_ASKPASS"} = "/tmp/writepass";
|
||||
|
||||
open WRITEPASS, ">/tmp/writepass" or die;
|
||||
print WRITEPASS "#! /bin/sh\ncat \"\$SSH_PASSWORD_FILE\"";
|
||||
close WRITEPASS;
|
||||
chmod 0755, "/tmp/writepass" or die;
|
||||
}
|
||||
|
||||
my $inputs = `cat inputs`; die if ($? != 0);
|
||||
$inputs =~ s/\n/ /g;
|
||||
|
||||
my $outputs = `cat outputs`; die if ($? != 0);
|
||||
$outputs =~ s/\n/ /g;
|
||||
|
||||
print "COPYING INPUTS...\n";
|
||||
|
||||
my $maybeSign = "";
|
||||
$maybeSign = "--sign" if -e "/nix/etc/nix/signing-key.sec";
|
||||
|
||||
system("NIX_SSHOPTS=\"$sshOpts\" nix-copy-closure $machine $maybeSign $drvPath $inputs") == 0
|
||||
or die "cannot copy inputs to $machine: $?";
|
||||
|
||||
print "BUILDING...\n";
|
||||
|
||||
system("ssh $sshOpts $machine 'nix-store -rvvK $drvPath'") == 0
|
||||
or die "remote build on $machine failed: $?";
|
||||
|
||||
print "REMOTE BUILD DONE: $drvPath on $machine\n";
|
||||
|
||||
foreach my $output (split '\n', $outputs) {
|
||||
my $maybeSignRemote = "";
|
||||
$maybeSignRemote = "--sign" if $UID != 0;
|
||||
|
||||
system("ssh $sshOpts $machine 'nix-store --export $maybeSignRemote $output' > dump") == 0
|
||||
or die "cannot copy $output from $machine: $?";
|
||||
|
||||
# This doesn't work yet, since the caller has a lock on the output
|
||||
# path. We should move towards lock-free invocation of build
|
||||
# hooks and substitutes.
|
||||
#system("nix-store --import < dump") == 0
|
||||
# or die "cannot import $output: $?";
|
||||
|
||||
# Hack: skip the first 8 bytes (the nix-store --export next
|
||||
# archive marker). The archive follows.
|
||||
system("(dd bs=1 count=8 of=/dev/null && cat) < dump | nix-store --restore $output") == 0
|
||||
or die "cannot restore $output: $?";
|
||||
}
|
||||
@@ -19,8 +19,6 @@ chdir $tmpDir or die "cannot change to `$tmpDir': $!";
|
||||
my $tmpNar = "$tmpDir/nar";
|
||||
my $tmpNar2 = "$tmpDir/nar2";
|
||||
|
||||
END { unlink $tmpNar; unlink $tmpNar2; rmdir $tmpDir; }
|
||||
|
||||
|
||||
# Load all manifests.
|
||||
my %narFiles;
|
||||
@@ -59,12 +57,29 @@ elsif ($ARGV[0] eq "--query-info") {
|
||||
}
|
||||
print "$storePath\n";
|
||||
print "$info->{deriver}\n";
|
||||
|
||||
my @references = split " ", $info->{references};
|
||||
my $count = scalar @references;
|
||||
print "$count\n";
|
||||
foreach my $reference (@references) {
|
||||
print "$reference\n";
|
||||
}
|
||||
}
|
||||
|
||||
my $isStateStorePath = `@bindir@/nix-state --is-state-store-path-download-using-manifests $storePath`;
|
||||
if($isStateStorePath ne "true" && $isStateStorePath ne "false"){
|
||||
die "The call for isStateStorePath must return true or false.....";
|
||||
}
|
||||
|
||||
if($isStateStorePath eq "true"){
|
||||
my @stateReferences = split " ", $info->{stateReferences};
|
||||
my $scount = scalar @stateReferences;
|
||||
print "$scount\n";
|
||||
foreach my $stateReference (@stateReferences) {
|
||||
print "$stateReference\n";
|
||||
}
|
||||
|
||||
print "$info->{revision}\n";
|
||||
}
|
||||
}
|
||||
exit 0;
|
||||
}
|
||||
|
||||
@@ -77,9 +77,9 @@ EOF
|
||||
push @instArgs, ("--attr", $ARGV[$n]);
|
||||
}
|
||||
|
||||
elsif ($arg eq "--arg") {
|
||||
die "$0: `--arg' requires two arguments\n" unless $n + 2 < scalar @ARGV;
|
||||
push @instArgs, ("--arg", $ARGV[$n + 1], $ARGV[$n + 2]);
|
||||
elsif ($arg eq "--arg" || $arg eq "--argstr") {
|
||||
die "$0: `$arg' requires two arguments\n" unless $n + 2 < scalar @ARGV;
|
||||
push @instArgs, ($arg, $ARGV[$n + 1], $ARGV[$n + 2]);
|
||||
$n += 2;
|
||||
}
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@ $binDir = "@bindir@" unless defined $binDir;
|
||||
|
||||
if (scalar @ARGV < 1) {
|
||||
print STDERR <<EOF
|
||||
Usage: nix-copy-closure [--from | --to] HOSTNAME [--sign] PATHS...
|
||||
Usage: nix-copy-closure [--from | --to] HOSTNAME [--sign] [--gzip] PATHS...
|
||||
EOF
|
||||
;
|
||||
exit 1;
|
||||
|
||||
@@ -36,6 +36,28 @@ if test -n "$expHash"; then
|
||||
fi
|
||||
|
||||
|
||||
mkTempDir() {
|
||||
local i=0
|
||||
while true; do
|
||||
if test -z "$TMPDIR"; then TMPDIR=/tmp; fi
|
||||
tmpPath=$TMPDIR/nix-prefetch-url-$$-$i
|
||||
if mkdir "$tmpPath"; then break; fi
|
||||
# !!! to bad we can't check for ENOENT in mkdir, so this check
|
||||
# is slightly racy (it bombs out if somebody just removed
|
||||
# $tmpPath...).
|
||||
if ! test -e "$tmpPath"; then exit 1; fi
|
||||
i=$((i + 1))
|
||||
done
|
||||
trap removeTempDir EXIT SIGINT SIGQUIT
|
||||
}
|
||||
|
||||
removeTempDir() {
|
||||
if test -n "$tmpPath"; then
|
||||
rm -rf "$tmpPath" || true
|
||||
fi
|
||||
}
|
||||
|
||||
|
||||
doDownload() {
|
||||
@curl@ $cacheFlags --fail -# --location --max-redirs 20 --disable-epsv \
|
||||
--cookie-jar $tmpPath/cookies "$url" -o $tmpFile
|
||||
@@ -46,9 +68,8 @@ doDownload() {
|
||||
# download the file and add it to the store.
|
||||
if test -z "$finalPath"; then
|
||||
|
||||
tmpPath=/tmp/nix-prefetch-url-$$ # !!! security?
|
||||
mkTempDir
|
||||
tmpFile=$tmpPath/$name
|
||||
mkdir $tmpPath # !!! retry if tmpPath already exists
|
||||
|
||||
# Optionally do timestamp-based caching of the download.
|
||||
# Actually, the only thing that we cache in $NIX_DOWNLOAD_CACHE is
|
||||
@@ -98,8 +119,6 @@ if test -z "$finalPath"; then
|
||||
# Add the downloaded file to the Nix store.
|
||||
finalPath=$(@bindir@/nix-store --add-fixed "$hashType" $tmpFile)
|
||||
|
||||
if test -n "$tmpPath"; then rm -rf $tmpPath || true; fi
|
||||
|
||||
if test -n "$expHash" -a "$expHash" != "$hash"; then
|
||||
echo "hash mismatch for URL \`$url'" >&2
|
||||
exit 1
|
||||
|
||||
@@ -19,6 +19,11 @@ $stateDir = "@localstatedir@/nix" unless defined $stateDir;
|
||||
my $storeDir = $ENV{"NIX_STORE_DIR"};
|
||||
$storeDir = "@storedir@" unless defined $storeDir;
|
||||
|
||||
my $storeStateDir = $ENV{"NIX_STORE_STATE_DIR"};
|
||||
$storeStateDir = "@storestatedir@" unless defined $storeStateDir;
|
||||
|
||||
my $ext3cowheader = $ENV{"NIX_EXT3_COW_HEADER"};
|
||||
$ext3cowheader = "@ext3cowheader@" unless defined $ext3cowheader;
|
||||
|
||||
# Prevent access problems in shared-stored installations.
|
||||
umask 0022;
|
||||
|
||||
@@ -116,9 +116,9 @@ print NIX "]";
|
||||
close NIX;
|
||||
|
||||
|
||||
# Instantiate store expressions from the Nix expression.
|
||||
# Instantiate store derivations from the Nix expression.
|
||||
my @storeExprs;
|
||||
print STDERR "instantiating store expressions...\n";
|
||||
print STDERR "instantiating store derivations...\n";
|
||||
my $pid = open(READ, "$binDir/nix-instantiate $nixExpr|")
|
||||
or die "cannot run nix-instantiate";
|
||||
while (<READ>) {
|
||||
@@ -146,7 +146,7 @@ while (scalar @tmp > 0) {
|
||||
# probably wouldn't make that much sense; pumping lots of data
|
||||
# around just to compress them won't gain that much.
|
||||
$ENV{"NIX_BUILD_HOOK"} = "";
|
||||
my $pid = open(READ, "$binDir/nix-store --realise @tmp2|")
|
||||
my $pid = open(READ, "$binDir/nix-store --no-build-hook --realise @tmp2|")
|
||||
or die "cannot run nix-store";
|
||||
while (<READ>) {
|
||||
chomp;
|
||||
|
||||
68
scripts/nix-reduce-build.in
Normal file
68
scripts/nix-reduce-build.in
Normal file
@@ -0,0 +1,68 @@
|
||||
#! @shell@
|
||||
|
||||
WORKING_DIRECTORY=$(mktemp -d "${TMPDIR:-/tmp}"/nix-reduce-build-XXXXXX);
|
||||
cd "$WORKING_DIRECTORY";
|
||||
|
||||
if test -z "$1" ; then
|
||||
echo 'nix-reduce-build (paths or Nix expressions) -- (logins at remote computers)' >&2
|
||||
echo As in: >&2
|
||||
echo nix-reduce-build /etc/nixos/nixos -- user@somewhere.nowhere.example.org >&2
|
||||
exit;
|
||||
fi;
|
||||
|
||||
while ! test "$1" = "--" || test "$1" = "" ; do
|
||||
echo "$1" >> initial; >&2
|
||||
shift;
|
||||
done
|
||||
shift;
|
||||
echo Will work on $(cat initial | wc -l) targets. >&2
|
||||
|
||||
while read ; do
|
||||
case "$REPLY" in
|
||||
${NIX_STORE_PATH:-/nix/store}/*)
|
||||
echo "$REPLY" >> paths; >&2
|
||||
;;
|
||||
*)
|
||||
nix-instantiate "$REPLY" >> paths;
|
||||
;;
|
||||
esac;
|
||||
done < initial;
|
||||
echo Proceeding $(cat paths | wc -l) paths. >&2
|
||||
|
||||
while read; do
|
||||
case "$REPLY" in
|
||||
*.drv)
|
||||
echo "$REPLY" >> derivers; >&2
|
||||
;;
|
||||
*)
|
||||
nix-store --query --deriver "$REPLY" >>derivers;
|
||||
;;
|
||||
esac;
|
||||
done < paths;
|
||||
echo Found $(cat derivers | wc -l) derivers. >&2
|
||||
|
||||
cat derivers | xargs nix-store --query -R > derivers-closure;
|
||||
echo Proceeding at most $(cat derivers-closure | wc -l) derivers. >&2
|
||||
|
||||
cat derivers-closure | egrep '[.]drv$' | xargs nix-store --query --outputs > wanted-paths;
|
||||
cat derivers-closure | egrep -v '[.]drv$' >> wanted-paths;
|
||||
echo Prepared $(cat wanted-paths | wc -l) paths to get. >&2
|
||||
|
||||
cat wanted-paths | xargs nix-store --check-validity --print-invalid > needed-paths;
|
||||
echo We need $(cat needed-paths | wc -l) paths. >&2
|
||||
|
||||
if test -z "$1" ; then
|
||||
cat needed-paths;
|
||||
fi;
|
||||
|
||||
for i in "$@"; do
|
||||
cat needed-paths | while read; do
|
||||
nix-copy-closure --from "$i" --gzip "$REPLY" </dev/null || true;
|
||||
done;
|
||||
mv needed-paths wanted-paths;
|
||||
cat wanted-paths | xargs nix-store --check-validity --print-invalid > needed-paths;
|
||||
echo We still need $(cat needed-paths | wc -l) paths. >&2
|
||||
done;
|
||||
|
||||
cd /
|
||||
rm -r "$WORKING_DIRECTORY"
|
||||
@@ -1,5 +1,5 @@
|
||||
SUBDIRS = bin2c boost libutil libstore libmain nix-store nix-hash \
|
||||
SUBDIRS = bin2c boost libutil libext3cow libstore libmain nix-store nix-hash \
|
||||
libexpr nix-instantiate nix-env nix-worker nix-setuid-helper \
|
||||
nix-log2xml bsdiff-4.3
|
||||
nix-log2xml bsdiff-4.3 nix-state
|
||||
|
||||
EXTRA_DIST = aterm-helper.pl
|
||||
|
||||
@@ -36,6 +36,7 @@ __FBSDID("$FreeBSD: src/usr.bin/bsdiff/bspatch/bspatch.c,v 1.1 2005/08/06 01:59:
|
||||
#include <errno.h>
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
static off_t offtin(u_char *buf)
|
||||
{
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
#include <iostream>
|
||||
|
||||
#include "eval.hh"
|
||||
#include "parser.hh"
|
||||
#include "hash.hh"
|
||||
@@ -15,19 +13,6 @@
|
||||
|
||||
|
||||
namespace nix {
|
||||
|
||||
|
||||
int cacheTerms;
|
||||
|
||||
bool shortCircuit;
|
||||
bool closedTerms; // don't substitute under terms known to be closed
|
||||
bool substCache; // memoization of the term substitution function
|
||||
bool posInfo; // attach position info to functions, assertions, attributes
|
||||
|
||||
#define maxActiveCalls 4096
|
||||
|
||||
ATerm activeCalls[maxActiveCalls];
|
||||
unsigned int activeCallsCount = 0;
|
||||
|
||||
|
||||
EvalState::EvalState()
|
||||
@@ -38,15 +23,6 @@ EvalState::EvalState()
|
||||
initNixExprHelpers();
|
||||
|
||||
addPrimOps();
|
||||
|
||||
if (!string2Int(getEnv("NIX_TERM_CACHE"), cacheTerms)) cacheTerms = 1;
|
||||
shortCircuit = getEnv("NIX_SHORT_CIRCUIT", "0") == "1";
|
||||
strictMode = getEnv("NIX_STRICT", "0") == "1";
|
||||
closedTerms = getEnv("NIX_CLOSED_TERMS", "1") == "1";
|
||||
substCache = getEnv("NIX_SUBST_CACHE", "1") == "1";
|
||||
posInfo = getEnv("NIX_POS_INFO", "1") == "1";
|
||||
|
||||
ATprotectMemory(activeCalls, maxActiveCalls);
|
||||
}
|
||||
|
||||
|
||||
@@ -93,19 +69,6 @@ LocalNoInline(void addErrorPrefix(Error & e, const char * s, const string & s2,
|
||||
}
|
||||
|
||||
|
||||
Expr speculativeEval(EvalState & state, Expr e)
|
||||
{
|
||||
if (!state.strictMode) return e;
|
||||
try {
|
||||
return evalExpr(state, e);
|
||||
} catch (EvalError & err) {
|
||||
/* ignore, pass the original arg and depend on
|
||||
laziness */
|
||||
return e;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* Substitute an argument set into the body of a function. */
|
||||
static Expr substArgs(EvalState & state,
|
||||
Expr body, ATermList formals, Expr arg)
|
||||
@@ -117,7 +80,7 @@ static Expr substArgs(EvalState & state,
|
||||
ATermMap args;
|
||||
queryAllAttrs(arg, args);
|
||||
for (ATermMap::const_iterator i = args.begin(); i != args.end(); ++i)
|
||||
subs.set(i->key, speculativeEval(state, i->value));
|
||||
subs.set(i->key, i->value);
|
||||
|
||||
/* Get the formal arguments. */
|
||||
ATermVector defsUsed;
|
||||
@@ -426,7 +389,7 @@ Expr autoCallFunction(Expr e, const ATermMap & args)
|
||||
Expr name, def, value; ATerm values, def2;
|
||||
if (!matchFormal(*i, name, values, def2)) abort();
|
||||
if ((value = args.get(name)))
|
||||
actualArgs.set(name, makeAttrRHS(allocCell(value), makeNoPos()));
|
||||
actualArgs.set(name, makeAttrRHS(value, makeNoPos()));
|
||||
else if (!matchDefaultValue(def2, def))
|
||||
throw TypeError(format("cannot auto-call a function that has an argument without a default value (`%1%')")
|
||||
% aterm2String(name));
|
||||
@@ -494,7 +457,7 @@ LocalNoInline(Expr evalCall(EvalState & state, Expr fun, Expr arg))
|
||||
else if (matchFunction(fun, formals, body, pos)) {
|
||||
arg = evalExpr(state, arg);
|
||||
try {
|
||||
return evalExpr(state, substArgs(state, allocCells(body), formals, arg));
|
||||
return evalExpr(state, substArgs(state, body, formals, arg));
|
||||
} catch (Error & e) {
|
||||
addErrorPrefix(e, "while evaluating the function at %1%:\n",
|
||||
showPos(pos));
|
||||
@@ -504,10 +467,9 @@ LocalNoInline(Expr evalCall(EvalState & state, Expr fun, Expr arg))
|
||||
|
||||
else if (matchFunction1(fun, name, body, pos)) {
|
||||
try {
|
||||
arg = speculativeEval(state, arg);
|
||||
ATermMap subs(1);
|
||||
subs.set(name, allocCell(arg));
|
||||
return evalExpr(state, substitute(Substitution(0, &subs), allocCells(body)));
|
||||
subs.set(name, arg);
|
||||
return evalExpr(state, substitute(Substitution(0, &subs), body));
|
||||
} catch (Error & e) {
|
||||
addErrorPrefix(e, "while evaluating the function at %1%:\n",
|
||||
showPos(pos));
|
||||
@@ -662,10 +624,6 @@ Expr evalExpr2(EvalState & state, Expr e)
|
||||
|
||||
Expr e1, e2, e3;
|
||||
ATerm name, pos;
|
||||
|
||||
int bla;
|
||||
if (matchCell(e, bla, e1)) e = e1;
|
||||
|
||||
AFun sym = ATgetAFun(e);
|
||||
|
||||
/* Normal forms. */
|
||||
@@ -757,141 +715,41 @@ Expr evalExpr2(EvalState & state, Expr e)
|
||||
if (matchOpConcat(e, e1, e2)) return evalOpConcat(state, e1, e2);
|
||||
|
||||
/* Barf. */
|
||||
//printMsg(lvlError, format("%1%") % e);
|
||||
abort();
|
||||
}
|
||||
|
||||
|
||||
class ShortCircuit
|
||||
{
|
||||
};
|
||||
|
||||
|
||||
unsigned int fnord;
|
||||
|
||||
|
||||
void maybeShortCircuit(EvalState & state, Expr e, Expr nf)
|
||||
{
|
||||
for (unsigned int i = 0; i < activeCallsCount; ++i) {
|
||||
Expr fun, arg;
|
||||
if (!matchCall(activeCalls[i], fun, arg)) abort();
|
||||
if (arg == e) {
|
||||
//printMsg(lvlError, format("blaat"));
|
||||
//printMsg(lvlError, format("blaat %1% %2% %3%") % fun % arg % e);
|
||||
Expr res = state.normalForms.get(makeCall(fun, nf));
|
||||
if (res) {
|
||||
fnord++;
|
||||
//printMsg(lvlError, format("blaat"));
|
||||
throw ShortCircuit();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Expr evalExpr(EvalState & state, Expr e)
|
||||
{
|
||||
checkInterrupt();
|
||||
|
||||
#if 1
|
||||
#if 0
|
||||
startNest(nest, lvlVomit,
|
||||
format("evaluating expression: %1%") % e);
|
||||
#endif
|
||||
|
||||
state.nrEvaluated++;
|
||||
|
||||
if (cacheTerms == 0) return evalExpr2(state, e);
|
||||
|
||||
if (cacheTerms == 2) {
|
||||
int pseudoAddr;
|
||||
Expr e2;
|
||||
if (!matchCell(e, pseudoAddr, e2)) return evalExpr2(state, e);
|
||||
}
|
||||
|
||||
/* Consult the memo table to quickly get the normal form of
|
||||
previously evaluated expressions. */
|
||||
Expr nf = state.normalForms.get(e);
|
||||
if (nf) {
|
||||
//if (nf == makeBlackHole())
|
||||
// throwEvalError("infinite recursion encountered");
|
||||
if (nf == makeBlackHole())
|
||||
throwEvalError("infinite recursion encountered");
|
||||
state.nrCached++;
|
||||
return nf;
|
||||
}
|
||||
|
||||
Expr fun, arg;
|
||||
if (shortCircuit && matchCall(e, fun, arg)) {
|
||||
|
||||
#if 0
|
||||
Expr arg2 = state.normalForms.get(arg);
|
||||
if (arg2) { /* the evaluated argument is now known */
|
||||
//printMsg(lvlError, "foo");
|
||||
/* do we know the result of the same function called
|
||||
with the evaluated argument? */
|
||||
Expr res = state.normalForms.get(makeCall(fun, arg2));
|
||||
if (res) { /* woohoo! */
|
||||
printMsg(lvlError, "dingdong");
|
||||
state.normalForms.set(e, res);
|
||||
return res;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
assert(activeCallsCount < maxActiveCalls);
|
||||
activeCalls[activeCallsCount++] = e;
|
||||
|
||||
//state.normalForms.set(e, makeBlackHole());
|
||||
try {
|
||||
nf = evalExpr2(state, e);
|
||||
}
|
||||
catch (ShortCircuit & exception) {
|
||||
//printMsg(lvlError, "catch!");
|
||||
Expr arg2 = state.normalForms.get(arg);
|
||||
if (arg2) { /* the evaluated argument is now known */
|
||||
/* do we know the result of the same function called
|
||||
with the evaluated argument? */
|
||||
Expr res = state.normalForms.get(makeCall(fun, arg2));
|
||||
if (res) { /* woohoo! */
|
||||
//printMsg(lvlError, "woohoo!");
|
||||
//printMsg(lvlError, format("woohoo! %1% %2% %3% %4%") % fun % arg % arg2 % res);
|
||||
activeCallsCount--;
|
||||
state.normalForms.set(e, res);
|
||||
maybeShortCircuit(state, e, res);
|
||||
return res;
|
||||
}
|
||||
}
|
||||
activeCallsCount--;
|
||||
state.normalForms.remove(e);
|
||||
throw; /* not for us */
|
||||
}
|
||||
catch (...) {
|
||||
activeCallsCount--;
|
||||
state.normalForms.remove(e);
|
||||
throw;
|
||||
}
|
||||
activeCallsCount--;
|
||||
state.normalForms.set(e, nf);
|
||||
Expr arg2 = state.normalForms.get(arg);
|
||||
if (arg2) state.normalForms.set(makeCall(fun, arg2), nf);
|
||||
maybeShortCircuit(state, e, nf);
|
||||
return nf;
|
||||
|
||||
}
|
||||
|
||||
else {
|
||||
|
||||
/* Otherwise, evaluate and memoize. */
|
||||
//state.normalForms.set(e, makeBlackHole());
|
||||
try {
|
||||
nf = evalExpr2(state, e);
|
||||
} catch (...) {
|
||||
state.normalForms.remove(e);
|
||||
throw;
|
||||
}
|
||||
state.normalForms.set(e, nf);
|
||||
if (shortCircuit) maybeShortCircuit(state, e, nf);
|
||||
return nf;
|
||||
|
||||
/* Otherwise, evaluate and memoize. */
|
||||
state.normalForms.set(e, makeBlackHole());
|
||||
try {
|
||||
nf = evalExpr2(state, e);
|
||||
} catch (Error & err) {
|
||||
state.normalForms.remove(e);
|
||||
throw;
|
||||
}
|
||||
state.normalForms.set(e, nf);
|
||||
return nf;
|
||||
}
|
||||
|
||||
|
||||
@@ -987,24 +845,16 @@ extern "C" {
|
||||
unsigned long AT_calcAllocatedSize();
|
||||
}
|
||||
|
||||
|
||||
unsigned int substs = 0;
|
||||
unsigned int substsCached = 0;
|
||||
|
||||
|
||||
void printEvalStats(EvalState & state)
|
||||
{
|
||||
char x;
|
||||
bool showStats = getEnv("NIX_SHOW_STATS", "0") != "0";
|
||||
printMsg(lvlError, format("FNORD %1%") % fnord);
|
||||
printMsg(showStats ? lvlInfo : lvlDebug,
|
||||
format("evaluated %1% expressions, %2% cache hits, %3%%% efficiency, used %4% ATerm bytes, used %5% bytes of stack space, %6% substitutions (%7% cached)")
|
||||
format("evaluated %1% expressions, %2% cache hits, %3%%% efficiency, used %4% ATerm bytes, used %5% bytes of stack space")
|
||||
% state.nrEvaluated % state.nrCached
|
||||
% ((float) state.nrCached / (float) state.nrEvaluated * 100)
|
||||
% AT_calcAllocatedSize()
|
||||
% (&x - deepestStack)
|
||||
% substs
|
||||
% substsCached);
|
||||
% (&x - deepestStack));
|
||||
if (showStats)
|
||||
printATermMapStats();
|
||||
}
|
||||
|
||||
@@ -16,7 +16,7 @@ class Hash;
|
||||
typedef std::map<Path, PathSet> DrvRoots;
|
||||
typedef std::map<Path, Hash> DrvHashes;
|
||||
|
||||
/* Cache for calls to addToStore(); maps source paths to the store
|
||||
/* Cache for calls to addToStore(); maps source paths to the store //THIS OK ????
|
||||
paths. */
|
||||
typedef std::map<Path, Path> SrcToStore;
|
||||
|
||||
@@ -27,9 +27,6 @@ struct EvalState;
|
||||
typedef Expr (* PrimOp) (EvalState &, const ATermVector & args);
|
||||
|
||||
|
||||
extern int cacheTerms; // 0 = don't, 1 = do, 2 = "cell" terms only
|
||||
|
||||
|
||||
struct EvalState
|
||||
{
|
||||
ATermMap normalForms;
|
||||
@@ -41,10 +38,6 @@ struct EvalState
|
||||
unsigned int nrEvaluated;
|
||||
unsigned int nrCached;
|
||||
|
||||
bool strictMode;
|
||||
|
||||
ATermMap parsings; /* path -> expr mapping */
|
||||
|
||||
EvalState();
|
||||
|
||||
void addPrimOps();
|
||||
|
||||
@@ -35,6 +35,53 @@ string DrvInfo::queryOutPath(EvalState & state) const
|
||||
return outPath;
|
||||
}
|
||||
|
||||
string DrvInfo::queryStateIdentifier(EvalState & state) const
|
||||
{
|
||||
if (stateIdentifier == "") {
|
||||
ATermMap attrs2 = *attrs;
|
||||
|
||||
for (ATermMap::const_iterator i = attrs2.begin(); i != attrs2.end(); ++i) {
|
||||
|
||||
string key = (string)aterm2String(i->key); //cast because aterm2String returns a char*
|
||||
//printMsg(lvlError, format("ATTR2: '%1%'") % key);
|
||||
if(key == "stateIdentifier"){
|
||||
PathSet context;
|
||||
string value = coerceToString(state, i->value, context);
|
||||
(string &) stateIdentifier = value;
|
||||
}
|
||||
}
|
||||
|
||||
//If still empty
|
||||
if (trim(stateIdentifier) == "")
|
||||
(string &) stateIdentifier = "__NOSTATE__";
|
||||
}
|
||||
|
||||
return stateIdentifier;
|
||||
}
|
||||
|
||||
string DrvInfo::queryRuntimeStateArgs(EvalState & state) const
|
||||
{
|
||||
if (runtimeStateArgs == "") {
|
||||
ATermMap attrs2 = *attrs;
|
||||
|
||||
for (ATermMap::const_iterator i = attrs2.begin(); i != attrs2.end(); ++i) {
|
||||
|
||||
string key = (string)aterm2String(i->key); //cast because aterm2String returns a char*
|
||||
if(key == "runtimeStateArgs"){
|
||||
PathSet context;
|
||||
string value = coerceToString(state, i->value, context);
|
||||
(string &) runtimeStateArgs = value;
|
||||
}
|
||||
}
|
||||
|
||||
//If still empty
|
||||
if (trim(runtimeStateArgs) == "")
|
||||
(string &) runtimeStateArgs = "__NOARGS__";
|
||||
}
|
||||
|
||||
return runtimeStateArgs;
|
||||
}
|
||||
|
||||
|
||||
MetaInfo DrvInfo::queryMetaInfo(EvalState & state) const
|
||||
{
|
||||
@@ -102,20 +149,23 @@ static bool getDerivation(EvalState & state, Expr e,
|
||||
|
||||
boost::shared_ptr<ATermMap> attrs(new ATermMap());
|
||||
queryAllAttrs(e, *attrs, false);
|
||||
|
||||
|
||||
Expr a = attrs->get(toATerm("type"));
|
||||
if (!a || evalStringNoCtx(state, a) != "derivation") return true;
|
||||
if (!a || evalStringNoCtx(state, a) != "derivation")
|
||||
return true;
|
||||
|
||||
/* Remove spurious duplicates (e.g., an attribute set like
|
||||
`rec { x = derivation {...}; y = x;}'. */
|
||||
if (doneExprs.find(e) != doneExprs.end()) return false;
|
||||
if (doneExprs.find(e) != doneExprs.end())
|
||||
return false;
|
||||
doneExprs.insert(e);
|
||||
|
||||
DrvInfo drv;
|
||||
|
||||
a = attrs->get(toATerm("name"));
|
||||
/* !!! We really would like to have a decent back trace here. */
|
||||
if (!a) throw TypeError("derivation name missing");
|
||||
if (!a)
|
||||
throw TypeError("derivation name missing");
|
||||
drv.name = evalStringNoCtx(state, a);
|
||||
|
||||
a = attrs->get(toATerm("system"));
|
||||
@@ -125,7 +175,9 @@ static bool getDerivation(EvalState & state, Expr e,
|
||||
drv.system = evalStringNoCtx(state, a);
|
||||
|
||||
drv.attrs = attrs;
|
||||
|
||||
|
||||
//printMsg(lvlError, format("TEST '%1%'") % evalStringNoCtx(state, a) );
|
||||
|
||||
drv.attrPath = attrPath;
|
||||
|
||||
drvs.push_back(drv);
|
||||
@@ -142,7 +194,8 @@ bool getDerivation(EvalState & state, Expr e, DrvInfo & drv)
|
||||
Exprs doneExprs;
|
||||
DrvInfos drvs;
|
||||
getDerivation(state, e, "", drvs, doneExprs);
|
||||
if (drvs.size() != 1) return false;
|
||||
if (drvs.size() != 1)
|
||||
return false;
|
||||
drv = drvs.front();
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -20,6 +20,8 @@ struct DrvInfo
|
||||
private:
|
||||
string drvPath;
|
||||
string outPath;
|
||||
string stateIdentifier;
|
||||
string runtimeStateArgs;
|
||||
|
||||
public:
|
||||
string name;
|
||||
@@ -33,6 +35,8 @@ public:
|
||||
|
||||
string queryDrvPath(EvalState & state) const;
|
||||
string queryOutPath(EvalState & state) const;
|
||||
string queryStateIdentifier(EvalState & state) const;
|
||||
string queryRuntimeStateArgs(EvalState & state) const;
|
||||
MetaInfo queryMetaInfo(EvalState & state) const;
|
||||
string queryMetaInfo(EvalState & state, const string & name) const;
|
||||
|
||||
@@ -45,7 +49,17 @@ public:
|
||||
{
|
||||
outPath = s;
|
||||
}
|
||||
|
||||
|
||||
void setStateIdentifier(const string & s)
|
||||
{
|
||||
stateIdentifier = s;
|
||||
}
|
||||
|
||||
void setRuntimeStateArgs(const string & s)
|
||||
{
|
||||
runtimeStateArgs = s;
|
||||
}
|
||||
|
||||
void setMetaInfo(const MetaInfo & meta);
|
||||
};
|
||||
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
|
||||
|
||||
%x STRING
|
||||
%x IND_STRING
|
||||
|
||||
|
||||
%{
|
||||
@@ -122,6 +123,30 @@ inherit { return INHERIT; }
|
||||
<STRING>\" { BEGIN(INITIAL); return '"'; }
|
||||
<STRING>. return yytext[0]; /* just in case: shouldn't be reached */
|
||||
|
||||
\'\'(\ *\n)? { BEGIN(IND_STRING); return IND_STRING_OPEN; }
|
||||
<IND_STRING>([^\$\']|\$[^\{\']|\'[^\'\$])+ {
|
||||
yylval->t = makeIndStr(toATerm(yytext));
|
||||
return IND_STR;
|
||||
}
|
||||
<IND_STRING>\'\'\$ {
|
||||
yylval->t = makeIndStr(toATerm("$"));
|
||||
return IND_STR;
|
||||
}
|
||||
<IND_STRING>\'\'\' {
|
||||
yylval->t = makeIndStr(toATerm("''"));
|
||||
return IND_STR;
|
||||
}
|
||||
<IND_STRING>\'\'\\. {
|
||||
yylval->t = unescapeStr(yytext + 2);
|
||||
return IND_STR;
|
||||
}
|
||||
<IND_STRING>\$\{ { BEGIN(INITIAL); return DOLLAR_CURLY; }
|
||||
<IND_STRING>\'\' { BEGIN(INITIAL); return IND_STRING_CLOSE; }
|
||||
<IND_STRING>\' {
|
||||
yylval->t = makeIndStr(toATerm("'"));
|
||||
return IND_STR;
|
||||
}
|
||||
<IND_STRING>. return yytext[0]; /* just in case: shouldn't be reached */
|
||||
|
||||
{PATH} { yylval->t = toATerm(yytext); return PATH; /* !!! alloc */ }
|
||||
{URI} { yylval->t = toATerm(yytext); return URI; /* !!! alloc */ }
|
||||
@@ -148,4 +173,10 @@ void backToString(yyscan_t scanner)
|
||||
BEGIN(STRING);
|
||||
}
|
||||
|
||||
void backToIndString(yyscan_t scanner)
|
||||
{
|
||||
struct yyguts_t * yyg = (struct yyguts_t *) scanner;
|
||||
BEGIN(IND_STRING);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -46,6 +46,9 @@ Int | int | Expr |
|
||||
Str | string ATermList | Expr |
|
||||
Str | string | Expr | ObsoleteStr
|
||||
|
||||
# Internal to the parser, doesn't occur in ASTs.
|
||||
IndStr | string | Expr |
|
||||
|
||||
# A path is a reference to a file system object that is to be copied
|
||||
# to the Nix store when used as a derivation attribute. When it is
|
||||
# concatenated to a string (i.e., `str + path'), it is also copied and
|
||||
@@ -73,8 +76,6 @@ Inherit | Expr ATermList Pos | ATerm |
|
||||
|
||||
Scope | | Expr |
|
||||
|
||||
Cell | int Expr | Expr |
|
||||
|
||||
Formal | string ValidValues DefaultValue | ATerm |
|
||||
|
||||
ValidValues | ATermList | ValidValues |
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
#include "derivations.hh"
|
||||
#include "util.hh"
|
||||
#include "aterm.hh"
|
||||
#include "eval.hh" // !!! urgh
|
||||
|
||||
#include "nixexpr-ast.hh"
|
||||
#include "nixexpr-ast.cc"
|
||||
@@ -109,16 +108,7 @@ Expr makeAttrs(const ATermMap & attrs)
|
||||
}
|
||||
|
||||
|
||||
extern unsigned int substs;
|
||||
extern unsigned int substsCached;
|
||||
extern bool closedTerms;
|
||||
extern bool substCache;
|
||||
|
||||
|
||||
static Expr substitute(ATermMap & done, const Substitution & subs, Expr e);
|
||||
|
||||
|
||||
static Expr substitute2(ATermMap & done, const Substitution & subs, Expr e)
|
||||
Expr substitute(const Substitution & subs, Expr e)
|
||||
{
|
||||
checkInterrupt();
|
||||
|
||||
@@ -126,20 +116,19 @@ static Expr substitute2(ATermMap & done, const Substitution & subs, Expr e)
|
||||
|
||||
ATerm name, pos, e2;
|
||||
|
||||
substs++;
|
||||
|
||||
/* As an optimisation, don't substitute in subterms known to be
|
||||
closed. */
|
||||
if (closedTerms && matchClosed(e, e2)) return e;
|
||||
if (matchClosed(e, e2)) return e;
|
||||
|
||||
if (matchVar(e, name)) {
|
||||
Expr sub = subs.lookup(name);
|
||||
if (sub == makeRemoved()) sub = 0;
|
||||
Expr wrapped;
|
||||
/* Add a "closed" wrapper around terms that aren't already
|
||||
closed. The check is necessary to prevent repeated
|
||||
wrapping, e.g., closed(closed(closed(...))), which kills
|
||||
caching. */
|
||||
return sub ? ((!closedTerms || matchClosed(sub, wrapped)) ? sub : makeClosed(sub)) : e;
|
||||
return sub ? (matchClosed(sub, wrapped) ? sub : makeClosed(sub)) : e;
|
||||
}
|
||||
|
||||
/* In case of a function, filter out all variables bound by this
|
||||
@@ -151,30 +140,18 @@ static Expr substitute2(ATermMap & done, const Substitution & subs, Expr e)
|
||||
for (ATermIterator i(formals); i; ++i) {
|
||||
ATerm d1, d2;
|
||||
if (!matchFormal(*i, name, d1, d2)) abort();
|
||||
if (subs.lookup(name))
|
||||
map.set(name, constRemoved);
|
||||
}
|
||||
if (map.size() == 0)
|
||||
return makeFunction(
|
||||
(ATermList) substitute(done, subs, (ATerm) formals),
|
||||
substitute(done, subs, body), pos);
|
||||
else {
|
||||
Substitution subs2(&subs, &map);
|
||||
ATermMap done2(128);
|
||||
return makeFunction(
|
||||
(ATermList) substitute(done2, subs2, (ATerm) formals),
|
||||
substitute(done2, subs2, body), pos);
|
||||
map.set(name, makeRemoved());
|
||||
}
|
||||
Substitution subs2(&subs, &map);
|
||||
return makeFunction(
|
||||
(ATermList) substitute(subs2, (ATerm) formals),
|
||||
substitute(subs2, body), pos);
|
||||
}
|
||||
|
||||
if (matchFunction1(e, name, body, pos)) {
|
||||
if (subs.lookup(name)) {
|
||||
ATermMap map(1);
|
||||
map.set(name, constRemoved);
|
||||
ATermMap done2(128);
|
||||
return makeFunction1(name, substitute(done2, Substitution(&subs, &map), body), pos);
|
||||
} else
|
||||
return makeFunction1(name, substitute(done, subs, body), pos);
|
||||
ATermMap map(1);
|
||||
map.set(name, makeRemoved());
|
||||
return makeFunction1(name, substitute(Substitution(&subs, &map), body), pos);
|
||||
}
|
||||
|
||||
/* Idem for a mutually recursive attribute set. */
|
||||
@@ -182,21 +159,14 @@ static Expr substitute2(ATermMap & done, const Substitution & subs, Expr e)
|
||||
if (matchRec(e, rbnds, nrbnds)) {
|
||||
ATermMap map(ATgetLength(rbnds) + ATgetLength(nrbnds));
|
||||
for (ATermIterator i(rbnds); i; ++i)
|
||||
if (matchBind(*i, name, e2, pos) && subs.lookup(name))
|
||||
map.set(name, constRemoved);
|
||||
if (matchBind(*i, name, e2, pos)) map.set(name, makeRemoved());
|
||||
else abort(); /* can't happen */
|
||||
for (ATermIterator i(nrbnds); i; ++i)
|
||||
if (matchBind(*i, name, e2, pos) && subs.lookup(name))
|
||||
map.set(name, constRemoved);
|
||||
if (map.size() == 0)
|
||||
return makeRec(
|
||||
(ATermList) substitute(done, subs, (ATerm) rbnds),
|
||||
(ATermList) substitute(done, subs, (ATerm) nrbnds));
|
||||
else {
|
||||
ATermMap done2(128);
|
||||
return makeRec(
|
||||
(ATermList) substitute(done2, Substitution(&subs, &map), (ATerm) rbnds),
|
||||
(ATermList) substitute(done, subs, (ATerm) nrbnds));
|
||||
}
|
||||
if (matchBind(*i, name, e2, pos)) map.set(name, makeRemoved());
|
||||
else abort(); /* can't happen */
|
||||
return makeRec(
|
||||
(ATermList) substitute(Substitution(&subs, &map), (ATerm) rbnds),
|
||||
(ATermList) substitute(subs, (ATerm) nrbnds));
|
||||
}
|
||||
|
||||
if (ATgetType(e) == AT_APPL) {
|
||||
@@ -207,73 +177,7 @@ static Expr substitute2(ATermMap & done, const Substitution & subs, Expr e)
|
||||
|
||||
for (int i = 0; i < arity; ++i) {
|
||||
ATerm arg = ATgetArgument(e, i);
|
||||
args[i] = substitute(done, subs, arg);
|
||||
if (args[i] != arg) changed = true;
|
||||
}
|
||||
|
||||
return changed ? (ATerm) ATmakeApplArray(fun, args) : e;
|
||||
}
|
||||
|
||||
if (ATgetType(e) == AT_LIST) {
|
||||
unsigned int len = ATgetLength((ATermList) e);
|
||||
ATerm es[len];
|
||||
ATermIterator i((ATermList) e);
|
||||
bool changed = false;
|
||||
for (unsigned int j = 0; i; ++i, ++j) {
|
||||
es[j] = substitute(done, subs, *i);
|
||||
if (es[j] != *i) changed = true;
|
||||
}
|
||||
if (!changed) return e;
|
||||
ATermList out = ATempty;
|
||||
for (unsigned int j = len; j; --j)
|
||||
out = ATinsert(out, es[j - 1]);
|
||||
return (ATerm) out;
|
||||
}
|
||||
|
||||
return e;
|
||||
}
|
||||
|
||||
|
||||
static Expr substitute(ATermMap & done, const Substitution & subs, Expr e)
|
||||
{
|
||||
Expr res = done[e];
|
||||
if (substCache && res) {
|
||||
substsCached++;
|
||||
return res;
|
||||
}
|
||||
res = substitute2(done, subs, e);
|
||||
done.set(e, res);
|
||||
return res;
|
||||
}
|
||||
|
||||
|
||||
Expr substitute(const Substitution & subs, Expr e)
|
||||
{
|
||||
ATermMap done(256);
|
||||
return substitute(done, subs, e);
|
||||
}
|
||||
|
||||
|
||||
Expr allocCells(Expr e)
|
||||
{
|
||||
checkInterrupt();
|
||||
|
||||
ATerm e2;
|
||||
if (matchClosed(e, e2)) return e;
|
||||
|
||||
int i;
|
||||
if (matchCell(e, i, e2))
|
||||
return allocCell(allocCells(e2));
|
||||
|
||||
if (ATgetType(e) == AT_APPL) {
|
||||
AFun fun = ATgetAFun(e);
|
||||
int arity = ATgetArity(fun);
|
||||
ATerm args[arity];
|
||||
bool changed = false;
|
||||
|
||||
for (int i = 0; i < arity; ++i) {
|
||||
ATerm arg = ATgetArgument(e, i);
|
||||
args[i] = allocCells(arg);
|
||||
args[i] = substitute(subs, arg);
|
||||
if (args[i] != arg) changed = true;
|
||||
}
|
||||
|
||||
@@ -285,7 +189,7 @@ Expr allocCells(Expr e)
|
||||
ATerm es[len];
|
||||
ATermIterator i((ATermList) e);
|
||||
for (unsigned int j = 0; i; ++i, ++j)
|
||||
es[j] = allocCells(*i);
|
||||
es[j] = substitute(subs, *i);
|
||||
ATermList out = ATempty;
|
||||
for (unsigned int j = len; j; --j)
|
||||
out = ATinsert(out, es[j - 1]);
|
||||
@@ -311,7 +215,11 @@ static void checkVarDefs2(set<Expr> & done, const ATermMap & defs, Expr e)
|
||||
ATerm with, body;
|
||||
ATermList rbnds, nrbnds;
|
||||
|
||||
if (matchVar(e, name)) {
|
||||
/* Closed terms don't have free variables, so we don't have to
|
||||
check by definition. */
|
||||
if (matchClosed(e, value)) return;
|
||||
|
||||
else if (matchVar(e, name)) {
|
||||
if (!defs.get(name))
|
||||
throw EvalError(format("undefined variable `%1%'")
|
||||
% aterm2String(name));
|
||||
@@ -495,18 +403,5 @@ string showValue(Expr e)
|
||||
return "<unknown>";
|
||||
}
|
||||
|
||||
|
||||
static unsigned int cellCount = 0;
|
||||
|
||||
|
||||
Expr allocCell(Expr e)
|
||||
{
|
||||
if (cacheTerms != 2) return e;
|
||||
int i;
|
||||
Expr e2;
|
||||
if (matchCell(e, i, e2)) return e;
|
||||
return makeCell(cellCount++, e);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -34,9 +34,6 @@ typedef ATerm Pos;
|
||||
typedef vector<ATerm> ATermVector;
|
||||
|
||||
|
||||
extern Expr constRemoved;
|
||||
|
||||
|
||||
/* A substitution is a linked list of ATermMaps that map names to
|
||||
identifiers. We use a list of ATermMaps rather than a single to
|
||||
make it easy to grow or shrink a substitution when entering a
|
||||
@@ -56,8 +53,7 @@ struct Substitution
|
||||
{
|
||||
Expr x;
|
||||
for (const Substitution * s(this); s; s = s->prev)
|
||||
if ((x = s->map->get(name)))
|
||||
return x == constRemoved ? 0 : x;
|
||||
if ((x = s->map->get(name))) return x;
|
||||
return 0;
|
||||
}
|
||||
};
|
||||
@@ -120,11 +116,6 @@ string showType(Expr e);
|
||||
|
||||
string showValue(Expr e);
|
||||
|
||||
|
||||
Expr allocCell(Expr e); // make an updateable cell (for simulating conventional laziness)
|
||||
|
||||
Expr allocCells(Expr e); // re-allocate all cells in e
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -57,7 +57,7 @@ static Expr fixAttrs(int recursive, ATermList as)
|
||||
bool fromScope = matchScope(src);
|
||||
for (ATermIterator j(names); j; ++j) {
|
||||
Expr rhs = fromScope ? makeVar(*j) : makeSelect(src, *j);
|
||||
*is = ATinsert(*is, makeBind(*j, allocCell(rhs), pos));
|
||||
*is = ATinsert(*is, makeBind(*j, rhs, pos));
|
||||
}
|
||||
} else bs = ATinsert(bs, *i);
|
||||
}
|
||||
@@ -68,15 +68,104 @@ static Expr fixAttrs(int recursive, ATermList as)
|
||||
}
|
||||
|
||||
|
||||
static Expr stripIndentation(ATermList es)
|
||||
{
|
||||
if (es == ATempty) return makeStr("");
|
||||
|
||||
/* Figure out the minimum indentation. Note that by design
|
||||
whitespace-only final lines are not taken into account. (So
|
||||
the " " in "\n ''" is ignored, but the " " in "\n foo''" is.) */
|
||||
bool atStartOfLine = true; /* = seen only whitespace in the current line */
|
||||
unsigned int minIndent = 1000000;
|
||||
unsigned int curIndent = 0;
|
||||
ATerm e;
|
||||
for (ATermIterator i(es); i; ++i) {
|
||||
if (!matchIndStr(*i, e)) {
|
||||
/* Anti-quotations end the current start-of-line whitespace. */
|
||||
if (atStartOfLine) {
|
||||
atStartOfLine = false;
|
||||
if (curIndent < minIndent) minIndent = curIndent;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
string s = aterm2String(e);
|
||||
for (unsigned int j = 0; j < s.size(); ++j) {
|
||||
if (atStartOfLine) {
|
||||
if (s[j] == ' ')
|
||||
curIndent++;
|
||||
else if (s[j] == '\n') {
|
||||
/* Empty line, doesn't influence minimum
|
||||
indentation. */
|
||||
curIndent = 0;
|
||||
} else {
|
||||
atStartOfLine = false;
|
||||
if (curIndent < minIndent) minIndent = curIndent;
|
||||
}
|
||||
} else if (s[j] == '\n') {
|
||||
atStartOfLine = true;
|
||||
curIndent = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Strip spaces from each line. */
|
||||
ATermList es2 = ATempty;
|
||||
atStartOfLine = true;
|
||||
unsigned int curDropped = 0;
|
||||
unsigned int n = ATgetLength(es);
|
||||
for (ATermIterator i(es); i; ++i, --n) {
|
||||
if (!matchIndStr(*i, e)) {
|
||||
atStartOfLine = false;
|
||||
curDropped = 0;
|
||||
es2 = ATinsert(es2, *i);
|
||||
continue;
|
||||
}
|
||||
|
||||
string s = aterm2String(e);
|
||||
string s2;
|
||||
for (unsigned int j = 0; j < s.size(); ++j) {
|
||||
if (atStartOfLine) {
|
||||
if (s[j] == ' ') {
|
||||
if (curDropped++ >= minIndent)
|
||||
s2 += s[j];
|
||||
}
|
||||
else if (s[j] == '\n') {
|
||||
curDropped = 0;
|
||||
s2 += s[j];
|
||||
} else {
|
||||
atStartOfLine = false;
|
||||
curDropped = 0;
|
||||
s2 += s[j];
|
||||
}
|
||||
} else {
|
||||
s2 += s[j];
|
||||
if (s[j] == '\n') atStartOfLine = true;
|
||||
}
|
||||
}
|
||||
|
||||
/* Remove the last line if it is empty and consists only of
|
||||
spaces. */
|
||||
if (n == 1) {
|
||||
unsigned int p = s2.find_last_of('\n');
|
||||
if (p != string::npos && s2.find_first_not_of(' ', p + 1) == string::npos)
|
||||
s2 = string(s2, 0, p + 1);
|
||||
}
|
||||
|
||||
es2 = ATinsert(es2, makeStr(s2));
|
||||
}
|
||||
|
||||
return makeConcatStrings(ATreverse(es2));
|
||||
}
|
||||
|
||||
|
||||
void backToString(yyscan_t scanner);
|
||||
void backToIndString(yyscan_t scanner);
|
||||
|
||||
|
||||
extern bool posInfo;
|
||||
|
||||
static Pos makeCurPos(YYLTYPE * loc, ParseData * data)
|
||||
{
|
||||
return posInfo ? makePos(toATerm(data->path),
|
||||
loc->first_line, loc->first_column) : makeNoPos();
|
||||
return makePos(toATerm(data->path),
|
||||
loc->first_line, loc->first_column);
|
||||
}
|
||||
|
||||
#define CUR_POS makeCurPos(yylocp, data)
|
||||
@@ -123,10 +212,11 @@ static void freeAndUnprotect(void * p)
|
||||
|
||||
%type <t> start expr expr_function expr_if expr_op
|
||||
%type <t> expr_app expr_select expr_simple bind inheritsrc formal
|
||||
%type <ts> binds ids expr_list formals string_parts
|
||||
%token <t> ID INT STR PATH URI
|
||||
%type <ts> binds ids expr_list formals string_parts ind_string_parts
|
||||
%token <t> ID INT STR IND_STR PATH URI
|
||||
%token IF THEN ELSE ASSERT WITH LET IN REC INHERIT EQ NEQ AND OR IMPL
|
||||
%token DOLLAR_CURLY /* == ${ */
|
||||
%token IND_STRING_OPEN IND_STRING_CLOSE
|
||||
|
||||
%nonassoc IMPL
|
||||
%left OR
|
||||
@@ -201,6 +291,9 @@ expr_simple
|
||||
else if (ATgetNext($2) == ATempty) $$ = ATgetFirst($2);
|
||||
else $$ = makeConcatStrings(ATreverse($2));
|
||||
}
|
||||
| IND_STRING_OPEN ind_string_parts IND_STRING_CLOSE {
|
||||
$$ = stripIndentation(ATreverse($2));
|
||||
}
|
||||
| PATH { $$ = makePath(toATerm(absPath(aterm2String($1), data->basePath))); }
|
||||
| URI { $$ = makeStr($1, ATempty); }
|
||||
| '(' expr ')' { $$ = $2; }
|
||||
@@ -221,6 +314,12 @@ string_parts
|
||||
| { $$ = ATempty; }
|
||||
;
|
||||
|
||||
ind_string_parts
|
||||
: ind_string_parts IND_STR { $$ = ATinsert($1, $2); }
|
||||
| ind_string_parts DOLLAR_CURLY expr '}' { backToIndString(scanner); $$ = ATinsert($1, $3); }
|
||||
| { $$ = ATempty; }
|
||||
;
|
||||
|
||||
binds
|
||||
: binds bind { $$ = ATinsert($1, $2); }
|
||||
| { $$ = ATempty; }
|
||||
@@ -228,7 +327,7 @@ binds
|
||||
|
||||
bind
|
||||
: ID '=' expr ';'
|
||||
{ $$ = makeBind($1, allocCell($3), CUR_POS); }
|
||||
{ $$ = makeBind($1, $3, CUR_POS); }
|
||||
| INHERIT inheritsrc ids ';'
|
||||
{ $$ = makeInherit($2, $3, CUR_POS); }
|
||||
;
|
||||
@@ -385,13 +484,8 @@ Expr parseExprFromFile(EvalState & state, Path path)
|
||||
if (S_ISDIR(st.st_mode))
|
||||
path = canonPath(path + "/default.nix");
|
||||
|
||||
Expr cached = state.parsings.get(toATerm(path));
|
||||
if (cached) return cached;
|
||||
|
||||
/* Read and parse the input file. */
|
||||
cached = parse(state, readFile(path).c_str(), path, dirOf(path));
|
||||
state.parsings.set(toATerm(path), cached);
|
||||
return cached;
|
||||
return parse(state, readFile(path).c_str(), path, dirOf(path));
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -1,11 +1,14 @@
|
||||
#include "misc.hh"
|
||||
#include "eval.hh"
|
||||
#include "db.hh"
|
||||
#include "globals.hh"
|
||||
#include "store-api.hh"
|
||||
#include "util.hh"
|
||||
#include "archive.hh"
|
||||
#include "expr-to-xml.hh"
|
||||
#include "nixexpr-ast.hh"
|
||||
#include "local-store.hh"
|
||||
#include "parser.hh"
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
@@ -296,18 +299,19 @@ static Expr prim_getEnv(EvalState & state, const ATermVector & args)
|
||||
return makeStr(getEnv(name));
|
||||
}
|
||||
|
||||
/* for debugging purposes. print the first arg on stdout (perhaps stderr should be used?)
|
||||
* and return the second
|
||||
|
||||
/* Evaluate the first expression, and print its abstract syntax tree
|
||||
on standard error. Then return the second expression. Useful for
|
||||
debugging.
|
||||
*/
|
||||
static Expr prim_trace(EvalState & state, const ATermVector & args)
|
||||
{
|
||||
//string str = evalStringNoCtx(state, args[0]);
|
||||
|
||||
Expr a = evalExpr(state, args[0]);
|
||||
printf("traced value: %s\n", atPrint(a).c_str());
|
||||
Expr e = evalExpr(state, args[0]);
|
||||
printMsg(lvlError, format("trace: %1%") % e);
|
||||
return evalExpr(state, args[1]);
|
||||
}
|
||||
|
||||
|
||||
static Expr prim_relativise(EvalState & state, const ATermVector & args)
|
||||
{
|
||||
PathSet context; /* !!! what to do? */
|
||||
@@ -344,39 +348,67 @@ static Expr prim_relativise(EvalState & state, const ATermVector & args)
|
||||
(basically) its outputHash. */
|
||||
static Hash hashDerivationModulo(EvalState & state, Derivation drv)
|
||||
{
|
||||
/* Return a fixed hash for fixed-output derivations. */
|
||||
//printMsg(lvlError, format("DRV: %1% %2%") % drv.env.find("name")->second % (drv.outputs.size() == 1));
|
||||
|
||||
/* Return a fixed hash for fixed-output derivations.
|
||||
* E.g. derivations that have a hash
|
||||
*/
|
||||
if (drv.outputs.size() == 1) {
|
||||
DerivationOutputs::const_iterator i = drv.outputs.begin();
|
||||
|
||||
if (i->first == "out" &&
|
||||
i->second.hash != "")
|
||||
{
|
||||
//printMsg(lvlError, format("FIXED OUTPUT: %1% - %2% - %3%") % i->second.hashAlgo % i->second.hash % i->second.path);
|
||||
|
||||
return hashString(htSHA256, "fixed:out:"
|
||||
+ i->second.hashAlgo + ":"
|
||||
+ i->second.hash + ":"
|
||||
+ i->second.path);
|
||||
}
|
||||
}
|
||||
|
||||
/* If we have a state derivation, we clear state paramters because they (sometimes) can affect the outPath:
|
||||
* If this drv has runtime paramters: The state indentifier and statepath may change, but the componentPath (outPath) can stay the same
|
||||
* If this drv doesnt have runtime paramters: The state indentifier and statepath may change, but the componentPath changes since it is build with another identifier
|
||||
* In both cases: Other runtime state parameters like stateDirs, synchronisation and shareState never change the out or statepath so always need to be out of the hash
|
||||
*/
|
||||
if(isStateDrv(drv)){
|
||||
|
||||
if(drv.stateOutputs.size() != 1)
|
||||
throw EvalError(format("There are more then one stateOutputs in the derviation....."));
|
||||
|
||||
DerivationStateOutput drvso = drv.stateOutputs["state"];
|
||||
|
||||
if(drvso.runtimeStateArgs != ""){ //Has runtime parameters --> Clear all state parameters
|
||||
drv.stateOutputs.clear();
|
||||
drv.stateOutputDirs.clear();
|
||||
drv.env["statePath"] = "";
|
||||
}
|
||||
else{ //Has NO runtime parameters --> Clear state parameters selectively
|
||||
drvso.clearAllRuntimeParamters();
|
||||
drv.stateOutputDirs.clear();
|
||||
}
|
||||
}
|
||||
|
||||
/* For other derivations, replace the inputs paths with recursive
|
||||
calls to this function.*/
|
||||
DerivationInputs inputs2;
|
||||
for (DerivationInputs::iterator i = drv.inputDrvs.begin();
|
||||
i != drv.inputDrvs.end(); ++i)
|
||||
for (DerivationInputs::iterator i = drv.inputDrvs.begin(); i != drv.inputDrvs.end(); ++i)
|
||||
{
|
||||
Hash h = state.drvHashes[i->first];
|
||||
if (h.type == htUnknown) {
|
||||
Derivation drv2 = derivationFromPath(i->first);
|
||||
Derivation drv2 = derivationFromPathTxn(noTxn, i->first);
|
||||
h = hashDerivationModulo(state, drv2);
|
||||
state.drvHashes[i->first] = h;
|
||||
}
|
||||
inputs2[printHash(h)] = i->second;
|
||||
}
|
||||
drv.inputDrvs = inputs2;
|
||||
|
||||
return hashTerm(unparseDerivation(drv));
|
||||
//printMsg(lvlError, format("%1% with %2% --> %3%") % drv.env.find("name")->second % printHash(hashTerm(unparseDerivation(drv))) % unparseDerivation(drv));
|
||||
return hashTerm(unparseDerivation(drv));
|
||||
}
|
||||
|
||||
|
||||
/* Construct (as a unobservable side effect) a Nix derivation
|
||||
expression that performs the derivation described by the argument
|
||||
set. Returns the original set extended with the following
|
||||
@@ -415,6 +447,18 @@ static Expr prim_derivationStrict(EvalState & state, const ATermVector & args)
|
||||
string outputHashAlgo;
|
||||
bool outputHashRecursive = false;
|
||||
|
||||
//state vars
|
||||
bool enableState = false; //We dont do state by default, but if a user defines stateDirs for example, than this becomes true.
|
||||
bool disableState = false; //Becomes true if the user explicitly says: no state
|
||||
string shareType = "none";
|
||||
string syncState = "none";
|
||||
string stateIdentifier = "";
|
||||
bool createDirsBeforeInstall = false;
|
||||
string runtimeStateArgs = "";
|
||||
string sharedState = "";
|
||||
string externalState = "";
|
||||
vector<DerivationStateOutputDir> stateDirs;
|
||||
|
||||
for (ATermMap::const_iterator i = attrs.begin(); i != attrs.end(); ++i) {
|
||||
string key = aterm2String(i->key);
|
||||
ATerm value;
|
||||
@@ -440,7 +484,73 @@ static Expr prim_derivationStrict(EvalState & state, const ATermVector & args)
|
||||
drv.args.push_back(s);
|
||||
}
|
||||
}
|
||||
|
||||
//Add specific state variables
|
||||
else if(key == "stateDirs") {
|
||||
|
||||
enableState = true;
|
||||
value = evalExpr(state, value);
|
||||
ATermList list = evalList(state, value);
|
||||
|
||||
for (ATermIterator j(list); j; ++j){
|
||||
Expr v = evalExpr(state, *j);
|
||||
ATermMap stateDirattrs;
|
||||
queryAllAttrs(evalExpr(state, v), stateDirattrs, true);
|
||||
DerivationStateOutputDir dir = DerivationStateOutputDir();
|
||||
|
||||
for (ATermMap::const_iterator k = stateDirattrs.begin(); k != stateDirattrs.end(); ++k) {
|
||||
|
||||
string statekey = aterm2String(k->key);
|
||||
ATerm statevalue;
|
||||
Expr statepos;
|
||||
ATerm staterhs = k->value;
|
||||
if (!matchAttrRHS(staterhs, statevalue, statepos)) abort();
|
||||
startNest(nest, lvlVomit, format("processing statedir attribute `%1%'") % statekey);
|
||||
try {
|
||||
string s = trim(coerceToString(state, statevalue, context, true));
|
||||
if (statekey == "dir") {
|
||||
|
||||
//Add a / to the end if it's not there
|
||||
if(s[s.length() - 1] != '/')
|
||||
s = s + "/";
|
||||
|
||||
//Remove the / at the beginning if it's there
|
||||
if(s[0] == '/' && s.length() != 1)
|
||||
s = s.substr(1, s.length());
|
||||
|
||||
dir.path = s;
|
||||
}
|
||||
else if (statekey == "file") { dir.path = s; }
|
||||
else if (statekey == "type") { dir.type = s; }
|
||||
else if (statekey == "interval") {
|
||||
if(s == "")
|
||||
continue;
|
||||
int n;
|
||||
if (!string2Int(s, n)) throw Error("interval is not a number");
|
||||
if(n == 0) throw Error("Interval cannot be 0");
|
||||
dir.interval = s;
|
||||
}
|
||||
else throw EvalError(format("invalid sub-attirbute `%1%' for attribute dirs") % statekey);
|
||||
}
|
||||
catch (Error & e) {
|
||||
e.addPrefix(format("while evaluating the state derivation attribute `%1%' at %2%:\n") % key % showPos(statepos));
|
||||
e.addPrefix(format("while instantiating the derivation named `%1%' at %2%:\n") % drvName % showPos(posDrvName));
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
stateDirs.push_back(dir);
|
||||
}
|
||||
|
||||
}
|
||||
else if(key == "solidStateDependency"){ externalState = coerceToString(state, value, context, true); }
|
||||
else if(key == "shareType") { shareType = coerceToString(state, value, context, true); }
|
||||
else if(key == "synchronization") { syncState = coerceToString(state, value, context, true); }
|
||||
else if(key == "disableState") { disableState = evalBool(state, value); }
|
||||
else if(key == "identifier"){ stateIdentifier = coerceToString(state, value, context, true); }
|
||||
else if(key == "createDirsBeforeInstall"){ createDirsBeforeInstall = evalBool(state, value); }
|
||||
else if(key == "runtimeStateArgs"){ runtimeStateArgs = coerceToString(state, value, context, true); }
|
||||
else if(key == "shareStateFrom"){ sharedState = coerceToString(state, value, context, true); }
|
||||
|
||||
/* All other attributes are passed to the builder through
|
||||
the environment. */
|
||||
else {
|
||||
@@ -465,7 +575,6 @@ static Expr prim_derivationStrict(EvalState & state, const ATermVector & args)
|
||||
% drvName % showPos(posDrvName));
|
||||
throw;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/* Everything in the context of the strings in the derivation
|
||||
@@ -520,24 +629,55 @@ static Expr prim_derivationStrict(EvalState & state, const ATermVector & args)
|
||||
have an empty value. This ensures that changes in the set of
|
||||
output names do get reflected in the hash. */
|
||||
drv.env["out"] = "";
|
||||
drv.outputs["out"] =
|
||||
DerivationOutput("", outputHashAlgo, outputHash);
|
||||
drv.outputs["out"] = DerivationOutput("", outputHashAlgo, outputHash);
|
||||
|
||||
/* If there are no runtime paratermers, then we need to take the the stateIdentifier into account for the hash calcaulation below: hashDerivationModulo(...)
|
||||
* We also add enableState to make it parse the drv to a state-drv
|
||||
* We also add runtimeStateArgs for the hash calc in hashDerivationModulo(...) to check if its needs to take the stateIdentiefier into account in the hash
|
||||
*/
|
||||
|
||||
if(enableState && !disableState){
|
||||
if(runtimeStateArgs == ""){
|
||||
string enableStateS = bool2string("true");
|
||||
drv.stateOutputs["state"] = DerivationStateOutput("", "", "", "", stateIdentifier, enableStateS, "", "", "", runtimeStateArgs, queryCurrentUsername(), "", "", false);
|
||||
}
|
||||
}
|
||||
|
||||
/* Use the masked derivation expression to compute the output
|
||||
path. */
|
||||
Path outPath = makeStorePath("output:out",
|
||||
hashDerivationModulo(state, drv), drvName);
|
||||
Hash componentHash = hashDerivationModulo(state, drv);
|
||||
Path outPath = makeStorePath("output:out", componentHash, drvName);
|
||||
|
||||
/* Construct the final derivation store expression. */
|
||||
drv.env["out"] = outPath;
|
||||
drv.outputs["out"] =
|
||||
DerivationOutput(outPath, outputHashAlgo, outputHash);
|
||||
drv.outputs["out"] = DerivationOutput(outPath, outputHashAlgo, outputHash);
|
||||
|
||||
//printMsg(lvlError, format("DerivationOutput %1% %2% %3%") % outPath % outputHashAlgo % outputHash);
|
||||
//only add state when we have to to keep compitibilty with the 'old' format.
|
||||
//We add state when it's enbaled by the keywords, and not excplicitly disabled by the user
|
||||
Path stateOutPath;
|
||||
if(enableState && !disableState){
|
||||
/* Add the state path based on the outPath
|
||||
*
|
||||
* NOTE: we do not include the username into the hash calculation of the statepath yet, multiple different users can use the same dervation
|
||||
* but need different state paths. Thats why we keep a 'dummy' value e.g. global hash for everyone, and later at build time recalculate the real state path
|
||||
*/
|
||||
stateOutPath = makeStatePath(printHash(componentHash), drvName, stateIdentifier, queryCurrentUsername()); //State path
|
||||
drv.env["statePath"] = stateOutPath;
|
||||
|
||||
string enableStateS = bool2string("true");
|
||||
string createDirsBeforeInstallS = bool2string(createDirsBeforeInstall);
|
||||
drv.stateOutputs["state"] = DerivationStateOutput(stateOutPath, printHash(componentHash), outputHashAlgo, outputHash, stateIdentifier, enableStateS,
|
||||
shareType, syncState, createDirsBeforeInstallS, runtimeStateArgs, queryCurrentUsername(), sharedState, externalState);
|
||||
|
||||
for(vector<DerivationStateOutputDir>::iterator i = stateDirs.begin(); i != stateDirs.end(); ++i)
|
||||
drv.stateOutputDirs[(*i).path] = *(i);
|
||||
}
|
||||
|
||||
/* Write the resulting term into the Nix store directory. */
|
||||
Path drvPath = writeDerivation(drv, drvName);
|
||||
|
||||
printMsg(lvlChatty, format("instantiated `%1%' -> `%2%'")
|
||||
% drvName % drvPath);
|
||||
printMsg(lvlChatty, format("instantiated `%1%' -> `%2%'") % drvName % drvPath);
|
||||
|
||||
/* Optimisation, but required in read-only mode! because in that
|
||||
case we don't actually write store expressions, so we can't
|
||||
@@ -546,10 +686,17 @@ static Expr prim_derivationStrict(EvalState & state, const ATermVector & args)
|
||||
|
||||
/* !!! assumes a single output */
|
||||
ATermMap outAttrs(2);
|
||||
outAttrs.set(toATerm("outPath"),
|
||||
makeAttrRHS(makeStr(outPath, singleton<PathSet>(drvPath)), makeNoPos()));
|
||||
outAttrs.set(toATerm("drvPath"),
|
||||
makeAttrRHS(makeStr(drvPath, singleton<PathSet>(drvPath)), makeNoPos()));
|
||||
outAttrs.set(toATerm("outPath"), makeAttrRHS(makeStr(outPath, singleton<PathSet>(drvPath)), makeNoPos()));
|
||||
outAttrs.set(toATerm("drvPath"), makeAttrRHS(makeStr(drvPath, singleton<PathSet>(drvPath)), makeNoPos()));
|
||||
|
||||
/* TODO !!!!!!!!!!!!!!!!!!!! Recheck this !!!!!!!!!!!!!!!!!!!!
|
||||
* We now always set the statePath since someone might 'convert' and old non-state expression into a state expression like this:
|
||||
* let oldDrv = import ../../applications/networking/mailreaders/thunderbird-2.x;
|
||||
* newDrv = stdenv.mkDerivation( oldDrv { ... } // { name = "x-state-v"; stateDirs = [ ... ]; } );
|
||||
* in newDrv
|
||||
*/
|
||||
//if(enableState && !disableState)
|
||||
outAttrs.set(toATerm("statePath"), makeAttrRHS(makeStr(stateOutPath, singleton<PathSet>(drvPath)), makeNoPos()));
|
||||
|
||||
return makeAttrs(outAttrs);
|
||||
}
|
||||
@@ -564,12 +711,14 @@ static Expr prim_derivationLazy(EvalState & state, const ATermVector & args)
|
||||
attrs.set(toATerm("type"),
|
||||
makeAttrRHS(makeStr("derivation"), makeNoPos()));
|
||||
|
||||
Expr drvStrict = allocCell(makeCall(makeVar(toATerm("derivation!")), eAttrs));
|
||||
Expr drvStrict = makeCall(makeVar(toATerm("derivation!")), eAttrs);
|
||||
|
||||
attrs.set(toATerm("outPath"),
|
||||
makeAttrRHS(makeSelect(drvStrict, toATerm("outPath")), makeNoPos()));
|
||||
attrs.set(toATerm("drvPath"),
|
||||
makeAttrRHS(makeSelect(drvStrict, toATerm("drvPath")), makeNoPos()));
|
||||
attrs.set(toATerm("statePath"),
|
||||
makeAttrRHS(makeSelect(drvStrict, toATerm("statePath")), makeNoPos()));
|
||||
|
||||
return makeAttrs(attrs);
|
||||
}
|
||||
@@ -605,8 +754,8 @@ static Expr prim_baseNameOf(EvalState & state, const ATermVector & args)
|
||||
{
|
||||
PathSet context;
|
||||
return makeStr(baseNameOf(coerceToString(state, args[0], context)), context);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/* Return the directory of the given path, i.e., everything before the
|
||||
last slash. Return either a path or a string depending on the type
|
||||
@@ -621,6 +770,17 @@ static Expr prim_dirOf(EvalState & state, const ATermVector & args)
|
||||
}
|
||||
|
||||
|
||||
/* Return the contents of a file as a string. */
|
||||
static Expr prim_readFile(EvalState & state, const ATermVector & args)
|
||||
{
|
||||
PathSet context;
|
||||
Path path = coerceToPath(state, args[0], context);
|
||||
if (!context.empty())
|
||||
throw EvalError(format("string `%1%' cannot refer to other paths") % path);
|
||||
return makeStr(readFile(path));
|
||||
}
|
||||
|
||||
|
||||
/*************************************************************
|
||||
* Creating files
|
||||
*************************************************************/
|
||||
@@ -647,6 +807,7 @@ static Expr prim_toFile(EvalState & state, const ATermVector & args)
|
||||
string contents = evalString(state, args[1], context);
|
||||
|
||||
PathSet refs;
|
||||
PathSet stateRefs; //refs refers to the file references, there are no state references in this case.
|
||||
|
||||
for (PathSet::iterator i = context.begin(); i != context.end(); ++i) {
|
||||
if (isDerivation(*i))
|
||||
@@ -773,7 +934,7 @@ static Expr prim_listToAttrs(EvalState & state, const ATermVector & args)
|
||||
Expr e = evalExpr(state, makeSelect(evaledExpr, toATerm("name")));
|
||||
string attr = evalStringNoCtx(state,e);
|
||||
Expr r = makeSelect(evaledExpr, toATerm("value"));
|
||||
res.set(toATerm(attr), makeAttrRHS(allocCell(r), makeNoPos()));
|
||||
res.set(toATerm(attr), makeAttrRHS(r, makeNoPos()));
|
||||
}
|
||||
else
|
||||
throw TypeError(format("list element in `listToAttrs' is %s, expected a set { name = \"<name>\"; value = <value>; }")
|
||||
@@ -788,6 +949,7 @@ static Expr prim_listToAttrs(EvalState & state, const ATermVector & args)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static Expr prim_removeAttrs(EvalState & state, const ATermVector & args)
|
||||
{
|
||||
ATermMap attrs;
|
||||
@@ -802,6 +964,7 @@ static Expr prim_removeAttrs(EvalState & state, const ATermVector & args)
|
||||
return makeAttrs(attrs);
|
||||
}
|
||||
|
||||
|
||||
/* Determine whether the argument is a list. */
|
||||
static Expr prim_isAttrs(EvalState & state, const ATermVector & args)
|
||||
{
|
||||
@@ -809,6 +972,7 @@ static Expr prim_isAttrs(EvalState & state, const ATermVector & args)
|
||||
return makeBool(matchAttrs(evalExpr(state, args[0]), list));
|
||||
}
|
||||
|
||||
|
||||
/*************************************************************
|
||||
* Lists
|
||||
*************************************************************/
|
||||
@@ -902,8 +1066,8 @@ static Expr prim_toString(EvalState & state, const ATermVector & args)
|
||||
}
|
||||
|
||||
|
||||
/* `substr start len str' returns the substring of `str' starting at
|
||||
character position `min(start, stringLength str)' inclusive and
|
||||
/* `substring start len str' returns the substring of `str' starting
|
||||
at character position `min(start, stringLength str)' inclusive and
|
||||
ending at `min(start + len, stringLength str)'. `start' must be
|
||||
non-negative. */
|
||||
static Expr prim_substring(EvalState & state, const ATermVector & args)
|
||||
@@ -927,16 +1091,29 @@ static Expr prim_stringLength(EvalState & state, const ATermVector & args)
|
||||
}
|
||||
|
||||
|
||||
/*************************************************************
|
||||
* Strictness
|
||||
*************************************************************/
|
||||
|
||||
|
||||
static Expr prim_strict(EvalState & state, const ATermVector & args)
|
||||
static Expr prim_unsafeDiscardStringContext(EvalState & state, const ATermVector & args)
|
||||
{
|
||||
return evalExpr(state, makeCall(args[0], evalExpr(state, args[1])));
|
||||
PathSet context;
|
||||
string s = coerceToString(state, args[0], context);
|
||||
return makeStr(s, PathSet());
|
||||
}
|
||||
|
||||
/* Expression serialization/deserialization */
|
||||
|
||||
static Expr prim_ExprToString ( EvalState & state, const ATermVector & args)
|
||||
{
|
||||
return makeStr ( atPrint ( evalExpr ( state, args [ 0 ] ) ) );
|
||||
}
|
||||
|
||||
static Expr prim_StringToExpr ( EvalState & state, const ATermVector & args)
|
||||
{
|
||||
string s;
|
||||
PathSet l;
|
||||
if (! matchStr ( evalExpr ( state, args[0] ), s, l )) {
|
||||
throw EvalError("__stringToExpr needs string argument!");
|
||||
}
|
||||
return ATreadFromString(s.c_str());
|
||||
}
|
||||
|
||||
/*************************************************************
|
||||
* Primop registration
|
||||
@@ -963,6 +1140,10 @@ void EvalState::addPrimOps()
|
||||
addPrimOp("throw", 1, prim_throw);
|
||||
addPrimOp("__getEnv", 1, prim_getEnv);
|
||||
addPrimOp("__trace", 2, prim_trace);
|
||||
|
||||
// Expr <-> String
|
||||
addPrimOp("__exprToString", 1, prim_ExprToString);
|
||||
addPrimOp("__stringToExpr", 1, prim_StringToExpr);
|
||||
|
||||
addPrimOp("relativise", 2, prim_relativise);
|
||||
|
||||
@@ -975,6 +1156,7 @@ void EvalState::addPrimOps()
|
||||
addPrimOp("__pathExists", 1, prim_pathExists);
|
||||
addPrimOp("baseNameOf", 1, prim_baseNameOf);
|
||||
addPrimOp("dirOf", 1, prim_dirOf);
|
||||
addPrimOp("__readFile", 1, prim_readFile);
|
||||
|
||||
// Creating files
|
||||
addPrimOp("__toXML", 1, prim_toXML);
|
||||
@@ -1004,9 +1186,8 @@ void EvalState::addPrimOps()
|
||||
addPrimOp("toString", 1, prim_toString);
|
||||
addPrimOp("__substring", 3, prim_substring);
|
||||
addPrimOp("__stringLength", 1, prim_stringLength);
|
||||
|
||||
// Strictness
|
||||
addPrimOp("strict", 2, prim_strict);
|
||||
addPrimOp("__unsafeDiscardStringContext", 1, prim_unsafeDiscardStringContext);
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
17
src/libext3cow/Makefile.am
Normal file
17
src/libext3cow/Makefile.am
Normal file
@@ -0,0 +1,17 @@
|
||||
pkglib_LTLIBRARIES = libext3cow.la
|
||||
|
||||
libext3cow_la_SOURCES = epoch2date.c snapshot.cc tt.c ext3cow_tools.h ext3cow_fs.h
|
||||
|
||||
pkginclude_HEADERS = snapshot.hh
|
||||
|
||||
#TODO linux kernel header
|
||||
|
||||
libext3cow_la_LIBADD = ../libutil/libutil.la \
|
||||
../boost/format/libformat.la
|
||||
|
||||
AM_CXXFLAGS = -Wall \
|
||||
-I$(srcdir)/.. ${aterm_include} \
|
||||
-I$(srcdir)/../libutil
|
||||
|
||||
AM_CFLAGS = \
|
||||
${aterm_include}
|
||||
24
src/libext3cow/epoch2date.c
Normal file
24
src/libext3cow/epoch2date.c
Normal file
@@ -0,0 +1,24 @@
|
||||
|
||||
/* epoch2date.c - an ext3cow tool for turning epoch numbers into dates.
|
||||
* Copyright (C) 2003-2007 Zachary N. J. Peterson
|
||||
*/
|
||||
|
||||
|
||||
#include "ext3cow_tools.h"
|
||||
|
||||
int main(int argc, char** argv){
|
||||
|
||||
time_t time;
|
||||
|
||||
if(argc < 2){
|
||||
fprintf(stderr, "usage: %s seconds\n", argv[0]);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
time = atoi(argv[1]);
|
||||
|
||||
printf("%s", ctime(&time));
|
||||
|
||||
return 0;
|
||||
|
||||
}
|
||||
10
src/libext3cow/ext3cow_tools.h
Normal file
10
src/libext3cow/ext3cow_tools.h
Normal file
@@ -0,0 +1,10 @@
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <fcntl.h>
|
||||
#include <errno.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <time.h>
|
||||
#include <sys/time.h>
|
||||
#include "ext3cow_fs.h"
|
||||
97
src/libext3cow/snapshot.cc
Normal file
97
src/libext3cow/snapshot.cc
Normal file
@@ -0,0 +1,97 @@
|
||||
|
||||
/* snapshot.c - an ext3cow tool for taking file system snapshots
|
||||
* Copyright (C) 2003-2007 Zachary N. J. Peterson
|
||||
*/
|
||||
|
||||
|
||||
#include "ext3cow_tools.h"
|
||||
#include "snapshot.hh"
|
||||
#include "types.hh"
|
||||
|
||||
//using namespace nix;
|
||||
|
||||
namespace nix {
|
||||
|
||||
/////////////////////Original functions commented out:
|
||||
|
||||
/*
|
||||
void snapshot_usage(void){
|
||||
|
||||
fprintf(stderr, "usage: snapshot <mountpoint>\n");
|
||||
|
||||
}
|
||||
|
||||
int snapshot_main(int argc, char** argv){
|
||||
|
||||
int fd;
|
||||
int ret;
|
||||
unsigned int epoch = 0;
|
||||
char path[256] = ".\0";
|
||||
|
||||
if(argc > 1)
|
||||
strcpy(path, argv[1]);
|
||||
|
||||
printf("Snapshot on %s: ", path);
|
||||
|
||||
fd = open(path, O_RDONLY);
|
||||
|
||||
// test for ext3cow fs type
|
||||
|
||||
if(fd < 0){
|
||||
printf("failed.\n");
|
||||
perror(argv[0]);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
epoch = (int)ioctl(fd, EXT3COW_IOC_TAKESNAPSHOT, &epoch);
|
||||
if((int)epoch < 0){
|
||||
printf("failed.\n");
|
||||
perror(argv[0]);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
printf("%u\n", (unsigned int)epoch);
|
||||
|
||||
|
||||
return 0;
|
||||
|
||||
}
|
||||
*/
|
||||
|
||||
/////////////////////End original functions
|
||||
|
||||
|
||||
unsigned int take_snapshot(const string & dir2) //const string & file_or_dir)
|
||||
{
|
||||
const char* dir = dir2.c_str();
|
||||
|
||||
int fd;
|
||||
unsigned int epoch = 0;
|
||||
|
||||
//char path[256] = ".\0";
|
||||
//TODO 256 length check ???
|
||||
//strcpy(path, dir);
|
||||
|
||||
fd = open(dir, O_RDONLY);
|
||||
|
||||
/* test for ext3cow fs type */
|
||||
|
||||
if(fd < 0){
|
||||
printf("failed.\n");
|
||||
perror(dir);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
epoch = (int)ioctl(fd, EXT3COW_IOC_TAKESNAPSHOT, &epoch);
|
||||
if((int)epoch < 0){
|
||||
printf("failed.\n");
|
||||
perror(dir);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
//printf("%u\n", (unsigned int)epoch);
|
||||
|
||||
return epoch;
|
||||
}
|
||||
|
||||
}
|
||||
13
src/libext3cow/snapshot.hh
Normal file
13
src/libext3cow/snapshot.hh
Normal file
@@ -0,0 +1,13 @@
|
||||
#ifndef SNAPSHOT_H_
|
||||
#define SNAPSHOT_H_
|
||||
|
||||
#include "types.hh"
|
||||
|
||||
namespace nix {
|
||||
|
||||
//unsigned int take_snapshot(const string & file_or_dir);
|
||||
unsigned int take_snapshot(const string & dir);
|
||||
|
||||
}
|
||||
|
||||
#endif /*SNAPSHOT_H_*/
|
||||
46
src/libext3cow/tt.c
Normal file
46
src/libext3cow/tt.c
Normal file
@@ -0,0 +1,46 @@
|
||||
|
||||
/* tt.c - an ext3cow tool for retreiving the current file system epoch
|
||||
* Copyright (C) 2003-2007 Zachary N. J. Peterson
|
||||
*/
|
||||
|
||||
#include "ext3cow_tools.h"
|
||||
|
||||
void tt_usage(void){
|
||||
|
||||
fprintf(stderr, "usage: tt <mountpoint>\n");
|
||||
|
||||
}
|
||||
|
||||
int tt_main(int argc, char** argv){
|
||||
|
||||
int fd;
|
||||
int ret;
|
||||
unsigned int epoch = 0;
|
||||
char path[255] = ".\0";
|
||||
|
||||
if(argc > 1)
|
||||
strcpy(path, argv[1]);
|
||||
|
||||
|
||||
fd = open(path, O_RDONLY);
|
||||
|
||||
/* test for ext3cow fs type */
|
||||
|
||||
if(fd < 0){
|
||||
fprintf(stderr, "Couldn't open %s\n", path);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
|
||||
ret = ioctl(fd, EXT3COW_IOC_GETEPOCH, &epoch);
|
||||
if(ret < 0){
|
||||
printf("tt on %s failed.\n", path);
|
||||
perror(argv[0]);
|
||||
exit(1);
|
||||
}
|
||||
printf("Epoch: %d\n", ret);
|
||||
close(fd);
|
||||
|
||||
return 0;
|
||||
|
||||
}
|
||||
@@ -4,6 +4,9 @@ libmain_la_SOURCES = shared.cc shared.hh
|
||||
|
||||
AM_CXXFLAGS = \
|
||||
-DNIX_STORE_DIR=\"$(storedir)\" \
|
||||
-DNIX_STORE_STATE_DIR=\"$(storestatedir)\" \
|
||||
-DNIX_EXT3_COW_HEADER=\"$(ext3cowheader)\" \
|
||||
-DNIX_RSYNC=\"$(rsync)\" \
|
||||
-DNIX_DATA_DIR=\"$(datadir)\" \
|
||||
-DNIX_STATE_DIR=\"$(localstatedir)/nix\" \
|
||||
-DNIX_LOG_DIR=\"$(localstatedir)/log/nix\" \
|
||||
|
||||
@@ -105,10 +105,13 @@ static void initAndRun(int argc, char * * argv)
|
||||
{
|
||||
/* Setup Nix paths. */
|
||||
nixStore = canonPath(getEnv("NIX_STORE_DIR", getEnv("NIX_STORE", NIX_STORE_DIR)));
|
||||
nixStoreState = canonPath(getEnv("NIX_STORE_STATE_DIR", NIX_STORE_STATE_DIR)); //store state dir usually /nix/state
|
||||
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));
|
||||
nixStateDir = canonPath(getEnv("NIX_STATE_DIR", NIX_STATE_DIR)); //nix global state dir
|
||||
nixDBPath = getEnv("NIX_DB_DIR", nixStateDir + "/db");
|
||||
nixExt3CowHeader = getEnv("NIX_EXT3_COW_HEADER", NIX_EXT3_COW_HEADER);
|
||||
nixRsync = getEnv("NIX_RSYNC", NIX_RSYNC);
|
||||
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));
|
||||
@@ -160,23 +163,29 @@ static void initAndRun(int argc, char * * argv)
|
||||
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) {
|
||||
string arg = *i;
|
||||
if (string(arg, 0, 4) == "-at-") ;
|
||||
else if (arg.length() > 2 && arg[0] == '-' && arg[1] != '-') {
|
||||
for (unsigned int j = 1; j < arg.length(); j++)
|
||||
if (isalpha(arg[j]))
|
||||
remaining.push_back((string) "-" + arg[j]);
|
||||
else {
|
||||
remaining.push_back(string(arg, j));
|
||||
break;
|
||||
}
|
||||
} else remaining.push_back(arg);
|
||||
/* We dont expand for nix-state since we need to pass arguments to other
|
||||
* programs that can decide for themselves if they want expansion or not
|
||||
*/
|
||||
if(programId != "nix-state")
|
||||
{
|
||||
/* 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) {
|
||||
string arg = *i;
|
||||
if (string(arg, 0, 4) == "-at-") ;
|
||||
else if (arg.length() > 2 && arg[0] == '-' && arg[1] != '-') {
|
||||
for (unsigned int j = 1; j < arg.length(); j++)
|
||||
if (isalpha(arg[j]))
|
||||
remaining.push_back((string) "-" + arg[j]);
|
||||
else {
|
||||
remaining.push_back(string(arg, j));
|
||||
break;
|
||||
}
|
||||
} else remaining.push_back(arg);
|
||||
}
|
||||
args = remaining;
|
||||
remaining.clear();
|
||||
}
|
||||
args = remaining;
|
||||
remaining.clear();
|
||||
|
||||
/* Process default options. */
|
||||
for (Strings::iterator i = args.begin(); i != args.end(); ++i) {
|
||||
@@ -192,13 +201,15 @@ static void initAndRun(int argc, char * * argv)
|
||||
; /* !!! obsolete - remove eventually */
|
||||
else if (arg == "--no-build-output" || arg == "-Q")
|
||||
buildVerbosity = lvlVomit;
|
||||
else if (arg == "--help") {
|
||||
|
||||
//we need to push back since arguments need to be passed on in the state wrapper script
|
||||
else if (arg == "--help" && programId != "nix-state") {
|
||||
printHelp();
|
||||
return;
|
||||
remaining.push_back(arg);
|
||||
}
|
||||
else if (arg == "--version") {
|
||||
std::cout << format("%1% (Nix) %2%") % programId % NIX_VERSION << std::endl;
|
||||
return;
|
||||
remaining.push_back(arg);
|
||||
}
|
||||
else if (arg == "--keep-failed" || arg == "-K")
|
||||
keepFailed = true;
|
||||
@@ -212,6 +223,8 @@ static void initAndRun(int argc, char * * argv)
|
||||
readOnlyMode = true;
|
||||
else if (arg == "--max-silent-time")
|
||||
maxSilentTime = getIntArg(arg, i, args.end());
|
||||
else if (arg == "--no-build-hook")
|
||||
useBuildHook = false;
|
||||
else remaining.push_back(arg);
|
||||
}
|
||||
|
||||
|
||||
@@ -2,21 +2,23 @@ pkglib_LTLIBRARIES = libstore.la
|
||||
|
||||
libstore_la_SOURCES = \
|
||||
store-api.cc local-store.cc remote-store.cc derivations.cc build.cc misc.cc \
|
||||
globals.cc db.cc references.cc pathlocks.cc gc.cc
|
||||
globals.cc db.cc references.cc pathlocks.cc gc.cc store-state.cc
|
||||
|
||||
pkginclude_HEADERS = \
|
||||
store-api.hh local-store.hh remote-store.hh derivations.hh misc.hh \
|
||||
globals.hh db.hh references.hh pathlocks.hh \
|
||||
worker-protocol.hh
|
||||
worker-protocol.hh store-state.hh build.hh
|
||||
|
||||
libstore_la_LIBADD = ../libutil/libutil.la ../boost/format/libformat.la
|
||||
libstore_la_LIBADD = ../libutil/libutil.la \
|
||||
../libext3cow/libext3cow.la \
|
||||
../boost/format/libformat.la
|
||||
|
||||
BUILT_SOURCES = derivations-ast.cc derivations-ast.hh
|
||||
|
||||
EXTRA_DIST = derivations-ast.def derivations-ast.cc
|
||||
|
||||
AM_CXXFLAGS = -Wall \
|
||||
-I$(srcdir)/.. ${bdb_include} ${aterm_include} -I$(srcdir)/../libutil
|
||||
-I$(srcdir)/.. ${bdb_include} ${aterm_include} -I$(srcdir)/../libutil -I$(srcdir)/../nix-state -I$(srcdir)/../libext3cow
|
||||
|
||||
derivations-ast.cc derivations-ast.hh: ../aterm-helper.pl derivations-ast.def
|
||||
$(perl) $(srcdir)/../aterm-helper.pl derivations-ast.hh derivations-ast.cc < $(srcdir)/derivations-ast.def
|
||||
|
||||
@@ -5,6 +5,8 @@
|
||||
#include "local-store.hh"
|
||||
#include "db.hh"
|
||||
#include "util.hh"
|
||||
#include "store-state.hh"
|
||||
#include "build.hh"
|
||||
|
||||
#include <map>
|
||||
#include <iostream>
|
||||
@@ -25,6 +27,19 @@
|
||||
#include <grp.h>
|
||||
|
||||
|
||||
/* Includes required for chroot support. */
|
||||
#include "config.h"
|
||||
|
||||
#if HAVE_SYS_PARAM_H
|
||||
#include <sys/param.h>
|
||||
#endif
|
||||
#if HAVE_SYS_MOUNT_H
|
||||
#include <sys/mount.h>
|
||||
#endif
|
||||
|
||||
#define CHROOT_ENABLED HAVE_CHROOT && HAVE_SYS_MOUNT_H && defined(MS_BIND)
|
||||
|
||||
|
||||
namespace nix {
|
||||
|
||||
using std::map;
|
||||
@@ -87,6 +102,7 @@ protected:
|
||||
{
|
||||
nrFailed = 0;
|
||||
exitCode = ecBusy;
|
||||
forceInputs = false;
|
||||
}
|
||||
|
||||
virtual ~Goal()
|
||||
@@ -94,6 +110,8 @@ protected:
|
||||
trace("goal destroyed");
|
||||
}
|
||||
|
||||
bool forceInputs;
|
||||
|
||||
public:
|
||||
virtual void work() = 0;
|
||||
|
||||
@@ -128,6 +146,11 @@ public:
|
||||
(important!), etc. */
|
||||
virtual void cancel() = 0;
|
||||
|
||||
void setForceInputs(bool x)
|
||||
{
|
||||
forceInputs = x;
|
||||
}
|
||||
|
||||
protected:
|
||||
void amDone(ExitCode result);
|
||||
};
|
||||
@@ -583,6 +606,89 @@ void deletePathWrapped(const Path & path)
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
/* Helper RAII class for automatically unmounting bind-mounts in
|
||||
chroots. */
|
||||
struct BindMount
|
||||
{
|
||||
Path source, target;
|
||||
Paths created;
|
||||
|
||||
BindMount()
|
||||
{
|
||||
}
|
||||
|
||||
BindMount(const Path & source, const Path & target)
|
||||
{
|
||||
bind(source, target);
|
||||
}
|
||||
|
||||
~BindMount()
|
||||
{
|
||||
try {
|
||||
unbind();
|
||||
} catch (...) {
|
||||
ignoreException();
|
||||
}
|
||||
}
|
||||
|
||||
void bind(const Path & source, const Path & target)
|
||||
{
|
||||
#if CHROOT_ENABLED
|
||||
debug(format("bind mounting `%1%' to `%2%'") % source % target);
|
||||
|
||||
this->source = source;
|
||||
this->target = target;
|
||||
|
||||
created = createDirs(target);
|
||||
|
||||
if (mount(source.c_str(), target.c_str(), "", MS_BIND, 0) == -1)
|
||||
throw SysError(format("bind mount from `%1%' to `%2%' failed") % source % target);
|
||||
#endif
|
||||
}
|
||||
|
||||
void unbind()
|
||||
{
|
||||
#if CHROOT_ENABLED
|
||||
if (source == "") return;
|
||||
|
||||
debug(format("unmount bind-mount `%1%'") % target);
|
||||
|
||||
/* Urgh. Unmount sometimes doesn't succeed right away because
|
||||
the mount point is still busy. It shouldn't be, because
|
||||
we've killed all the build processes by now (at least when
|
||||
using a build user; see the check in killUser()). But
|
||||
maybe this is because those processes are still zombies and
|
||||
are keeping some kernel structures busy (open files,
|
||||
current directories, etc.). So retry a few times
|
||||
(actually, a 1 second sleep is almost certainly enough for
|
||||
the zombies to be cleaned up). */
|
||||
unsigned int tries = 0;
|
||||
while (umount(target.c_str()) == -1) {
|
||||
if (errno == EBUSY && ++tries < 10) {
|
||||
printMsg(lvlError, format("unmounting `%1%' failed, retrying after 1 second...") % target);
|
||||
sleep(1);
|
||||
}
|
||||
else
|
||||
throw SysError(format("unmounting bind-mount `%1%' failed") % target);
|
||||
}
|
||||
|
||||
/* Get rid of the directories for the mount point created in
|
||||
bind(). */
|
||||
for (Paths::reverse_iterator i = created.rbegin(); i != created.rend(); ++i) {
|
||||
debug(format("deleting `%1%'") % *i);
|
||||
if (remove(i->c_str()) == -1)
|
||||
throw SysError(format("cannot unlink `%1%'") % *i);
|
||||
}
|
||||
|
||||
source = "";
|
||||
#endif
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
class DerivationGoal : public Goal
|
||||
{
|
||||
private:
|
||||
@@ -600,10 +706,17 @@ private:
|
||||
/* All input paths (that is, the union of FS closures of the
|
||||
immediate input paths). */
|
||||
PathSet inputPaths;
|
||||
|
||||
/* All input-state-paths (that is, the union of FS closures of the
|
||||
immediate input paths). */
|
||||
PathSet inputStatePaths;
|
||||
|
||||
/* Referenceable paths (i.e., input and output paths). */
|
||||
PathSet allPaths;
|
||||
|
||||
/* Referenceable paths (i.e., input and output paths). */
|
||||
PathSet allStatePaths;
|
||||
|
||||
/* User selected for running the builder. */
|
||||
UserLock buildUser;
|
||||
|
||||
@@ -612,6 +725,9 @@ private:
|
||||
|
||||
/* The temporary directory. */
|
||||
Path tmpDir;
|
||||
|
||||
/* The state directory. */
|
||||
Path stateDir;
|
||||
|
||||
/* File descriptor for the log file. */
|
||||
AutoCloseFD fdLogFile;
|
||||
@@ -623,6 +739,17 @@ private:
|
||||
Pipe toHook;
|
||||
Pipe fromHook;
|
||||
|
||||
/* Whether we're currently doing a chroot build. */
|
||||
bool useChroot;
|
||||
|
||||
/* A RAII object to delete the chroot directory. */
|
||||
boost::shared_ptr<AutoDelete> autoDelChroot;
|
||||
|
||||
/* In chroot builds, the list of bind mounts currently active.
|
||||
The destructor of BindMount will cause the binds to be
|
||||
unmounted. */
|
||||
list<boost::shared_ptr<BindMount> > bindMounts;
|
||||
|
||||
typedef void (DerivationGoal::*GoalState)();
|
||||
GoalState state;
|
||||
|
||||
@@ -638,7 +765,7 @@ public:
|
||||
{
|
||||
return drvPath;
|
||||
}
|
||||
|
||||
|
||||
private:
|
||||
/* The states. */
|
||||
void init();
|
||||
@@ -678,7 +805,7 @@ private:
|
||||
void openLogFile();
|
||||
|
||||
/* Common initialisation to be performed in child processes (i.e.,
|
||||
both in builders and in build hooks. */
|
||||
both in builders and in build hooks). */
|
||||
void initChild();
|
||||
|
||||
/* Delete the temporary directory, if we have one. */
|
||||
@@ -705,7 +832,6 @@ DerivationGoal::DerivationGoal(const Path & drvPath, Worker & worker)
|
||||
trace("created");
|
||||
}
|
||||
|
||||
|
||||
DerivationGoal::~DerivationGoal()
|
||||
{
|
||||
/* Careful: we should never ever throw an exception from a
|
||||
@@ -718,7 +844,6 @@ DerivationGoal::~DerivationGoal()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void DerivationGoal::killChild()
|
||||
{
|
||||
if (pid != -1) {
|
||||
@@ -787,19 +912,23 @@ void DerivationGoal::haveDerivation()
|
||||
assert(store->isValidPath(drvPath));
|
||||
|
||||
/* Get the derivation. */
|
||||
drv = derivationFromPath(drvPath);
|
||||
drv = derivationFromPathTxn(noTxn, drvPath);
|
||||
|
||||
for (DerivationOutputs::iterator i = drv.outputs.begin();
|
||||
i != drv.outputs.end(); ++i)
|
||||
store->addTempRoot(i->second.path);
|
||||
|
||||
//TODO !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! Also add a temproot for state ????????????
|
||||
|
||||
/* Check what outputs paths are not already valid. */
|
||||
PathSet invalidOutputs = checkPathValidity(false);
|
||||
|
||||
/* If they are all valid, then we're done. */
|
||||
if (invalidOutputs.size() == 0) {
|
||||
amDone(ecSuccess);
|
||||
return;
|
||||
if(! forceInputs) {
|
||||
amDone(ecSuccess);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/* If this is a fixed-output derivation, it is possible that some
|
||||
@@ -843,8 +972,10 @@ void DerivationGoal::outputsSubstituted()
|
||||
nrFailed = 0;
|
||||
|
||||
if (checkPathValidity(false).size() == 0) {
|
||||
amDone(ecSuccess);
|
||||
return;
|
||||
if (! forceInputs){
|
||||
amDone(ecSuccess);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/* Otherwise, at least one of the output paths could not be
|
||||
@@ -853,13 +984,43 @@ void DerivationGoal::outputsSubstituted()
|
||||
/* The inputs must be built before we can build this goal. */
|
||||
/* !!! but if possible, only install the paths that we need */
|
||||
for (DerivationInputs::iterator i = drv.inputDrvs.begin();
|
||||
i != drv.inputDrvs.end(); ++i)
|
||||
addWaitee(worker.makeDerivationGoal(i->first));
|
||||
i != drv.inputDrvs.end(); ++i){
|
||||
GoalPtr newGoal = worker.makeDerivationGoal(i->first);
|
||||
newGoal->setForceInputs(forceInputs);
|
||||
addWaitee(newGoal);
|
||||
}
|
||||
|
||||
for (PathSet::iterator i = drv.inputSrcs.begin();
|
||||
i != drv.inputSrcs.end(); ++i)
|
||||
addWaitee(worker.makeSubstitutionGoal(*i));
|
||||
|
||||
/* Actually, I do some work twice just to be on the safe side */
|
||||
string s = drv.env["exportBuildReferencesGraph"];
|
||||
Strings ss = tokenizeString(s);
|
||||
if (ss.size() % 2 !=0)
|
||||
throw BuildError(format("odd number of tokens in `exportBuildReferencesGraph': `%1%'") % s);
|
||||
for (Strings::iterator i = ss.begin(); i != ss.end(); ) {
|
||||
string fileName = *i++;
|
||||
Path storePath=*i++;
|
||||
|
||||
if (!isInStore(storePath))
|
||||
throw BuildError(format("`exportBuildReferencesGraph' contains a non-store path `%1%'")
|
||||
% storePath);
|
||||
storePath = toStorePath(storePath);
|
||||
if (!store->isValidPath(storePath))
|
||||
throw BuildError(format("`exportBuildReferencesGraph' contains an invalid path `%1%'")
|
||||
% storePath);
|
||||
|
||||
/* Build-time closure should be in dependencies
|
||||
* We really want just derivation, its closure
|
||||
* and outputs. Looks like we should build it.
|
||||
* */
|
||||
|
||||
GoalPtr newGoal = worker.makeDerivationGoal(storePath);
|
||||
newGoal->setForceInputs(true);
|
||||
addWaitee(newGoal);
|
||||
}
|
||||
|
||||
state = &DerivationGoal::inputsRealised;
|
||||
}
|
||||
|
||||
@@ -877,6 +1038,12 @@ void DerivationGoal::inputsRealised()
|
||||
return;
|
||||
}
|
||||
|
||||
/* Maybe we just wanted to force build of inputs */
|
||||
if (checkPathValidity(false).size() == 0) {
|
||||
amDone(ecSuccess);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Okay, try to build. Note that here we don't wait for a build
|
||||
slot to become available, since we don't need one if there is a
|
||||
build hook. */
|
||||
@@ -888,7 +1055,7 @@ void DerivationGoal::inputsRealised()
|
||||
void DerivationGoal::tryToBuild()
|
||||
{
|
||||
trace("trying to build");
|
||||
|
||||
|
||||
try {
|
||||
|
||||
/* Is the build hook willing to accept this job? */
|
||||
@@ -1024,6 +1191,9 @@ void DerivationGoal::buildDone()
|
||||
|
||||
deleteTmpDir(true);
|
||||
|
||||
/* In chroot builds, unmount the bind mounts ASAP. */
|
||||
bindMounts.clear(); /* the destructors will do the rest */
|
||||
|
||||
/* Compute the FS closure of the outputs and register them as
|
||||
being valid. */
|
||||
computeClosure();
|
||||
@@ -1118,25 +1288,46 @@ static string makeValidityRegistration(const PathSet & paths,
|
||||
for (PathSet::iterator i = paths.begin(); i != paths.end(); ++i) {
|
||||
s += *i + "\n";
|
||||
|
||||
Path deriver = showDerivers ? store->queryDeriver(*i) : "";
|
||||
Path deriver = showDerivers ? store->queryDeriver(*i) : ""; //TODO HOW ABOUT MULTIPLE STATE-STORE DERIVERS? take last one ??!!
|
||||
s += deriver + "\n";
|
||||
|
||||
//store references
|
||||
|
||||
PathSet references;
|
||||
store->queryReferences(*i, references);
|
||||
store->queryStoreReferences(*i, references, 0);
|
||||
|
||||
s += (format("%1%\n") % references.size()).str();
|
||||
|
||||
for (PathSet::iterator j = references.begin();
|
||||
j != references.end(); ++j)
|
||||
s += *j + "\n";
|
||||
|
||||
if(store->isStateComponent(*i)){
|
||||
|
||||
//state references
|
||||
|
||||
PathSet stateReferences;
|
||||
store->queryStateReferences(*i, stateReferences, 0);
|
||||
|
||||
s += (format("%1%\n") % stateReferences.size()).str();
|
||||
|
||||
for (PathSet::iterator j = stateReferences.begin();
|
||||
j != stateReferences.end(); ++j)
|
||||
s += *j + "\n";
|
||||
|
||||
//revision
|
||||
|
||||
string revision = unsignedInt2String(0);
|
||||
s += revision + "\n";
|
||||
}
|
||||
}
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
|
||||
DerivationGoal::HookReply DerivationGoal::tryBuildHook()
|
||||
{
|
||||
if (!useBuildHook) return rpDecline;
|
||||
Path buildHook = getEnv("NIX_BUILD_HOOK");
|
||||
if (buildHook == "") return rpDecline;
|
||||
buildHook = absPath(buildHook);
|
||||
@@ -1173,7 +1364,7 @@ DerivationGoal::HookReply DerivationGoal::tryBuildHook()
|
||||
throw SysError(format("executing `%1%'") % buildHook);
|
||||
|
||||
} catch (std::exception & e) {
|
||||
std::cerr << format("build hook error: %1%\n") % e.what();
|
||||
std::cerr << format("build hook error: %1%") % e.what() << std::endl;
|
||||
}
|
||||
quickExit(1);
|
||||
}
|
||||
@@ -1240,7 +1431,7 @@ DerivationGoal::HookReply DerivationGoal::tryBuildHook()
|
||||
*probably* already has it.) */
|
||||
PathSet allInputs;
|
||||
allInputs.insert(inputPaths.begin(), inputPaths.end());
|
||||
computeFSClosure(drvPath, allInputs);
|
||||
computeFSClosure(drvPath, allInputs, true, false, 0); //TODO !!!!!!!!!!!!!!!!!!!!!!!!!!! WE (MAY) ALSO NEED TO COPY STATE
|
||||
|
||||
string s;
|
||||
for (PathSet::iterator i = allInputs.begin();
|
||||
@@ -1260,7 +1451,7 @@ DerivationGoal::HookReply DerivationGoal::tryBuildHook()
|
||||
/* The `references' file has exactly the format accepted by
|
||||
`nix-store --register-validity'. */
|
||||
writeStringToFile(referencesFN,
|
||||
makeValidityRegistration(allInputs, true));
|
||||
makeValidityRegistration(allInputs, true, false));
|
||||
|
||||
/* Tell the hook to proceed. */
|
||||
writeLine(toHook.writeSide, "okay");
|
||||
@@ -1352,47 +1543,53 @@ DerivationGoal::PrepareBuildReply DerivationGoal::prepareBuild()
|
||||
deletePathWrapped(path);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* Gather information necessary for computing the closure and/or
|
||||
running the build hook. */
|
||||
|
||||
/* The outputs are referenceable paths. */
|
||||
for (DerivationOutputs::iterator i = drv.outputs.begin();
|
||||
i != drv.outputs.end(); ++i)
|
||||
for (DerivationOutputs::iterator i = drv.outputs.begin(); i != drv.outputs.end(); ++i)
|
||||
{
|
||||
debug(format("building path `%1%'") % i->second.path);
|
||||
allPaths.insert(i->second.path);
|
||||
}
|
||||
|
||||
/* The state output is a referenceable path */
|
||||
if(isStateDrv(drv))
|
||||
allStatePaths.insert(drv.stateOutputs.find("state")->second.statepath);
|
||||
|
||||
/* Determine the full set of input paths. */
|
||||
|
||||
/* First, the input derivations. */
|
||||
for (DerivationInputs::iterator i = drv.inputDrvs.begin();
|
||||
i != drv.inputDrvs.end(); ++i)
|
||||
for (DerivationInputs::iterator i = drv.inputDrvs.begin(); i != drv.inputDrvs.end(); ++i)
|
||||
{
|
||||
/* Add the relevant output closures of the input derivation
|
||||
`*i' as input paths. Only add the closures of output paths
|
||||
that are specified as inputs. */
|
||||
assert(store->isValidPath(i->first));
|
||||
Derivation inDrv = derivationFromPath(i->first);
|
||||
for (StringSet::iterator j = i->second.begin();
|
||||
j != i->second.end(); ++j)
|
||||
if (inDrv.outputs.find(*j) != inDrv.outputs.end())
|
||||
computeFSClosure(inDrv.outputs[*j].path, inputPaths);
|
||||
Derivation inDrv = derivationFromPathTxn(noTxn, i->first);
|
||||
for (StringSet::iterator j = i->second.begin(); j != i->second.end(); ++j)
|
||||
if (inDrv.outputs.find(*j) != inDrv.outputs.end()){
|
||||
computeFSClosure(inDrv.outputs[*j].path, inputPaths, true, false, 0); //TODO !!!!!!!!!!!!!!!!!!!!!!!!!!! WE (MAY) ALSO NEED TO COPY STATE (done?)
|
||||
computeFSClosure(inDrv.outputs[*j].path, inputStatePaths, false, true, 0);
|
||||
}
|
||||
else
|
||||
throw BuildError(
|
||||
format("derivation `%1%' requires non-existent output `%2%' from input derivation `%3%'")
|
||||
% drvPath % *j % i->first);
|
||||
}
|
||||
|
||||
|
||||
/* Second, the input sources. */
|
||||
for (PathSet::iterator i = drv.inputSrcs.begin();
|
||||
i != drv.inputSrcs.end(); ++i)
|
||||
computeFSClosure(*i, inputPaths);
|
||||
|
||||
for (PathSet::iterator i = drv.inputSrcs.begin(); i != drv.inputSrcs.end(); ++i){
|
||||
computeFSClosure(*i, inputPaths, true, false, 0); //TODO !!!!!!!!!!!!!!!!!!!!!!!!!!! WE (MAY) ALSO NEED TO COPY STATE (done?)
|
||||
computeFSClosure(*i, inputStatePaths, false, true, 0);
|
||||
}
|
||||
|
||||
debug(format("added input paths %1%") % showPaths(inputPaths));
|
||||
debug(format("added input state paths %1%") % showPaths(inputStatePaths));
|
||||
|
||||
allPaths.insert(inputPaths.begin(), inputPaths.end());
|
||||
allStatePaths.insert(inputStatePaths.begin(), inputStatePaths.end());
|
||||
|
||||
return prProceed;
|
||||
}
|
||||
@@ -1433,13 +1630,24 @@ void DerivationGoal::startBuilder()
|
||||
env["NIX_STORE"] = nixStore;
|
||||
|
||||
/* Add all bindings specified in the derivation. */
|
||||
for (StringPairs::iterator i = drv.env.begin();
|
||||
i != drv.env.end(); ++i)
|
||||
env[i->first] = i->second;
|
||||
for (StringPairs::iterator i = drv.env.begin(); i != drv.env.end(); ++i){
|
||||
env[i->first] = i->second;
|
||||
}
|
||||
|
||||
/* Create a temporary directory where the build will take
|
||||
place. */
|
||||
tmpDir = createTempDir();
|
||||
|
||||
/* Create the state directory where the component can store it's state files place */
|
||||
//We only create state dirs when state is enabled and when the dirs need to be created before the installation
|
||||
if(drv.stateOutputs.size() != 0){
|
||||
|
||||
/* we check the recalculated state path at build time with the correct user for securiyt */
|
||||
checkStatePath(drv);
|
||||
|
||||
if(drv.stateOutputs.find("state")->second.getCreateDirsBeforeInstall())
|
||||
createSubStateDirsTxn(noTxn, drv.stateOutputDirs, drv.stateOutputs);
|
||||
}
|
||||
|
||||
/* For convenience, set an environment pointing to the top build
|
||||
directory. */
|
||||
@@ -1501,13 +1709,52 @@ void DerivationGoal::startBuilder()
|
||||
|
||||
/* Write closure info to `fileName'. */
|
||||
PathSet refs;
|
||||
computeFSClosure(storePath, refs);
|
||||
computeFSClosure(storePath, refs, true, false, 0); //TODO !!!!!!!!!!!!!!!!!!!!!!!!!!! WE (MAY) ALSO NEED TO COPY STATE
|
||||
/* !!! in secure Nix, the writing should be done on the
|
||||
build uid for security (maybe). */
|
||||
writeStringToFile(tmpDir + "/" + fileName,
|
||||
makeValidityRegistration(refs, false));
|
||||
makeValidityRegistration(refs, false, false));
|
||||
}
|
||||
|
||||
// The same for derivations
|
||||
s = drv.env["exportBuildReferencesGraph"];
|
||||
ss = tokenizeString(s);
|
||||
if (ss.size() % 2 != 0)
|
||||
throw BuildError(format("odd number of tokens in `exportBuildReferencesGraph': `%1%'") % s);
|
||||
for (Strings::iterator i = ss.begin(); i != ss.end(); ) {
|
||||
string fileName = *i++;
|
||||
checkStoreName(fileName); /* !!! abuse of this function */
|
||||
|
||||
/* Check that the store path is valid. */
|
||||
Path storePath = *i++;
|
||||
if (!isInStore(storePath))
|
||||
throw BuildError(format("`exportBuildReferencesGraph' contains a non-store path `%1%'")
|
||||
% storePath);
|
||||
storePath = toStorePath(storePath);
|
||||
if (!store->isValidPath(storePath))
|
||||
throw BuildError(format("`exportBuildReferencesGraph' contains an invalid path `%1%'")
|
||||
% storePath);
|
||||
|
||||
/* Write closure info to `fileName'. */
|
||||
PathSet refs1,refs;
|
||||
computeFSClosure(storePath, refs1, true, false, 0); //TODO !!!!!!!!!!!!!!!!!!!!!!!!!!! WE (MAY) ALSO NEED TO COPY STATE
|
||||
for (PathSet::iterator j = refs1.begin(); j != refs1.end() ; j++) {
|
||||
refs.insert (*j);
|
||||
if (isDerivation (*j)) {
|
||||
Derivation deriv = derivationFromPath (*j);
|
||||
for (DerivationOutputs::iterator k=deriv.outputs.begin();
|
||||
k != deriv.outputs.end(); k++) {
|
||||
refs.insert(k->second.path);
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
/* !!! in secure Nix, the writing should be done on the
|
||||
build uid for security (maybe). */
|
||||
writeStringToFile(tmpDir + "/" + fileName,
|
||||
makeValidityRegistration(refs, false, false));
|
||||
}
|
||||
|
||||
|
||||
/* If `build-users-group' is not empty, then we have to build as
|
||||
one of the members of that group. */
|
||||
@@ -1544,6 +1791,56 @@ void DerivationGoal::startBuilder()
|
||||
% buildUser.getGID() % nixStore);
|
||||
}
|
||||
|
||||
|
||||
/* Are we doing a chroot build? */
|
||||
useChroot = queryBoolSetting("build-use-chroot", false);
|
||||
Path tmpRootDir;
|
||||
|
||||
if (useChroot) {
|
||||
#if CHROOT_ENABLED
|
||||
/* Create a temporary directory in which we set up the chroot
|
||||
environment using bind-mounts.
|
||||
|
||||
!!! Big danger here: since we're doing this in /tmp, there
|
||||
is a risk that the admin does something like "rm -rf
|
||||
/tmp/chroot-nix-*" to clean up aborted builds, and if some
|
||||
of the bind-mounts are still active, then "rm -rf" will
|
||||
happily recurse into those mount points (thereby deleting,
|
||||
say, /nix/store). Ideally, tmpRootDir should be created in
|
||||
some special location (maybe in /nix/var/nix) where Nix
|
||||
takes care of unmounting / deleting old chroots
|
||||
automatically. */
|
||||
tmpRootDir = createTempDir("", "chroot-nix");
|
||||
|
||||
/* Clean up the chroot directory automatically, but don't
|
||||
recurse; that would be very very bad if the unmount of a
|
||||
bind-mount fails. Instead BindMount::unbind() unmounts and
|
||||
deletes exactly those directories that it created to
|
||||
produce the mount point, so that after all the BindMount
|
||||
destructors have run, tmpRootDir should be empty. */
|
||||
autoDelChroot = boost::shared_ptr<AutoDelete>(new AutoDelete(tmpRootDir, false));
|
||||
|
||||
printMsg(lvlChatty, format("setting up chroot environment in `%1%'") % tmpRootDir);
|
||||
|
||||
Paths defaultDirs;
|
||||
defaultDirs.push_back("/dev");
|
||||
defaultDirs.push_back("/proc");
|
||||
Paths dirsInChroot = querySetting("build-chroot-dirs", defaultDirs);
|
||||
|
||||
dirsInChroot.push_front(nixStore);
|
||||
dirsInChroot.push_front(tmpDir);
|
||||
|
||||
/* Push BindMounts at the front of the list so that they get
|
||||
unmounted in LIFO order. (!!! Does the C++ standard
|
||||
guarantee that list elements are destroyed in order?) */
|
||||
for (Paths::iterator i = dirsInChroot.begin(); i != dirsInChroot.end(); ++i)
|
||||
bindMounts.push_front(boost::shared_ptr<BindMount>(new BindMount(*i, tmpRootDir + *i)));
|
||||
|
||||
#else
|
||||
throw Error("chroot builds are not supported on this platform");
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
/* Run the builder. */
|
||||
printMsg(lvlChatty, format("executing builder `%1%'") %
|
||||
@@ -1569,6 +1866,17 @@ void DerivationGoal::startBuilder()
|
||||
|
||||
try { /* child */
|
||||
|
||||
#if CHROOT_ENABLED
|
||||
/* If building in a chroot, do the chroot right away.
|
||||
initChild() will do a chdir() to the temporary build
|
||||
directory to make sure the current directory is in the
|
||||
chroot. (Actually the order doesn't matter, since due
|
||||
to the bind mount tmpDir and tmpRootDit/tmpDir are the
|
||||
same directories.) */
|
||||
if (useChroot && chroot(tmpRootDir.c_str()) == -1)
|
||||
throw SysError(format("cannot change root directory to `%1%'") % tmpRootDir);
|
||||
#endif
|
||||
|
||||
initChild();
|
||||
|
||||
/* Fill in the environment. */
|
||||
@@ -1632,7 +1940,7 @@ void DerivationGoal::startBuilder()
|
||||
% drv.builder);
|
||||
|
||||
} catch (std::exception & e) {
|
||||
std::cerr << format("build error: %1%\n") % e.what();
|
||||
std::cerr << format("build error: %1%") % e.what() << std::endl;
|
||||
}
|
||||
quickExit(1);
|
||||
}
|
||||
@@ -1668,14 +1976,28 @@ PathSet parseReferenceSpecifiers(const Derivation & drv, string attr)
|
||||
|
||||
void DerivationGoal::computeClosure()
|
||||
{
|
||||
map<Path, PathSet> allReferences;
|
||||
map<Path, PathSet> allReferences; //all component references in componentpath (one drv can have multiple output paths)
|
||||
map<Path, PathSet> allStateReferences; //all state references in componentpath
|
||||
PathSet state_references; //all component references in statepath (one drv can have only one state path)
|
||||
PathSet state_stateReferences; //all state references in statepath
|
||||
map<Path, Hash> contentHashes;
|
||||
|
||||
//We create state dirs only when state is enabled and when the dirs need to be created after the installation
|
||||
if(drv.stateOutputs.size() != 0)
|
||||
if(!drv.stateOutputs.find("state")->second.getCreateDirsBeforeInstall())
|
||||
createSubStateDirsTxn(noTxn, drv.stateOutputDirs, drv.stateOutputs);
|
||||
|
||||
/*
|
||||
for (PathSet::iterator i = allPaths.begin(); i != allPaths.end(); ++i)
|
||||
printMsg(lvlError, format("scanning for allPaths: %1%") % *i);
|
||||
for (PathSet::iterator i = allStatePaths.begin(); i != allStatePaths.end(); ++i)
|
||||
printMsg(lvlError, format("scanning for allStatePaths: %1%") % *i);
|
||||
*/
|
||||
|
||||
/* Check whether the output paths were created, and grep each
|
||||
output path to determine what other paths it references. Also make all
|
||||
output paths read-only. */
|
||||
for (DerivationOutputs::iterator i = drv.outputs.begin();
|
||||
i != drv.outputs.end(); ++i)
|
||||
for (DerivationOutputs::iterator i = drv.outputs.begin(); i != drv.outputs.end(); ++i)
|
||||
{
|
||||
Path path = i->second.path;
|
||||
if (!pathExists(path)) {
|
||||
@@ -1688,8 +2010,7 @@ void DerivationGoal::computeClosure()
|
||||
if (lstat(path.c_str(), &st) == -1)
|
||||
throw SysError(format("getting attributes of path `%1%'") % path);
|
||||
|
||||
startNest(nest, lvlTalkative,
|
||||
format("scanning for references inside `%1%'") % path);
|
||||
startNest(nest, lvlTalkative, format("scanning for component and state references inside `%1%'") % path);
|
||||
|
||||
/* Check that fixed-output derivations produced the right
|
||||
outputs (i.e., the content hash should match the specified
|
||||
@@ -1724,14 +2045,30 @@ void DerivationGoal::computeClosure()
|
||||
format("output path `%1%' should have %2% hash `%3%', instead has `%4%'")
|
||||
% path % algo % printHash(h) % printHash(h2));
|
||||
}
|
||||
|
||||
/* Get rid of all weird permissions. */
|
||||
canonicalisePathMetaData(path);
|
||||
|
||||
/* In case we have an externalState:
|
||||
* Just before the very first scanForReferences, we insert the solid state references
|
||||
* in its table so its references will show up in the scan
|
||||
*/
|
||||
if(isStateDrv(drv)){
|
||||
if(drv.stateOutputs.find("state")->second.externalState != ""){
|
||||
PathSet singStatePath;
|
||||
singStatePath.insert(drv.stateOutputs.find("state")->second.statepath);
|
||||
setSolidStateReferencesTxn(noTxn, path, singStatePath);
|
||||
}
|
||||
}
|
||||
|
||||
/* Get rid of all weird permissions. */
|
||||
canonicalisePathMetaData(path);
|
||||
|
||||
/* For this output path, find the references to other paths contained
|
||||
in it. */
|
||||
/* For this output path, find the component references to other paths contained in it. */
|
||||
PathSet references = scanForReferences(path, allPaths);
|
||||
|
||||
allReferences[path] = references;
|
||||
|
||||
/* For this output path, find the state references to other paths contained in it. */
|
||||
PathSet output_state_references = scanForStateReferences(path, allStatePaths);
|
||||
allStateReferences[path] = output_state_references;
|
||||
|
||||
/* For debugging, print out the referenced and unreferenced
|
||||
paths. */
|
||||
for (PathSet::iterator i = inputPaths.begin();
|
||||
@@ -1743,9 +2080,8 @@ void DerivationGoal::computeClosure()
|
||||
else
|
||||
debug(format("referenced input: `%1%'") % *i);
|
||||
}
|
||||
|
||||
allReferences[path] = references;
|
||||
|
||||
|
||||
|
||||
/* If the derivation specifies an `allowedReferences'
|
||||
attribute (containing a list of paths that the output may
|
||||
refer to), check that all references are in that list. !!!
|
||||
@@ -1757,13 +2093,39 @@ void DerivationGoal::computeClosure()
|
||||
throw BuildError(format("output is not allowed to refer to path `%1%'") % *i);
|
||||
}
|
||||
|
||||
//TODO !!!!!!!!!!!!!!!!!!!!!!!! AllowedStateReferences??
|
||||
|
||||
/* Hash the contents of the path. The hash is stored in the
|
||||
database so that we can verify later on whether nobody has
|
||||
messed with the store. !!! inefficient: it would be nice
|
||||
if we could combine this with filterReferences(). */
|
||||
contentHashes[path] = hashPath(htSHA256, path);
|
||||
}
|
||||
|
||||
|
||||
/* We already scanned for [Component references in Component paths] //1
|
||||
* and [Component paths for state references] //2
|
||||
*
|
||||
* If state is enabled for the path we:
|
||||
* [scan for and state references and component references in the state path] //3,4
|
||||
*/
|
||||
if(isStateDrv(drv)){
|
||||
|
||||
Path sharedState = drv.stateOutputs.find("state")->second.sharedState;
|
||||
if(sharedState != ""){
|
||||
if (!store->isValidStatePath(sharedState))
|
||||
throw BuildError(format("sharedState path `%1%', is not a valid state path") % sharedState);
|
||||
|
||||
//We dont need to scan for state references since at the query to the state path we give the results of the linked-to path
|
||||
}
|
||||
else{
|
||||
Path statePath = drv.stateOutputs.find("state")->second.statepath;
|
||||
printMsg(lvlTalkative, format("scanning for component and state references inside `%1%'") % statePath);
|
||||
|
||||
state_references = scanForReferences(statePath, allPaths);
|
||||
state_stateReferences = scanForStateReferences(statePath, allStatePaths);
|
||||
}
|
||||
}
|
||||
|
||||
/* Register each output path as valid, and register the sets of
|
||||
paths referenced by each of them. This is wrapped in one
|
||||
database transaction to ensure that if we crash, either
|
||||
@@ -1775,18 +2137,93 @@ void DerivationGoal::computeClosure()
|
||||
The reason that we do the transaction here and not on the fly
|
||||
while we are scanning (above) is so that we don't hold database
|
||||
locks for too long. */
|
||||
|
||||
Transaction txn;
|
||||
createStoreTransaction(txn);
|
||||
for (DerivationOutputs::iterator i = drv.outputs.begin();
|
||||
i != drv.outputs.end(); ++i)
|
||||
{
|
||||
registerValidPath(txn, i->second.path,
|
||||
if(isStateDrv(drv)){ //TODO !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! CHECK IF THE BUILDER ALSO COMES HERE TWICE WITH A RUNTIME STATEPATH DRV
|
||||
|
||||
Path statePath = drv.stateOutputs.find("state")->second.statepath;
|
||||
string identifier = drv.stateOutputs.find("state")->second.stateIdentifier;
|
||||
string user = drv.stateOutputs.find("state")->second.username;
|
||||
|
||||
setStateComponentTxn(txn, i->second.path, statePath, identifier , user); //Register the path as a store-state path
|
||||
}
|
||||
|
||||
registerValidPath(txn,
|
||||
i->second.path, //component path
|
||||
contentHashes[i->second.path],
|
||||
allReferences[i->second.path],
|
||||
drvPath);
|
||||
allReferences[i->second.path], //set of component-references
|
||||
allStateReferences[i->second.path], //set of state-references
|
||||
drvPath, 0);
|
||||
}
|
||||
txn.commit();
|
||||
|
||||
//Register the state path valid
|
||||
if(isStateDrv(drv))
|
||||
{
|
||||
Path statePath = drv.stateOutputs.find("state")->second.statepath;
|
||||
|
||||
registerValidPath(txn,
|
||||
statePath,
|
||||
Hash(), //emtpy hash
|
||||
state_references,
|
||||
state_stateReferences,
|
||||
drvPath, 0);
|
||||
|
||||
//Convert stateInfo from drv to DB format
|
||||
//And set all interval-ed paths to zero to begin with
|
||||
DerivationStateOutputDirs stateOutputDirs = drv.stateOutputDirs;
|
||||
StateInfos infos;
|
||||
CommitIntervals intervals;
|
||||
for (DerivationStateOutputDirs::const_reverse_iterator j = stateOutputDirs.rbegin(); j != stateOutputDirs.rend(); ++j){
|
||||
DerivationStateOutputDir d = j->second;
|
||||
StateInfo si;
|
||||
si.path = d.path;
|
||||
si.type = d.type;
|
||||
|
||||
if(d.interval != ""){
|
||||
bool succeed = string2UnsignedInt(d.interval, si.interval);
|
||||
if(!succeed)
|
||||
throw Error(format("Malformed interval value '%1%' in drv '%2%'") % d.interval % drvPath);
|
||||
}
|
||||
else
|
||||
si.interval = 0;
|
||||
infos.push_back(si);
|
||||
|
||||
|
||||
if(d.type == "interval")
|
||||
intervals[d.path] = 0;
|
||||
}
|
||||
setStatePathsIntervalTxn(txn, statePath, intervals); //Set intervals to zero
|
||||
setVersionedStateEntriesTxn(txn, statePath, infos); //register subdirs/files and their types of versioning
|
||||
|
||||
//register state options that may change
|
||||
DerivationStateOutput drvso = drv.stateOutputs["state"];
|
||||
setStateOptionsTxn(txn, statePath, queryCallingUsername(), "nixbld", 700, drvso.runtimeStateArgs);
|
||||
|
||||
//Commit state (we only include our own state in the rivisionMapping (but other build component states might have been changed !!!! TODO)
|
||||
RevisionClosure rivisionMapping;
|
||||
rivisionMapping[statePath] = commitStatePathTxn(txn, statePath);
|
||||
|
||||
//Save the new revision
|
||||
setStateRevisionsTxn(txn, rivisionMapping, statePath, "Initial build revision.");
|
||||
|
||||
//Shared state
|
||||
Path sharedState = drv.stateOutputs.find("state")->second.sharedState;
|
||||
if(sharedState != ""){
|
||||
shareStateTxn(txn, sharedState, statePath, false);
|
||||
}
|
||||
|
||||
//If not shared: create the dir and set the rights
|
||||
else{
|
||||
ensureStateDir(statePath, queryCallingUsername(), "nixbld", "700");
|
||||
}
|
||||
}
|
||||
|
||||
txn.commit();
|
||||
|
||||
/* It is now safe to delete the lock files, since all future
|
||||
lockers will see that the output paths are valid; they will not
|
||||
create new lock files with the same names as the old (unlinked)
|
||||
@@ -1855,7 +2292,8 @@ void DerivationGoal::deleteTmpDir(bool force)
|
||||
getOwnership(tmpDir);
|
||||
}
|
||||
else
|
||||
deletePathWrapped(tmpDir);
|
||||
deletePathWrapped(tmpDir);
|
||||
|
||||
tmpDir = "";
|
||||
}
|
||||
}
|
||||
@@ -1903,7 +2341,7 @@ class SubstitutionGoal : public Goal
|
||||
|
||||
private:
|
||||
/* The store path that should be realised through a substitute. */
|
||||
Path storePath;
|
||||
Path storePath; //TODO !!!!!!!!!!!!!!!!!!!!! add statePath?
|
||||
|
||||
/* The remaining substituters. */
|
||||
Paths subs;
|
||||
@@ -1914,6 +2352,7 @@ private:
|
||||
/* Path info returned by the substituter's --query-info operation. */
|
||||
bool infoOkay;
|
||||
PathSet references;
|
||||
PathSet stateReferences; /* Outgoing state references for this path. */
|
||||
Path deriver;
|
||||
|
||||
/* Pipe for the substitute's standard output/error. */
|
||||
@@ -2115,6 +2554,12 @@ void SubstitutionGoal::tryToRun()
|
||||
if (pathExists(storePath))
|
||||
deletePathWrapped(storePath);
|
||||
|
||||
/* TODO Remove the (stale) state path if it exists. ????????????????? !!!!!!!!!!!!!!!!!!!! */
|
||||
//if(isStatePath .... ??)
|
||||
// if (pathExists(statePath))
|
||||
// deletePathWrapped(statePath);
|
||||
|
||||
|
||||
/* Fork the substitute program. */
|
||||
pid = fork();
|
||||
switch (pid) {
|
||||
@@ -2143,7 +2588,7 @@ void SubstitutionGoal::tryToRun()
|
||||
throw SysError(format("executing `%1%'") % sub);
|
||||
|
||||
} catch (std::exception & e) {
|
||||
std::cerr << format("substitute error: %1%\n") % e.what();
|
||||
std::cerr << format("substitute error: %1%") % e.what() << std::endl;
|
||||
}
|
||||
quickExit(1);
|
||||
}
|
||||
@@ -2207,8 +2652,8 @@ void SubstitutionGoal::finished()
|
||||
|
||||
Transaction txn;
|
||||
createStoreTransaction(txn);
|
||||
registerValidPath(txn, storePath, contentHash,
|
||||
references, deriver);
|
||||
registerValidPath(txn, storePath, contentHash, //TODO !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! how about substituing a state path ?????
|
||||
references, stateReferences, deriver, 0);
|
||||
txn.commit();
|
||||
|
||||
outputLock->setDeletion(true);
|
||||
@@ -2246,7 +2691,8 @@ static bool working = false;
|
||||
Worker::Worker()
|
||||
{
|
||||
/* Debugging: prevent recursive workers. */
|
||||
if (working) abort();
|
||||
if (working)
|
||||
abort();
|
||||
working = true;
|
||||
nrChildren = 0;
|
||||
}
|
||||
@@ -2443,10 +2889,11 @@ void Worker::getInfo()
|
||||
args.push_back("--query-info");
|
||||
args.insert(args.end(), paths2.begin(), paths2.end());
|
||||
string res = runProgram(sub, false, args);
|
||||
//printMsg(lvlError, format("run: '%1%' res: '%2%'") % sub % res);
|
||||
std::istringstream str(res);
|
||||
|
||||
while (true) {
|
||||
ValidPathInfo info = decodeValidPathInfo(str);
|
||||
ValidPathInfo info = decodeValidPathInfo(str); //TODO Test is this is ok with the state-brand extension to struct ValidPath
|
||||
if (info.path == "") break;
|
||||
|
||||
/* !!! inefficient */
|
||||
@@ -2457,8 +2904,10 @@ void Worker::getInfo()
|
||||
if (goal) {
|
||||
SubstitutionGoal * goal2 = dynamic_cast<SubstitutionGoal *>(goal.get());
|
||||
if (goal2->storePath == info.path) {
|
||||
goal2->references = info.references;
|
||||
goal2->references = info.references;
|
||||
goal2->deriver = info.deriver;
|
||||
//goal2->stateReferences = info.stateReferences; //TODO !!!!!!!!!!!!!!!!!!!!!!!!!! STATE REFERENCES FOR A SubstitutionGoal
|
||||
//goal2->revision = info.revision;
|
||||
goal2->infoOkay = true;
|
||||
wakeUp(goal);
|
||||
}
|
||||
@@ -2642,11 +3091,11 @@ void LocalStore::buildDerivations(const PathSet & drvPaths)
|
||||
format("building %1%") % showPaths(drvPaths));
|
||||
|
||||
Worker worker;
|
||||
|
||||
Goals goals;
|
||||
for (PathSet::const_iterator i = drvPaths.begin();
|
||||
i != drvPaths.end(); ++i)
|
||||
for (PathSet::const_iterator i = drvPaths.begin(); i != drvPaths.end(); ++i){
|
||||
printMsg(lvlError, format("BUILD: '%1%'") % *i);
|
||||
goals.insert(worker.makeDerivationGoal(*i));
|
||||
}
|
||||
|
||||
worker.run(goals);
|
||||
|
||||
@@ -2663,10 +3112,14 @@ void LocalStore::buildDerivations(const PathSet & drvPaths)
|
||||
}
|
||||
|
||||
|
||||
void LocalStore::ensurePath(const Path & path)
|
||||
void ensurePathTxn(const Transaction & txn, const Path & path)
|
||||
{
|
||||
/* If the path is already valid, we're done. */
|
||||
if (store->isValidPath(path)) return;
|
||||
|
||||
if (isValidPathTxn(txn, path))
|
||||
return;
|
||||
|
||||
printMsg(lvlError, format("ensurePathTxn(%1%)") % path);
|
||||
|
||||
Worker worker;
|
||||
GoalPtr goal = worker.makeSubstitutionGoal(path);
|
||||
@@ -2678,5 +3131,10 @@ void LocalStore::ensurePath(const Path & path)
|
||||
throw Error(format("path `%1%' does not exist and cannot be created") % path);
|
||||
}
|
||||
|
||||
void LocalStore::ensurePath(const Path & path)
|
||||
{
|
||||
ensurePathTxn(noTxn, path);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
12
src/libstore/build.hh
Normal file
12
src/libstore/build.hh
Normal file
@@ -0,0 +1,12 @@
|
||||
#ifndef BUILD_HH_
|
||||
#define BUILD_HH_
|
||||
|
||||
#include "db.hh"
|
||||
|
||||
namespace nix {
|
||||
|
||||
void ensurePathTxn(const Transaction & txn, const Path & path);
|
||||
|
||||
}
|
||||
|
||||
#endif /*BUILD_HH_*/
|
||||
@@ -1,6 +1,9 @@
|
||||
#include "db.hh"
|
||||
#include "util.hh"
|
||||
#include "pathlocks.hh"
|
||||
#include "derivations.hh"
|
||||
#include "local-store.hh"
|
||||
#include "misc.hh"
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
@@ -68,7 +71,7 @@ Transaction::~Transaction()
|
||||
void Transaction::begin(Database & db)
|
||||
{
|
||||
assert(txn == 0);
|
||||
db.requireEnv();
|
||||
db.requireEnv("begin transaction");
|
||||
try {
|
||||
db.env->txn_begin(0, &txn, 0);
|
||||
} catch (DbException e) { rethrow(e); }
|
||||
@@ -107,11 +110,11 @@ void Transaction::moveTo(Transaction & t)
|
||||
}
|
||||
|
||||
|
||||
void Database::requireEnv()
|
||||
void Database::requireEnv(string debug)
|
||||
{
|
||||
checkInterrupt();
|
||||
if (!env) throw Error("database environment is not open "
|
||||
"(maybe you don't have sufficient permission?)");
|
||||
if (!env) throw Error(format("database environment is not open while trying '%1%'"
|
||||
"(maybe you don't have sufficient permission?)") % debug);
|
||||
}
|
||||
|
||||
|
||||
@@ -213,9 +216,9 @@ void Database::open2(const string & path, bool removeOldEnv)
|
||||
run db_recover on the database to remove the existing DB
|
||||
environment (since changes only take effect on new
|
||||
environments). */
|
||||
env->set_lk_max_locks(10000);
|
||||
env->set_lk_max_lockers(10000);
|
||||
env->set_lk_max_objects(10000);
|
||||
env->set_lk_max_locks(100000);
|
||||
env->set_lk_max_lockers(100000);
|
||||
env->set_lk_max_objects(100000);
|
||||
env->set_lk_detect(DB_LOCK_DEFAULT);
|
||||
|
||||
/* Dangerous, probably, but from the docs it *seems* that BDB
|
||||
@@ -307,7 +310,7 @@ void Database::close()
|
||||
|
||||
TableId Database::openTable(const string & tableName, bool sorted)
|
||||
{
|
||||
requireEnv();
|
||||
requireEnv(tableName);
|
||||
TableId table = nextId++;
|
||||
|
||||
try {
|
||||
@@ -454,6 +457,7 @@ void Database::enumTable(const Transaction & txn, TableId table,
|
||||
} catch (DbException e) { rethrow(e); }
|
||||
}
|
||||
|
||||
|
||||
|
||||
void Database::clearTable(const Transaction & txn, TableId table)
|
||||
{
|
||||
|
||||
@@ -54,12 +54,14 @@ private:
|
||||
TableId nextId;
|
||||
std::map<TableId, Db *> tables;
|
||||
|
||||
void requireEnv();
|
||||
void requireEnv(string debug);
|
||||
|
||||
Db * getDb(TableId table);
|
||||
|
||||
void open2(const string & path, bool removeOldEnv);
|
||||
|
||||
|
||||
|
||||
public:
|
||||
Database();
|
||||
~Database();
|
||||
|
||||
@@ -1,10 +1,22 @@
|
||||
init initDerivationsHelpers
|
||||
|
||||
Derive | ATermList ATermList ATermList string string ATermList ATermList | ATerm |
|
||||
|
||||
Derive | ATermList ATermList ATermList ATermList ATermList string string ATermList ATermList | ATerm |
|
||||
| string string | ATerm | EnvBinding |
|
||||
| string ATermList | ATerm | DerivationInput |
|
||||
| string string string string | ATerm | DerivationOutput |
|
||||
| string string string string string string string string string string string string string string | ATerm | DerivationStateOutput |
|
||||
| string string string | ATerm | DerivationStateOutputDir |
|
||||
|
||||
#We use DeriveWithOutState to create derivations that dont use state, and thus dont have the stateDerivationStateOutput and DerivationStateOutputDir in their derivation
|
||||
#Ive put this in becuase its easy to stay backwards compatible, but maybe it should be removed somtime to prevent confusion & duplication
|
||||
#The function will be called matchDerivateWithOutState, but it will match the Derive term to remain backwards compatible
|
||||
|
||||
Derive | ATermList ATermList ATermList string string ATermList ATermList | ATerm | DeriveWithOutState
|
||||
| string string | ATerm | EnvBindingWithOutState |
|
||||
| string ATermList | ATerm | DerivationInputWithOutState |
|
||||
| string string string string | ATerm | DerivationOutputWithOutState |
|
||||
|
||||
#end drv without state
|
||||
|
||||
Closure | ATermList ATermList | ATerm | OldClosure |
|
||||
| string ATermList | ATerm | OldClosureElem |
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
#include "store-api.hh"
|
||||
#include "aterm.hh"
|
||||
#include "globals.hh"
|
||||
#include "util.hh"
|
||||
|
||||
#include "derivations-ast.hh"
|
||||
#include "derivations-ast.cc"
|
||||
@@ -20,12 +21,12 @@ Path writeDerivation(const Derivation & drv, const string & name)
|
||||
{
|
||||
PathSet references;
|
||||
references.insert(drv.inputSrcs.begin(), drv.inputSrcs.end());
|
||||
for (DerivationInputs::const_iterator i = drv.inputDrvs.begin();
|
||||
i != drv.inputDrvs.end(); ++i)
|
||||
for (DerivationInputs::const_iterator i = drv.inputDrvs.begin(); i != drv.inputDrvs.end(); ++i)
|
||||
references.insert(i->first);
|
||||
/* Note that the outputs of a derivation are *not* references
|
||||
(that can be missing (of course) and should not necessarily be
|
||||
held during a garbage collection). */
|
||||
|
||||
string suffix = name + drvExtension;
|
||||
string contents = atPrint(unparseDerivation(drv));
|
||||
return readOnlyMode
|
||||
@@ -66,9 +67,14 @@ Derivation parseDerivation(ATerm t)
|
||||
{
|
||||
Derivation drv;
|
||||
ATermList outs, inDrvs, inSrcs, args, bnds;
|
||||
ATermList stateOuts = ATempty, stateOutDirs = ATempty;
|
||||
|
||||
ATerm builder, platform;
|
||||
|
||||
if (!matchDerive(t, outs, inDrvs, inSrcs, platform, builder, args, bnds))
|
||||
bool withState;
|
||||
if (matchDerive(t, outs, stateOuts, stateOutDirs, inDrvs, inSrcs, platform, builder, args, bnds) ) { withState = true; }
|
||||
else if (matchDeriveWithOutState(t, outs, inDrvs, inSrcs, platform, builder, args, bnds) ) { withState = false; }
|
||||
else
|
||||
throwBadDrv(t);
|
||||
|
||||
for (ATermIterator i(outs); i; ++i) {
|
||||
@@ -82,6 +88,46 @@ Derivation parseDerivation(ATerm t)
|
||||
out.hash = aterm2String(hash);
|
||||
drv.outputs[aterm2String(id)] = out;
|
||||
}
|
||||
|
||||
if(withState)
|
||||
{
|
||||
//parse state part
|
||||
for (ATermIterator i(stateOuts); i; ++i) {
|
||||
ATerm id, statepath, componentHash, hashAlgo, hash, stateIdentifier, enabled, shareType, synchronization, createDirsBeforeInstall, runtimeStateArgs, username, sharedState, externalState; //TODO unitialized warning
|
||||
if (!matchDerivationStateOutput(*i, id, statepath, componentHash, hashAlgo, hash, stateIdentifier, enabled, shareType, synchronization, createDirsBeforeInstall, runtimeStateArgs, username, sharedState, externalState))
|
||||
throwBadDrv(t);
|
||||
DerivationStateOutput stateOut;
|
||||
stateOut.statepath = aterm2String(statepath);
|
||||
stateOut.componentHash = aterm2String(componentHash);
|
||||
//checkPath(stateOut.path); //should we check the statpath .... ???
|
||||
stateOut.hashAlgo = aterm2String(hashAlgo);
|
||||
stateOut.hash = aterm2String(hash);
|
||||
stateOut.stateIdentifier = aterm2String(stateIdentifier);
|
||||
stateOut.enabled = aterm2String(enabled);
|
||||
stateOut.shareType = aterm2String(shareType);
|
||||
stateOut.synchronization = aterm2String(synchronization);
|
||||
stateOut.createDirsBeforeInstall = aterm2String(createDirsBeforeInstall);
|
||||
stateOut.runtimeStateArgs = aterm2String(runtimeStateArgs);
|
||||
stateOut.username = aterm2String(username);
|
||||
stateOut.sharedState = aterm2String(sharedState);
|
||||
stateOut.externalState = aterm2String(externalState);
|
||||
drv.stateOutputs[aterm2String(id)] = stateOut;
|
||||
}
|
||||
|
||||
//parse state dirs part
|
||||
for (ATermIterator i(stateOutDirs); i; ++i) {
|
||||
ATerm id, path, type, interval;
|
||||
if (!matchDerivationStateOutputDir(*i, id, type, interval))
|
||||
throwBadDrv(t);
|
||||
path = id; //We prevent duplication since the key is also the path
|
||||
DerivationStateOutputDir stateOutDirs;
|
||||
stateOutDirs.path = aterm2String(path);
|
||||
stateOutDirs.type = aterm2String(type);
|
||||
stateOutDirs.interval = aterm2String(interval);
|
||||
drv.stateOutputDirs[aterm2String(id)] = stateOutDirs;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
for (ATermIterator i(inDrvs); i; ++i) {
|
||||
ATerm drvPath;
|
||||
@@ -120,8 +166,7 @@ Derivation parseDerivation(ATerm t)
|
||||
ATerm unparseDerivation(const Derivation & drv)
|
||||
{
|
||||
ATermList outputs = ATempty;
|
||||
for (DerivationOutputs::const_reverse_iterator i = drv.outputs.rbegin();
|
||||
i != drv.outputs.rend(); ++i)
|
||||
for (DerivationOutputs::const_reverse_iterator i = drv.outputs.rbegin(); i != drv.outputs.rend(); ++i)
|
||||
outputs = ATinsert(outputs,
|
||||
makeDerivationOutput(
|
||||
toATerm(i->first),
|
||||
@@ -129,6 +174,42 @@ ATerm unparseDerivation(const Derivation & drv)
|
||||
toATerm(i->second.hashAlgo),
|
||||
toATerm(i->second.hash)));
|
||||
|
||||
//decide if we need to add state to the derivation
|
||||
bool createState = false;
|
||||
|
||||
ATermList stateOutputs = ATempty;
|
||||
for (DerivationStateOutputs::const_reverse_iterator i = drv.stateOutputs.rbegin(); i != drv.stateOutputs.rend(); ++i){
|
||||
if(i->second.enabled == "true"){
|
||||
createState = true;
|
||||
}
|
||||
stateOutputs = ATinsert(stateOutputs,
|
||||
makeDerivationStateOutput(
|
||||
toATerm(i->first),
|
||||
toATerm(i->second.statepath),
|
||||
toATerm(i->second.componentHash),
|
||||
toATerm(i->second.hashAlgo),
|
||||
toATerm(i->second.hash),
|
||||
toATerm(i->second.stateIdentifier),
|
||||
toATerm(i->second.enabled),
|
||||
toATerm(i->second.shareType),
|
||||
toATerm(i->second.synchronization),
|
||||
toATerm(i->second.createDirsBeforeInstall),
|
||||
toATerm(i->second.runtimeStateArgs),
|
||||
toATerm(i->second.username),
|
||||
toATerm(i->second.sharedState),
|
||||
toATerm(i->second.externalState)
|
||||
));
|
||||
}
|
||||
|
||||
ATermList stateOutputDirs = ATempty;
|
||||
for (DerivationStateOutputDirs::const_reverse_iterator i = drv.stateOutputDirs.rbegin(); i != drv.stateOutputDirs.rend(); ++i)
|
||||
stateOutputDirs = ATinsert(stateOutputDirs,
|
||||
makeDerivationStateOutputDir(
|
||||
toATerm(i->first),
|
||||
toATerm(i->second.type),
|
||||
toATerm(i->second.interval)
|
||||
));
|
||||
|
||||
ATermList inDrvs = ATempty;
|
||||
for (DerivationInputs::const_reverse_iterator i = drv.inputDrvs.rbegin();
|
||||
i != drv.inputDrvs.rend(); ++i)
|
||||
@@ -150,14 +231,28 @@ ATerm unparseDerivation(const Derivation & drv)
|
||||
toATerm(i->first),
|
||||
toATerm(i->second)));
|
||||
|
||||
return makeDerive(
|
||||
outputs,
|
||||
inDrvs,
|
||||
toATermList(drv.inputSrcs),
|
||||
toATerm(drv.platform),
|
||||
toATerm(drv.builder),
|
||||
args,
|
||||
env);
|
||||
if(createState){
|
||||
return makeDerive(
|
||||
outputs,
|
||||
stateOutputs,
|
||||
stateOutputDirs,
|
||||
inDrvs,
|
||||
toATermList(drv.inputSrcs),
|
||||
toATerm(drv.platform),
|
||||
toATerm(drv.builder),
|
||||
args,
|
||||
env);
|
||||
}
|
||||
else{
|
||||
return makeDeriveWithOutState(
|
||||
outputs,
|
||||
inDrvs,
|
||||
toATermList(drv.inputSrcs),
|
||||
toATerm(drv.platform),
|
||||
toATerm(drv.builder),
|
||||
args,
|
||||
env);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
typedef struct _ATerm * ATerm;
|
||||
|
||||
#include "hash.hh"
|
||||
#include "util.hh"
|
||||
|
||||
#include <map>
|
||||
|
||||
@@ -20,8 +21,8 @@ const string drvExtension = ".drv";
|
||||
struct DerivationOutput
|
||||
{
|
||||
Path path;
|
||||
string hashAlgo; /* hash used for expected hash computation */
|
||||
string hash; /* expected hash, may be null */
|
||||
string hashAlgo; /* hash used for expected hash computation */
|
||||
string hash; /* expected hash, may be null */
|
||||
DerivationOutput()
|
||||
{
|
||||
}
|
||||
@@ -33,17 +34,147 @@ struct DerivationOutput
|
||||
}
|
||||
};
|
||||
|
||||
struct DerivationStateOutput
|
||||
{
|
||||
Path statepath;
|
||||
string componentHash;
|
||||
string hashAlgo;
|
||||
string hash;
|
||||
string stateIdentifier; //the identifier
|
||||
string enabled; //enable or disable state
|
||||
string shareType; //none, full, group
|
||||
string synchronization; //none (no locks), exclusive-lock, recursive-exclusive-lock
|
||||
|
||||
string commitReferences; //TODO none, direct, recursive-all
|
||||
string commitBinaries; //TODO list of binaries that need (or not) to be committed when these binaries are called
|
||||
|
||||
string createDirsBeforeInstall; //if true: creates state dirs before installation
|
||||
string runtimeStateArgs; //if not empty: these are the runtime parameters where state can be found (you can use $statepath here)
|
||||
|
||||
string username;
|
||||
|
||||
string sharedState; //Path to share state From
|
||||
|
||||
string externalState; //a statePath not not in the stateStore (Official hack)
|
||||
|
||||
DerivationStateOutput()
|
||||
{
|
||||
}
|
||||
|
||||
//TODO add const ??
|
||||
DerivationStateOutput(Path statepath, string componentHash, string hashAlgo, string hash, string stateIdentifier, string enabled, string shareType, string synchronization, string createDirsBeforeInstall, string runtimeStateArgs, string username, string sharedState, string externalState, bool check=true)
|
||||
{
|
||||
if(check){
|
||||
if(shareType != "none" && shareType != "full" && shareType != "group")
|
||||
throw Error(format("shareType '%1%' is not a correct type") % shareType);
|
||||
if(synchronization != "none" && synchronization != "exclusive-lock" && synchronization != "recursive-exclusive-lock")
|
||||
throw Error(format("synchronization '%1%' is not a correct type") % synchronization);
|
||||
if(username == "")
|
||||
throw Error(format("Username cannot be empty"));
|
||||
if(stateIdentifier == "__EMTPY__" || stateIdentifier == "__NOSTATE__")
|
||||
throw Error(format("the stateIdenfier cannot be this value '%1%'") % stateIdentifier);
|
||||
if(runtimeStateArgs == "__NOARGS__")
|
||||
throw Error(format("the runtimeStateArgs cannot be this value '%1%'") % runtimeStateArgs);
|
||||
if(externalState != "" && sharedState != "")
|
||||
throw Error(format("You cannot have an externalState and sharedState at the same time"));
|
||||
}
|
||||
|
||||
//TODO
|
||||
//commitReferences
|
||||
//commitBinaries
|
||||
|
||||
this->statepath = statepath;
|
||||
this->componentHash = componentHash;
|
||||
this->hashAlgo = hashAlgo;
|
||||
this->hash = hash;
|
||||
this->stateIdentifier = stateIdentifier;
|
||||
this->enabled = enabled;
|
||||
this->shareType = shareType;
|
||||
this->synchronization = synchronization;
|
||||
this->createDirsBeforeInstall = createDirsBeforeInstall;
|
||||
this->runtimeStateArgs = runtimeStateArgs;
|
||||
this->username = username;
|
||||
this->sharedState = sharedState;
|
||||
this->externalState = externalState;
|
||||
}
|
||||
|
||||
bool getEnabled(){
|
||||
return string2bool(enabled);
|
||||
}
|
||||
|
||||
bool getCreateDirsBeforeInstall(){
|
||||
return string2bool(createDirsBeforeInstall);
|
||||
}
|
||||
|
||||
/*
|
||||
* This sets all the paramters to "" to ensure they're not taken into account for the hash calculation in primops.cc
|
||||
*/
|
||||
void clearAllRuntimeParamters(){
|
||||
this->statepath = "";
|
||||
this->componentHash = "";
|
||||
//this->hashAlgo; //Clear this one?
|
||||
//this->hash; //Clear this one?
|
||||
//this->stateIdentifier; //Changes the statepath directly
|
||||
this->enabled = "";
|
||||
this->shareType = "";
|
||||
this->synchronization = "";
|
||||
this->createDirsBeforeInstall = "";
|
||||
this->runtimeStateArgs = "";
|
||||
//this->username; //Changes the statepath directly
|
||||
this->sharedState = "";
|
||||
this->externalState = "";
|
||||
|
||||
}
|
||||
};
|
||||
|
||||
struct DerivationStateOutputDir
|
||||
{
|
||||
string path;
|
||||
string type; //none, manual, interval, full
|
||||
string interval; //type int
|
||||
DerivationStateOutputDir()
|
||||
{
|
||||
}
|
||||
DerivationStateOutputDir(string path, string type, string interval)
|
||||
{
|
||||
if(type != "none" && type != "manual" && type != "interval" && type != "full")
|
||||
throw Error(format("interval '%1%' is not a correct type") % type);
|
||||
|
||||
this->path = path;
|
||||
this->type = type;
|
||||
this->interval = interval;
|
||||
}
|
||||
|
||||
int getInterval(){
|
||||
if(interval == "")
|
||||
return 0;
|
||||
else{
|
||||
int i;
|
||||
if (!string2Int(interval, i)) throw Error("interval is not a number");
|
||||
return i;
|
||||
}
|
||||
}
|
||||
|
||||
//sort function
|
||||
bool operator<(const DerivationStateOutputDir& a) const { return path < a.path; }
|
||||
};
|
||||
|
||||
|
||||
typedef std::map<string, DerivationOutput> DerivationOutputs;
|
||||
typedef std::map<string, DerivationStateOutput> DerivationStateOutputs;
|
||||
typedef std::map<string, DerivationStateOutputDir> DerivationStateOutputDirs;
|
||||
|
||||
|
||||
/* For inputs that are sub-derivations, we specify exactly which
|
||||
output IDs we are interested in. */
|
||||
typedef std::map<Path, StringSet> DerivationInputs;
|
||||
|
||||
typedef std::map<string, string> StringPairs;
|
||||
|
||||
struct Derivation
|
||||
{
|
||||
DerivationOutputs outputs; /* keyed on symbolic IDs */
|
||||
DerivationStateOutputs stateOutputs; /* TODO */
|
||||
DerivationStateOutputDirs stateOutputDirs; /* TODO */
|
||||
DerivationInputs inputDrvs; /* inputs that are sub-derivations */
|
||||
PathSet inputSrcs; /* inputs that are sources */
|
||||
string platform;
|
||||
|
||||
@@ -26,6 +26,8 @@ static string gcLockName = "gc.lock";
|
||||
static string tempRootsDir = "temproots";
|
||||
static string gcRootsDir = "gcroots";
|
||||
|
||||
static const int defaultGcLevel = 1000;
|
||||
|
||||
|
||||
/* Acquire the global GC lock. This is used to prevent new Nix
|
||||
processes from starting after the temporary root files have been
|
||||
@@ -407,12 +409,13 @@ static void addAdditionalRoots(PathSet & roots)
|
||||
static void dfsVisit(const PathSet & paths, const Path & path,
|
||||
PathSet & visited, Paths & sorted)
|
||||
{
|
||||
if (visited.find(path) != visited.end()) return;
|
||||
if (visited.find(path) != visited.end())
|
||||
return;
|
||||
visited.insert(path);
|
||||
|
||||
PathSet references;
|
||||
if (store->isValidPath(path))
|
||||
store->queryReferences(path, references);
|
||||
store->queryStoreReferences(path, references, 0);
|
||||
|
||||
for (PathSet::iterator i = references.begin();
|
||||
i != references.end(); ++i)
|
||||
@@ -424,6 +427,8 @@ static void dfsVisit(const PathSet & paths, const Path & path,
|
||||
sorted.push_front(path);
|
||||
}
|
||||
|
||||
//TODO maybe? stateTopoSortPaths(const PathSet & paths)
|
||||
|
||||
|
||||
Paths topoSortPaths(const PathSet & paths)
|
||||
{
|
||||
@@ -435,7 +440,7 @@ Paths topoSortPaths(const PathSet & paths)
|
||||
}
|
||||
|
||||
|
||||
void LocalStore::collectGarbage(GCAction action, const PathSet & pathsToDelete,
|
||||
void LocalStore::collectGarbage(GCAction action, const PathSet & pathsToDelete, //TODO ? statePathsToDelete
|
||||
bool ignoreLiveness, PathSet & result, unsigned long long & bytesFreed)
|
||||
{
|
||||
result.clear();
|
||||
@@ -445,11 +450,15 @@ void LocalStore::collectGarbage(GCAction action, const PathSet & pathsToDelete,
|
||||
queryBoolSetting("gc-keep-outputs", false);
|
||||
bool gcKeepDerivations =
|
||||
queryBoolSetting("gc-keep-derivations", true);
|
||||
int gcKeepOutputsThreshold =
|
||||
queryIntSetting ("gc-keep-outputs-threshold", defaultGcLevel);
|
||||
|
||||
//printMsg(lvlError, format("gcKeepOutputs %1% gcKeepDerivations: %2%") % gcKeepOutputs % gcKeepDerivations);
|
||||
|
||||
/* Acquire the global GC root. This prevents
|
||||
a) New roots from being added.
|
||||
b) Processes from creating new temporary root files. */
|
||||
AutoCloseFD fdGCLock = openGCLock(ltWrite);
|
||||
AutoCloseFD fdGCLock = openGCLock(ltWrite); //TODO !!!!!!!!!!!!!!!!!!! GET STATE LOCKS !!!!!!!!!!!!!!!!!!!!!!!!!!!!!
|
||||
|
||||
/* Find the roots. Since we've grabbed the GC lock, the set of
|
||||
permanent roots cannot increase now. */
|
||||
@@ -458,6 +467,7 @@ void LocalStore::collectGarbage(GCAction action, const PathSet & pathsToDelete,
|
||||
PathSet roots;
|
||||
for (Roots::iterator i = rootMap.begin(); i != rootMap.end(); ++i)
|
||||
roots.insert(i->second);
|
||||
|
||||
|
||||
/* Add additional roots returned by the program specified by the
|
||||
NIX_ROOT_FINDER environment variable. This is typically used
|
||||
@@ -475,32 +485,85 @@ void LocalStore::collectGarbage(GCAction action, const PathSet & pathsToDelete,
|
||||
roots under the `references' relation. */
|
||||
PathSet livePaths;
|
||||
for (PathSet::const_iterator i = roots.begin(); i != roots.end(); ++i)
|
||||
computeFSClosure(canonPath(*i), livePaths);
|
||||
computeFSClosure(canonPath(*i), livePaths, true, true, 0);
|
||||
|
||||
/* Note that the deriver need not be valid (e.g., if we
|
||||
previously ran the collector with `gcKeepDerivations'
|
||||
turned off). */
|
||||
if (gcKeepDerivations) {
|
||||
for (PathSet::iterator i = livePaths.begin();
|
||||
i != livePaths.end(); ++i)
|
||||
for (PathSet::iterator i = livePaths.begin(); i != livePaths.end(); ++i)
|
||||
{
|
||||
/* Note that the deriver need not be valid (e.g., if we
|
||||
previously ran the collector with `gcKeepDerivations'
|
||||
turned off). */
|
||||
Path deriver = store->queryDeriver(*i);
|
||||
if (deriver != "" && store->isValidPath(deriver))
|
||||
computeFSClosure(deriver, livePaths);
|
||||
if (store->isStateComponent(*i)){
|
||||
//printMsg(lvlError, format("Live state store '%1%'") % *i);
|
||||
|
||||
PathSet derivers = store->queryDerivers(*i); //we select ALL state Derivations here
|
||||
for (PathSet::const_iterator j = derivers.begin(); j != derivers.end(); ++j)
|
||||
if (*j != "" && store->isValidPath(*j))
|
||||
computeFSClosure(*j, livePaths, true, true, 0);
|
||||
}
|
||||
else if (store->isValidPath(*i)){
|
||||
//printMsg(lvlError, format("Live Store '%1%'") % *i);
|
||||
|
||||
Path deriver = store->queryDeriver(*i);
|
||||
if (deriver != "" && store->isValidPath(deriver))
|
||||
computeFSClosure(deriver, livePaths, true, true, 0);
|
||||
}
|
||||
else if (store->isValidStatePath(*i)){
|
||||
//printMsg(lvlError, format("Live State '%1%'") % *i);
|
||||
Path deriver = queryStatePathDrvTxn(noTxn, *i);
|
||||
|
||||
if(!store->isValidPath(deriver))
|
||||
throw Error(format("deriver `%1%' of state-store component `%2%' in GC is not valid") % deriver % *i);
|
||||
|
||||
computeFSClosure(deriver, livePaths, true, true, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (gcKeepOutputs) {
|
||||
/* Hmz, identical to storePathRequisites in nix-store. */
|
||||
for (PathSet::iterator i = livePaths.begin();
|
||||
i != livePaths.end(); ++i)
|
||||
for (PathSet::iterator i = livePaths.begin(); i != livePaths.end(); ++i)
|
||||
if (isDerivation(*i)) {
|
||||
Derivation drv = derivationFromPath(*i);
|
||||
Derivation drv = derivationFromPathTxn(noTxn, *i);
|
||||
|
||||
/*
|
||||
* TODO REMOVE
|
||||
<<<<<<< .working
|
||||
for (DerivationOutputs::iterator j = drv.outputs.begin();
|
||||
j != drv.outputs.end(); ++j)
|
||||
if (store->isValidPath(j->second.path))
|
||||
computeFSClosure(j->second.path, livePaths);
|
||||
}
|
||||
computeFSClosure(j->second.path, livePaths, true, true, 0);
|
||||
else if (store->isValidStatePath(j->second.path))
|
||||
computeFSClosure(j->second.path, livePaths, true, true, 0);
|
||||
=======
|
||||
|
||||
|
||||
string gcLevelStr = drv.env["__gcLevel"];
|
||||
int gcLevel;
|
||||
if (!string2Int(gcLevelStr,gcLevel)) {
|
||||
gcLevel = defaultGcLevel;
|
||||
}
|
||||
|
||||
if (gcLevel >= gcKeepOutputsThreshold)
|
||||
for (DerivationOutputs::iterator j = drv.outputs.begin();
|
||||
j != drv.outputs.end(); ++j)
|
||||
if (store->isValidPath(j->second.path) || store->isValidStatePath(j->second.path))
|
||||
computeFSClosure(j->second.path, livePaths, true, true, 0);
|
||||
}
|
||||
=======
|
||||
*/
|
||||
|
||||
string gcLevelStr = drv.env["__gcLevel"];
|
||||
int gcLevel;
|
||||
if (!string2Int(gcLevelStr, gcLevel))
|
||||
gcLevel = defaultGcLevel;
|
||||
|
||||
if (gcLevel >= gcKeepOutputsThreshold)
|
||||
for (DerivationOutputs::iterator j = drv.outputs.begin();
|
||||
j != drv.outputs.end(); ++j)
|
||||
if (store->isValidPath(j->second.path) || store->isValidStatePath(j->second.path))
|
||||
computeFSClosure(j->second.path, livePaths, true, true, 0);
|
||||
}
|
||||
}
|
||||
|
||||
if (action == gcReturnLive) {
|
||||
@@ -514,7 +577,7 @@ void LocalStore::collectGarbage(GCAction action, const PathSet & pathsToDelete,
|
||||
PathSet tempRoots;
|
||||
FDs fds;
|
||||
readTempRoots(tempRoots, fds);
|
||||
|
||||
|
||||
/* Close the temporary roots. Note that we *cannot* do this in
|
||||
readTempRoots(), because there we may not have all locks yet,
|
||||
meaning that an invalid path can become valid (and thus add to
|
||||
@@ -524,7 +587,9 @@ void LocalStore::collectGarbage(GCAction action, const PathSet & pathsToDelete,
|
||||
PathSet tempRootsClosed;
|
||||
for (PathSet::iterator i = tempRoots.begin(); i != tempRoots.end(); ++i)
|
||||
if (store->isValidPath(*i))
|
||||
computeFSClosure(*i, tempRootsClosed);
|
||||
computeFSClosure(*i, tempRootsClosed, true, true, 0);
|
||||
else if(store->isValidStatePath(*i))
|
||||
computeFSClosure(*i, tempRootsClosed, true, true, 0);
|
||||
else
|
||||
tempRootsClosed.insert(*i);
|
||||
|
||||
@@ -537,29 +602,67 @@ void LocalStore::collectGarbage(GCAction action, const PathSet & pathsToDelete,
|
||||
that is not currently in in `livePaths' or `tempRootsClosed'
|
||||
can be deleted. */
|
||||
|
||||
/* Read the Nix store directory to find all currently existing
|
||||
/*
|
||||
* We lookup all state (also shared) paths for: livePaths, tempRootsClosed
|
||||
*/
|
||||
PathSet allLiveStatePaths;
|
||||
for (PathSet::iterator i = livePaths.begin(); i != livePaths.end(); ++i)
|
||||
if(store->isValidStatePath(*i)){
|
||||
allLiveStatePaths.insert(*i);
|
||||
allLiveStatePaths = pathSets_union(getSharedWithPathSetRecTxn(noTxn, *i), allLiveStatePaths);
|
||||
}
|
||||
for (PathSet::iterator i = tempRootsClosed.begin(); i != tempRootsClosed.end(); ++i)
|
||||
if(store->isValidStatePath(*i)){
|
||||
allLiveStatePaths.insert(*i);
|
||||
allLiveStatePaths = pathSets_union(getSharedWithPathSetRecTxn(noTxn, *i), allLiveStatePaths);
|
||||
}
|
||||
|
||||
|
||||
/* Read the Nix store and state directory's to find all currently existing
|
||||
paths. */
|
||||
PathSet storePathSet;
|
||||
PathSet statePathSet;
|
||||
|
||||
if (action != gcDeleteSpecific) {
|
||||
|
||||
Paths entries = readDirectory(nixStore);
|
||||
Paths stateEntries = readDirectory(nixStoreState);
|
||||
|
||||
for (Paths::iterator i = entries.begin(); i != entries.end(); ++i)
|
||||
storePathSet.insert(canonPath(nixStore + "/" + *i));
|
||||
|
||||
for (Paths::iterator i = stateEntries.begin(); i != stateEntries.end(); ++i)
|
||||
statePathSet.insert(canonPath(nixStoreState + "/" + *i));
|
||||
|
||||
} else {
|
||||
for (PathSet::iterator i = pathsToDelete.begin();
|
||||
i != pathsToDelete.end(); ++i)
|
||||
{
|
||||
assertStorePath(*i);
|
||||
assertStorePath(*i); //TODO ASSERTSTATEPATH, but for now we have no arg statePathsToDelete
|
||||
storePathSet.insert(*i);
|
||||
}
|
||||
}
|
||||
|
||||
/* Topologically sort them under the `referrers' relation. That
|
||||
is, a < b iff a is in referrers(b). This gives us the order in
|
||||
is, a < b if a is in referrers(b). This gives us the order in
|
||||
which things can be deleted safely. */
|
||||
/* !!! when we have multiple output paths per derivation, this
|
||||
will not work anymore because we get cycles. */
|
||||
Paths storePaths = topoSortPaths(storePathSet);
|
||||
|
||||
for (Paths::iterator i = storePaths.begin(); i != storePaths.end(); ++i) {
|
||||
|
||||
if(*i != "/nix/store/zg8x9wdhcs4j0hvf33vg34c7m65adrpa-env-manifest")
|
||||
continue;
|
||||
|
||||
PathSet references;
|
||||
store->queryStoreReferences(*i, references, 0);
|
||||
for (PathSet::iterator j = references.begin(); j != references.end(); ++j) {
|
||||
printMsg(lvlError, format("REF `%1%'") % *j);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/* Try to delete store paths in the topologically sorted order. */
|
||||
for (Paths::iterator i = storePaths.begin(); i != storePaths.end(); ++i) {
|
||||
|
||||
@@ -576,7 +679,7 @@ void LocalStore::collectGarbage(GCAction action, const PathSet & pathsToDelete,
|
||||
debug(format("temporary root `%1%'") % *i);
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
debug(format("dead path `%1%'") % *i);
|
||||
result.insert(*i);
|
||||
|
||||
@@ -606,7 +709,7 @@ void LocalStore::collectGarbage(GCAction action, const PathSet & pathsToDelete,
|
||||
|
||||
if (!pathExists(*i)) continue;
|
||||
|
||||
printMsg(lvlInfo, format("deleting `%1%'") % *i);
|
||||
printMsg(lvlInfo, format("deleting store path `%1%'") % *i);
|
||||
|
||||
/* Okay, it's safe to delete. */
|
||||
try {
|
||||
@@ -615,7 +718,7 @@ void LocalStore::collectGarbage(GCAction action, const PathSet & pathsToDelete,
|
||||
bytesFreed += freed;
|
||||
} catch (PathInUse & e) {
|
||||
printMsg(lvlError, format("warning: %1%") % e.msg());
|
||||
}
|
||||
}
|
||||
|
||||
#ifndef __CYGWIN__
|
||||
if (fdLock != -1)
|
||||
@@ -624,6 +727,87 @@ void LocalStore::collectGarbage(GCAction action, const PathSet & pathsToDelete,
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
/* Try to delete state paths. */
|
||||
/* Topologically sort them under the `referrers' relation. That
|
||||
is, a < b if a is in referrers(b). This gives us the order in
|
||||
which things can be deleted safely. */
|
||||
/* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! when we have multiple output paths per derivation, this
|
||||
will not work anymore because we get cycles. */
|
||||
Paths statePaths;
|
||||
for (PathSet::iterator i = statePathSet.begin(); i != statePathSet.end(); ++i)
|
||||
statePaths.push_back(*i);
|
||||
|
||||
//
|
||||
for (Paths::iterator i = statePaths.begin(); i != statePaths.end(); ++i) {
|
||||
|
||||
debug(format("considering deletion of state path `%1%'") % *i);
|
||||
|
||||
if (allLiveStatePaths.find(*i) != allLiveStatePaths.end()) {
|
||||
debug(format("State path `%1%' is alive (possibyly shared with another live path)") % *i);
|
||||
continue;
|
||||
}
|
||||
|
||||
string lostAndFound = nixStoreState + "/lost+found";
|
||||
if(*i == lostAndFound){
|
||||
debug(format("Keeping `%1%' because its a special path") % lostAndFound);
|
||||
continue;
|
||||
}
|
||||
|
||||
debug(format("dead path `%1%'") % *i);
|
||||
result.insert(*i);
|
||||
|
||||
/* If just returning the set of dead paths, we also return the
|
||||
space that would be freed if we deleted them. */
|
||||
if (action == gcReturnDead)
|
||||
bytesFreed += computePathSize(*i);
|
||||
|
||||
if (action == gcDeleteDead || action == gcDeleteSpecific) {
|
||||
|
||||
//TODO !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! state locks
|
||||
/*
|
||||
#ifndef __CYGWIN__
|
||||
AutoCloseFD fdLock;
|
||||
|
||||
/* Only delete a lock file if we can acquire a write lock
|
||||
on it. That means that it's either stale, or the
|
||||
process that created it hasn't locked it yet. In the
|
||||
latter case the other process will detect that we
|
||||
deleted the lock, and retry (see pathlocks.cc). * /
|
||||
if (i->size() >= 5 && string(*i, i->size() - 5) == ".lock") {
|
||||
fdLock = openLockFile(*i, false);
|
||||
if (fdLock != -1 && !lockFile(fdLock, ltWrite, false)) {
|
||||
debug(format("skipping active lock `%1%'") % *i);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
*/
|
||||
|
||||
if (!pathExists(*i)) continue;
|
||||
|
||||
printMsg(lvlInfo, format("deleting state path `%1%'") % *i);
|
||||
|
||||
/* Okay, it's safe to delete. */
|
||||
try {
|
||||
unsigned long long freed;
|
||||
deleteFromState(*i, freed);
|
||||
bytesFreed += freed;
|
||||
} catch (PathInUse & e) {
|
||||
printMsg(lvlError, format("warning: %1%") % e.msg());
|
||||
}
|
||||
|
||||
/*
|
||||
#ifndef __CYGWIN__
|
||||
if (fdLock != -1)
|
||||
/* Write token to stale (deleted) lock file. * /
|
||||
writeFull(fdLock, (const unsigned char *) "d", 1);
|
||||
#endif
|
||||
*/
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -3,16 +3,19 @@
|
||||
|
||||
#include <map>
|
||||
#include <algorithm>
|
||||
|
||||
#include <pwd.h>
|
||||
|
||||
namespace nix {
|
||||
|
||||
|
||||
string nixStore = "/UNINIT";
|
||||
string nixStoreState = "/UNINIT";
|
||||
string nixDataDir = "/UNINIT";
|
||||
string nixLogDir = "/UNINIT";
|
||||
string nixStateDir = "/UNINIT";
|
||||
string nixDBPath = "/UNINIT";
|
||||
string nixExt3CowHeader = "/UNINIT";
|
||||
string nixRsync = "/UNINIT";
|
||||
string nixConfDir = "/UNINIT";
|
||||
string nixLibexecDir = "/UNINIT";
|
||||
string nixBinDir = "/UNINIT";
|
||||
@@ -26,10 +29,16 @@ bool readOnlyMode = false;
|
||||
string thisSystem = "unset";
|
||||
unsigned int maxSilentTime = 0;
|
||||
Paths substituters;
|
||||
bool useBuildHook = true;
|
||||
|
||||
|
||||
static bool settingsRead = false;
|
||||
|
||||
uid_t callingUID = 0; //A root user will not set this value, so the default uid is 0
|
||||
bool singleThreaded = false; //TODO Gives an error: cannot start worker (environment already open) / waiting for process X: No child processes
|
||||
bool sendOutput = true;
|
||||
bool sleepForGDB = false;
|
||||
|
||||
static std::map<string, Strings> settings;
|
||||
|
||||
|
||||
@@ -115,5 +124,39 @@ unsigned int queryIntSetting(const string & name, unsigned int def)
|
||||
return n;
|
||||
}
|
||||
|
||||
|
||||
uid_t queryCallingUID()
|
||||
{
|
||||
/* A root user will not even bother calling the daemon, so there is no way to check
|
||||
* If the uid is not yet set...
|
||||
*/
|
||||
return callingUID;
|
||||
}
|
||||
|
||||
void setCallingUID(uid_t uid, bool reset)
|
||||
{
|
||||
if(callingUID != 0 && !reset)
|
||||
throw Error(format("The UID of the caller aleady set! at this point"));
|
||||
|
||||
callingUID = uid;
|
||||
}
|
||||
|
||||
string uidToUsername(uid_t uid)
|
||||
{
|
||||
passwd *pwd = getpwuid(uid);
|
||||
char *pw_name = pwd->pw_name;
|
||||
return (string)pw_name;
|
||||
}
|
||||
|
||||
string queryCallingUsername()
|
||||
{
|
||||
uid_t uid = queryCallingUID();
|
||||
return uidToUsername(uid);
|
||||
}
|
||||
|
||||
string queryCurrentUsername()
|
||||
{
|
||||
return uidToUsername(geteuid());
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -18,12 +18,21 @@ extern string nixDataDir; /* !!! fix */
|
||||
/* nixLogDir is the directory where we log various operations. */
|
||||
extern string nixLogDir;
|
||||
|
||||
/* nixStoreState is the directory where the state dirs of the components are stored. */
|
||||
extern string nixStoreState;
|
||||
|
||||
/* nixStateDir is the directory where state is stored. */
|
||||
extern string nixStateDir;
|
||||
|
||||
/* nixDBPath is the path name of our Berkeley DB environment. */
|
||||
extern string nixDBPath;
|
||||
|
||||
/* nixExt3CowHeader is the header file used to communicate with ext3cow. */
|
||||
extern string nixExt3CowHeader;
|
||||
|
||||
/* nixRsync is used to copy from one statedir to the other. */
|
||||
extern string nixRsync;
|
||||
|
||||
/* nixConfDir is the directory where configuration files are
|
||||
stored. */
|
||||
extern string nixConfDir;
|
||||
@@ -35,7 +44,6 @@ extern string nixLibexecDir;
|
||||
/* nixBinDir is the directory where the main programs are stored. */
|
||||
extern string nixBinDir;
|
||||
|
||||
|
||||
/* Misc. global flags. */
|
||||
|
||||
/* Whether to keep temporary directories of failed builds. */
|
||||
@@ -72,6 +80,9 @@ extern unsigned int maxSilentTime;
|
||||
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;
|
||||
|
||||
Strings querySetting(const string & name, const Strings & def);
|
||||
|
||||
@@ -81,7 +92,27 @@ bool queryBoolSetting(const string & name, bool def);
|
||||
|
||||
unsigned int queryIntSetting(const string & name, unsigned int def);
|
||||
|
||||
|
||||
/* TODO PRIVATE: UID of the user that calls the nix-worker daemon */
|
||||
extern uid_t callingUID;
|
||||
|
||||
/* get/set the UID of the user that calls the nix-worker daemon */
|
||||
uid_t queryCallingUID();
|
||||
void setCallingUID(uid_t uid, bool reset = false);
|
||||
|
||||
/* Convert a uid to a username: Watch it! this segfaults when given a wrong uid !! */
|
||||
string uidToUsername(uid_t uid);
|
||||
|
||||
/* get the username based on the UID of the user that calls the nix-worker daemon */
|
||||
string queryCallingUsername();
|
||||
|
||||
/* get the username based on the UID of the user currently runs the process */
|
||||
string queryCurrentUsername();
|
||||
|
||||
/* Debugging variables */
|
||||
extern bool singleThreaded;
|
||||
extern bool sendOutput;
|
||||
extern bool sleepForGDB;
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -58,12 +58,24 @@ public:
|
||||
/* Implementations of abstract store API methods. */
|
||||
|
||||
bool isValidPath(const Path & path);
|
||||
|
||||
|
||||
bool isValidStatePath(const Path & path);
|
||||
|
||||
bool isValidComponentOrStatePath(const Path & path);
|
||||
|
||||
PathSet queryValidPaths();
|
||||
|
||||
Hash queryPathHash(const Path & path);
|
||||
|
||||
Path queryStatePathDrv(const Path & statePath);
|
||||
|
||||
void queryReferences(const Path & path, PathSet & references);
|
||||
|
||||
void queryReferrers(const Path & path, PathSet & referrers);
|
||||
void queryStoreReferences(const Path & path, PathSet & references, const unsigned int revision);
|
||||
|
||||
void queryStateReferences(const Path & storePath, PathSet & stateReferences, const unsigned int revision);
|
||||
|
||||
void queryStoreReferrers(const Path & path, PathSet & referrers, const unsigned int revision);
|
||||
|
||||
void queryStateReferrers(const Path & path, PathSet & stateReferrers, const unsigned int revision);
|
||||
|
||||
Path queryDeriver(const Path & path);
|
||||
|
||||
@@ -97,6 +109,45 @@ public:
|
||||
|
||||
void collectGarbage(GCAction action, const PathSet & pathsToDelete,
|
||||
bool ignoreLiveness, PathSet & result, unsigned long long & bytesFreed);
|
||||
|
||||
/////////////////////////////
|
||||
|
||||
void setStatePathsInterval(const Path & statePath, const CommitIntervals & intervals);
|
||||
|
||||
CommitIntervals getStatePathsInterval(const Path & statePath);
|
||||
|
||||
bool isStateComponent(const Path & storePath);
|
||||
|
||||
void storePathRequisites(const Path & storeOrstatePath, const bool includeOutputs, PathSet & paths, const bool withComponents, const bool withState, const unsigned int revision);
|
||||
|
||||
void setStateRevisions(const RevisionClosure & revisions, const Path & rootStatePath, const string & comment);
|
||||
|
||||
bool queryStateRevisions(const Path & statePath, RevisionClosure & revisions, RevisionClosureTS & timestamps, const unsigned int revision);
|
||||
|
||||
bool queryAvailableStateRevisions(const Path & statePath, RevisionInfos & revisions);
|
||||
|
||||
Snapshots commitStatePath(const Path & statePath);
|
||||
|
||||
PathSet queryDerivers(const Path & storePath); //should these be in here ????
|
||||
|
||||
void scanAndUpdateAllReferences(const Path & statePath, const bool recursive);
|
||||
|
||||
bool getSharedWith(const Path & statePath1, Path & statePath2);
|
||||
|
||||
PathSet toNonSharedPathSet(const PathSet & statePaths);
|
||||
|
||||
void revertToRevision(const Path & statePath, const unsigned int revision_arg, const bool recursive);
|
||||
|
||||
void shareState(const Path & from, const Path & to, const bool snapshot);
|
||||
|
||||
void unShareState(const Path & path, const bool branch, const bool restoreOld);
|
||||
|
||||
Path lookupStatePath(const Path & storePath, const string & identifier, const string & user);
|
||||
|
||||
void setStateOptions(const Path & statePath, const string & user, const string & group, int chmod, const string & runtimeArgs);
|
||||
|
||||
void getStateOptions(const Path & statePath, string & user, string & group, int & chmod, string & runtimeArgs);
|
||||
|
||||
|
||||
/* Optimise the disk space usage of the Nix store by hard-linking
|
||||
files with the same contents. */
|
||||
@@ -117,8 +168,9 @@ void copyPath(const Path & src, const Path & dst);
|
||||
of the file system contents of the path. The hash must be a
|
||||
SHA-256 hash. */
|
||||
void registerValidPath(const Transaction & txn,
|
||||
const Path & path, const Hash & hash, const PathSet & references,
|
||||
const Path & deriver);
|
||||
const Path & component_or_state_path, const Hash & hash,
|
||||
const PathSet & references, const PathSet & stateReferences,
|
||||
const Path & deriver, const unsigned int revision);
|
||||
|
||||
typedef list<ValidPathInfo> ValidPathInfos;
|
||||
|
||||
@@ -138,18 +190,27 @@ void canonicalisePathMetaData(const Path & path);
|
||||
/* Checks whether a path is valid. */
|
||||
bool isValidPathTxn(const Transaction & txn, const Path & path);
|
||||
|
||||
/* Sets the set of outgoing FS references for a store path. Use with
|
||||
care! */
|
||||
void setReferences(const Transaction & txn, const Path & path,
|
||||
const PathSet & references);
|
||||
/* Sets the set of outgoing FS (also state) references for a store path. Use with
|
||||
care!
|
||||
|
||||
0 for revision means overwrite the last revision
|
||||
*/
|
||||
void setReferences(const Transaction & txn, const Path & store_or_statePath,
|
||||
const PathSet & references, const PathSet & stateReferences, const unsigned int revision);
|
||||
|
||||
/* Sets the deriver of a store path. Use with care! */
|
||||
void setDeriver(const Transaction & txn, const Path & path,
|
||||
const Path & deriver);
|
||||
|
||||
/* Query the derivers of a state-store path. */
|
||||
PathSet queryDerivers(const Transaction & txn, const Path & storePath);
|
||||
|
||||
/* Delete a value from the nixStore directory. */
|
||||
void deleteFromStore(const Path & path, unsigned long long & bytesFreed);
|
||||
|
||||
/* Delete a value from the nixState directory. */
|
||||
void deleteFromState(const Path & _path, unsigned long long & bytesFreed);
|
||||
|
||||
MakeError(PathInUse, Error);
|
||||
|
||||
void verifyStore(bool checkContents);
|
||||
@@ -169,7 +230,67 @@ void deletePathWrapped(const Path & path,
|
||||
unsigned long long & bytesFreed);
|
||||
|
||||
void deletePathWrapped(const Path & path);
|
||||
|
||||
|
||||
/* Adds a new deriver based on storePath to the dbDerivers table */
|
||||
void addStateDeriver(const Transaction & txn, const Path & storePath, const Path & deriver);
|
||||
|
||||
bool isStateComponentTxn(const Transaction & txn, const Path & path);
|
||||
|
||||
bool isStateDrvPathTxn(const Transaction & txn, const Path & drvPath);
|
||||
|
||||
bool isStateDrv(const Derivation & drv);
|
||||
|
||||
|
||||
//TODO CHECK IF THESE DONT BELONG HERE, REFACTOR CODE, EG MOVE FUNCTIONS AROUND
|
||||
|
||||
void queryAllValidPathsTxn(const Transaction & txn, PathSet & allComponentPaths, PathSet & allStatePaths);
|
||||
bool isValidStatePathTxn(const Transaction & txn, const Path & path);
|
||||
|
||||
void queryXReferencesTxn(const Transaction & txn, const Path & path, PathSet & references, const bool component_or_state, const unsigned int revision, const unsigned int timestamp = 0);
|
||||
|
||||
void setStateComponentReferencesTxn(const Transaction & txn, const Path & statePath, const Strings & references, const unsigned int revision, const unsigned int timestamp);
|
||||
void setStateStateReferencesTxn(const Transaction & txn, const Path & statePath, const Strings & references, const unsigned int revision, const unsigned int timestamp);
|
||||
|
||||
void queryStoreReferrersTxn(const Transaction & txn, const Path & storePath, PathSet & referrers, const unsigned int revision);
|
||||
void queryStateReferrersTxn(const Transaction & txn, const Path & storePath, PathSet & stateReferrers, const unsigned int revision);
|
||||
|
||||
Path queryStatePathDrvTxn(const Transaction & txn, const Path & statePath);
|
||||
void storePathRequisitesTxn(const Transaction & txn, const Path & storeOrstatePath, const bool includeOutputs, PathSet & paths, const bool withComponents, const bool withState, const unsigned int revision);
|
||||
void setStateRevisionsTxn(const Transaction & txn, const RevisionClosure & revisions, const Path & rootStatePath, const string & comment);
|
||||
|
||||
bool isValidPathTxn(const Transaction & txn, const Path & path);
|
||||
bool isValidStatePathTxn(const Transaction & txn, const Path & path);
|
||||
|
||||
void setSolidStateReferencesTxn(const Transaction & txn, const Path & statePath, const PathSet & paths);
|
||||
bool querySolidStateReferencesTxn(const Transaction & txn, const Path & statePath, PathSet & paths);
|
||||
|
||||
void shareStateTxn(const Transaction & txn, const Path & from, const Path & to, const bool snapshot);
|
||||
void unShareStateTxn(const Transaction & txn, const Path & path, const bool branch, const bool restoreOld);
|
||||
|
||||
PathSet toNonSharedPathSetTxn(const Transaction & txn, const PathSet & statePaths);
|
||||
Path toNonSharedPathTxn(const Transaction & txn, const Path & statePath);
|
||||
|
||||
/*
|
||||
* Returns a pathset with all paths (including itself) that eventually share the same statePath
|
||||
*/
|
||||
PathSet getSharedWithPathSetRecTxn(const Transaction & txn, const Path & statePath);
|
||||
|
||||
void ensurePathTxn(const Transaction & txn, const Path & path);
|
||||
|
||||
CommitIntervals getStatePathsIntervalTxn(const Transaction & txn, const Path & statePath);
|
||||
void setStatePathsIntervalTxn(const Transaction & txn, const Path & statePath, const CommitIntervals & intervals);
|
||||
|
||||
bool queryStateRevisionsTxn(const Transaction & txn, const Path & statePath, RevisionClosure & revisions, RevisionClosureTS & timestamps, const unsigned int revision);
|
||||
bool querySharedStateTxn(const Transaction & txn, const Path & statePath, Path & shared_with);
|
||||
|
||||
void setStateComponentTxn(const Transaction & txn, const Path & storePath, const Path & statePath, const string & identifier, const string & user);
|
||||
|
||||
void setVersionedStateEntriesTxn(const Transaction & txn, const Path & statePath, const StateInfos & infos, const unsigned int revision = 0, const unsigned int timestamp = 0);
|
||||
bool getVersionedStateEntriesTxn(const Transaction & txn, const Path & statePath, StateInfos & infos, const unsigned int revision = 0, const unsigned int timestamp = 0);
|
||||
|
||||
void setStateOptionsTxn(const Transaction & txn, const Path & statePath, const string & user, const string & group, int chmod, const string & runtimeArgs);
|
||||
void getStateOptionsTxn(const Transaction & txn, const Path & statePath, string & user, string & group, int & chmod, string & runtimeArgs);
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#include "misc.hh"
|
||||
#include "store-api.hh"
|
||||
#include "db.hh"
|
||||
#include "local-store.hh"
|
||||
|
||||
#include <aterm2.h>
|
||||
|
||||
@@ -8,31 +9,92 @@
|
||||
namespace nix {
|
||||
|
||||
|
||||
Derivation derivationFromPath(const Path & drvPath)
|
||||
Derivation derivationFromPathPrivate(const bool dotxn, const Transaction & txn, const Path & drvPath)
|
||||
{
|
||||
assertStorePath(drvPath);
|
||||
store->ensurePath(drvPath);
|
||||
if(dotxn)
|
||||
ensurePathTxn(txn, drvPath);
|
||||
else
|
||||
store->ensurePath(drvPath);
|
||||
|
||||
ATerm t = ATreadFromNamedFile(drvPath.c_str());
|
||||
if (!t) throw Error(format("cannot read aterm from `%1%'") % drvPath);
|
||||
if (!t)
|
||||
throw Error(format("cannot read aterm from `%1%'") % drvPath);
|
||||
return parseDerivation(t);
|
||||
}
|
||||
|
||||
|
||||
void computeFSClosure(const Path & storePath,
|
||||
PathSet & paths, bool flipDirection)
|
||||
//Wrappers
|
||||
Derivation derivationFromPath(const Path & drvPath)
|
||||
{
|
||||
if (paths.find(storePath) != paths.end()) return;
|
||||
paths.insert(storePath);
|
||||
return derivationFromPathPrivate(false, noTxn, drvPath);
|
||||
}
|
||||
Derivation derivationFromPathTxn(const Transaction & txn, const Path & drvPath)
|
||||
{
|
||||
return derivationFromPathPrivate(true, txn, drvPath);
|
||||
}
|
||||
|
||||
void computeFSClosure(const Path & path, PathSet & paths, const bool & withComponents, const bool & withState, const int revision, bool flipDirection)
|
||||
{
|
||||
computeFSClosureTxn(noTxn, path, paths, withComponents, withState, revision, flipDirection);
|
||||
}
|
||||
|
||||
/*
|
||||
* Notice that the incoming 'paths' parameter can already be filled and only needs to be expanded
|
||||
* Notice that we CANNOT change the order of the existing paths
|
||||
*/
|
||||
void computeFSClosureTxn(const Transaction & txn, const Path & path, PathSet & paths, const bool & withComponents, const bool & withState, const int revision, bool flipDirection)
|
||||
{
|
||||
PathSet allPaths;
|
||||
computeFSClosureRecTxn(txn, path, allPaths, revision, flipDirection);
|
||||
|
||||
if(!withComponents && !withState)
|
||||
throw Error(format("Useless call to computeFSClosure, at leat withComponents or withState must be true"));
|
||||
|
||||
for (PathSet::iterator i = allPaths.begin(); i != allPaths.end(); ++i)
|
||||
if ( !isValidPathTxn(txn, *i) && !isValidStatePathTxn(txn, *i) )
|
||||
throw Error(format("Not a state or store path: ") % *i);
|
||||
|
||||
//if withComponents, we add all component paths
|
||||
if( withComponents ){
|
||||
for (PathSet::iterator i = allPaths.begin(); i != allPaths.end(); ++i)
|
||||
if ( isValidPathTxn(txn, *i) )
|
||||
paths.insert(*i);
|
||||
}
|
||||
|
||||
//if withState, we add all state paths
|
||||
if( withState ){
|
||||
for (PathSet::iterator i = allPaths.begin(); i != allPaths.end(); ++i)
|
||||
if ( isValidStatePathTxn(txn, *i) )
|
||||
paths.insert(*i);
|
||||
}
|
||||
}
|
||||
|
||||
void computeFSClosureRecTxn(const Transaction & txn, const Path & path, PathSet & paths, const int revision, const bool & flipDirection)
|
||||
{
|
||||
if (paths.find(path) != paths.end()) //takes care of double entries
|
||||
return;
|
||||
|
||||
//printMsg(lvlError, format("ComputeFSClosureRec '%1%'") % path);
|
||||
|
||||
paths.insert(path);
|
||||
|
||||
PathSet references;
|
||||
if (flipDirection)
|
||||
store->queryReferrers(storePath, references);
|
||||
else
|
||||
store->queryReferences(storePath, references);
|
||||
PathSet stateReferences;
|
||||
|
||||
if (flipDirection){
|
||||
queryStoreReferrersTxn(txn, path, references, revision);
|
||||
queryStateReferrersTxn(txn, path, stateReferences, revision);
|
||||
}
|
||||
else{
|
||||
queryXReferencesTxn(txn, path, references, true, revision);
|
||||
queryXReferencesTxn(txn, path, stateReferences, false, revision);
|
||||
}
|
||||
|
||||
PathSet allReferences;
|
||||
allReferences = pathSets_union(references, stateReferences);
|
||||
|
||||
for (PathSet::iterator i = references.begin();
|
||||
i != references.end(); ++i)
|
||||
computeFSClosure(*i, paths, flipDirection);
|
||||
for (PathSet::iterator i = allReferences.begin(); i != allReferences.end(); ++i)
|
||||
computeFSClosureRecTxn(txn, *i, paths, revision, flipDirection);
|
||||
}
|
||||
|
||||
|
||||
@@ -58,7 +120,7 @@ void queryMissing(const PathSet & targets,
|
||||
|
||||
if (isDerivation(p)) {
|
||||
if (!store->isValidPath(p)) continue;
|
||||
Derivation drv = derivationFromPath(p);
|
||||
Derivation drv = derivationFromPathTxn(noTxn, p);
|
||||
|
||||
bool mustBuild = false;
|
||||
for (DerivationOutputs::iterator i = drv.outputs.begin();
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
#define __MISC_H
|
||||
|
||||
#include "derivations.hh"
|
||||
#include "db.hh"
|
||||
|
||||
|
||||
namespace nix {
|
||||
@@ -9,6 +10,9 @@ namespace nix {
|
||||
|
||||
/* Read a derivation, after ensuring its existence through
|
||||
ensurePath(). */
|
||||
Derivation derivationFromPathTxn(const Transaction & txn, const Path & drvPath);
|
||||
|
||||
/* Same as above, but wihouth a txn now. This function can be called from the user side */
|
||||
Derivation derivationFromPath(const Path & drvPath);
|
||||
|
||||
/* Place in `paths' the set of all store paths in the file system
|
||||
@@ -17,9 +21,18 @@ Derivation derivationFromPath(const Path & drvPath);
|
||||
`flipDirection' is true, the set of paths that can reach
|
||||
`storePath' is returned; that is, the closures under the
|
||||
`referrers' relation instead of the `references' relation is
|
||||
|
||||
withState = include the state paths into the closure
|
||||
withComponents = include the component paths into the closure
|
||||
|
||||
We can currentky only compute the closure of the latsest revision!
|
||||
|
||||
returned. */
|
||||
void computeFSClosure(const Path & storePath,
|
||||
PathSet & paths, bool flipDirection = false);
|
||||
void computeFSClosure(const Path & storePath, PathSet & paths, const bool & withComponents, const bool & withState, const int revision, bool flipDirection = false);
|
||||
|
||||
void computeFSClosureTxn(const Transaction & txn, const Path & storePath, PathSet & paths, const bool & withComponents, const bool & withState, const int revision, bool flipDirection = false);
|
||||
|
||||
void computeFSClosureRecTxn(const Transaction & txn, const Path & path, PathSet & paths, const int revision, const bool & flipDirection); //TODO private
|
||||
|
||||
/* Return the path corresponding to the output identifier `id' in the
|
||||
given derivation. */
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#include "references.hh"
|
||||
#include "hash.hh"
|
||||
#include "util.hh"
|
||||
#include "local-store.hh"
|
||||
|
||||
#include <cerrno>
|
||||
#include <map>
|
||||
@@ -117,8 +118,36 @@ void checkPath(const string & path,
|
||||
else throw Error(format("unknown file type: %1%") % path);
|
||||
}
|
||||
|
||||
|
||||
PathSet scanForReferences(const string & path, const PathSet & paths)
|
||||
{
|
||||
return scanForReferencesTxn(noTxn, path, paths);
|
||||
}
|
||||
|
||||
PathSet scanForReferencesTxn(const Transaction & txn, const Path & path, const PathSet & paths)
|
||||
{
|
||||
return scanForReferencesTxn_(txn, path, paths);
|
||||
}
|
||||
|
||||
PathSet scanForStateReferences(const string & path, const PathSet & paths)
|
||||
{
|
||||
return scanForStateReferencesTxn(noTxn, path, paths);
|
||||
}
|
||||
|
||||
PathSet scanForStateReferencesTxn(const Transaction & txn, const Path & path, const PathSet & paths)
|
||||
{
|
||||
//Get the references from the scan
|
||||
PathSet org_references = scanForReferencesTxn_(txn, path, paths);
|
||||
|
||||
//Get the solid state dependencies, and also insert them
|
||||
PathSet solidStateDeps;
|
||||
querySolidStateReferencesTxn(txn, path, solidStateDeps);
|
||||
org_references.insert(solidStateDeps.begin(), solidStateDeps.end());
|
||||
|
||||
return org_references;
|
||||
}
|
||||
|
||||
|
||||
PathSet scanForReferencesTxn_(const Transaction & txn, const Path & path, const PathSet & paths)
|
||||
{
|
||||
std::map<string, Path> backMap;
|
||||
StringSet ids;
|
||||
@@ -134,6 +163,7 @@ PathSet scanForReferences(const string & path, const PathSet & paths)
|
||||
throw Error(format("bad reference `%1%'") % *i);
|
||||
string s = string(baseName, 0, pos);
|
||||
assert(s.size() == refLength);
|
||||
//printMsg(lvlError, format("BACKMAP[%1%] = '%2%'") % s % *i);
|
||||
assert(backMap.find(s) == backMap.end());
|
||||
// parseHash(htSHA256, s);
|
||||
ids.insert(s);
|
||||
@@ -152,5 +182,5 @@ PathSet scanForReferences(const string & path, const PathSet & paths)
|
||||
return found;
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -2,11 +2,21 @@
|
||||
#define __REFERENCES_H
|
||||
|
||||
#include "types.hh"
|
||||
#include "db.hh"
|
||||
|
||||
namespace nix {
|
||||
|
||||
/* Scans for Component References (currently doesnt add solid dependencys) */
|
||||
PathSet scanForReferences(const Path & path, const PathSet & refs);
|
||||
|
||||
PathSet scanForReferencesTxn(const Transaction & txn, const Path & path, const PathSet & refs);
|
||||
|
||||
/* Scans for State References and adds solid state dependencys*/
|
||||
PathSet scanForStateReferences(const Path & path, const PathSet & refs);
|
||||
PathSet scanForStateReferencesTxn(const Transaction & txn, const Path & path, const PathSet & refs);
|
||||
|
||||
/* The original scanForReferences */
|
||||
PathSet scanForReferencesTxn_(const Transaction & txn, const Path & path, const PathSet & refs);
|
||||
|
||||
}
|
||||
|
||||
#endif /* !__REFERENCES_H */
|
||||
|
||||
@@ -18,27 +18,61 @@
|
||||
namespace nix {
|
||||
|
||||
|
||||
Path readStorePath(Source & from)
|
||||
Path readXPath(Source & from, const bool canBeStore, const bool canBeState)
|
||||
{
|
||||
Path path = readString(from);
|
||||
assertStorePath(path);
|
||||
|
||||
if(canBeStore && canBeState)
|
||||
assertStoreOrStatePath(path);
|
||||
else if(canBeStore)
|
||||
assertStorePath(path);
|
||||
else if(canBeState)
|
||||
assertStatePath(path);
|
||||
|
||||
return path;
|
||||
}
|
||||
Path readStorePath(Source & from)
|
||||
{
|
||||
return readXPath(from, true, false);
|
||||
}
|
||||
Path readStatePath(Source & from)
|
||||
{
|
||||
return readXPath(from, false, true);
|
||||
}
|
||||
Path readStoreOrStatePath(Source & from)
|
||||
{
|
||||
return readXPath(from, true, true);
|
||||
}
|
||||
|
||||
|
||||
PathSet readStorePaths(Source & from)
|
||||
PathSet readXPaths(Source & from, const bool canBeStore, const bool canBeState)
|
||||
{
|
||||
PathSet paths = readStringSet(from);
|
||||
for (PathSet::iterator i = paths.begin(); i != paths.end(); ++i)
|
||||
assertStorePath(*i);
|
||||
for (PathSet::iterator i = paths.begin(); i != paths.end(); ++i){
|
||||
if(canBeStore && canBeState)
|
||||
assertStoreOrStatePath(*i);
|
||||
else if(canBeStore)
|
||||
assertStorePath(*i);
|
||||
else if(canBeState)
|
||||
assertStatePath(*i);
|
||||
}
|
||||
return paths;
|
||||
}
|
||||
PathSet readStorePaths(Source & from)
|
||||
{
|
||||
return readXPaths(from, true, false);
|
||||
}
|
||||
PathSet readStatePaths(Source & from)
|
||||
{
|
||||
return readXPaths(from, false, true);
|
||||
}
|
||||
|
||||
|
||||
|
||||
RemoteStore::RemoteStore()
|
||||
{
|
||||
string remoteMode = getEnv("NIX_REMOTE");
|
||||
|
||||
debug(format("Client remoteMode: '%1%'") % remoteMode);
|
||||
if (remoteMode == "slave")
|
||||
/* Fork off a setuid worker to do the privileged work. */
|
||||
forkSlave();
|
||||
@@ -59,7 +93,7 @@ RemoteStore::RemoteStore()
|
||||
unsigned int magic = readInt(from);
|
||||
if (magic != WORKER_MAGIC_2) throw Error("protocol mismatch");
|
||||
|
||||
unsigned int daemonVersion = readInt(from);
|
||||
daemonVersion = readInt(from);
|
||||
if (GET_PROTOCOL_MAJOR(daemonVersion) != GET_PROTOCOL_MAJOR(PROTOCOL_VERSION))
|
||||
throw Error("Nix daemon protocol version not supported");
|
||||
writeInt(PROTOCOL_VERSION, to);
|
||||
@@ -169,6 +203,8 @@ void RemoteStore::setOptions()
|
||||
writeInt(verbosity, to);
|
||||
writeInt(maxBuildJobs, to);
|
||||
writeInt(maxSilentTime, to);
|
||||
if (GET_PROTOCOL_MINOR(daemonVersion) >= 2)
|
||||
writeInt(useBuildHook, to);
|
||||
processStderr();
|
||||
}
|
||||
|
||||
@@ -182,6 +218,29 @@ bool RemoteStore::isValidPath(const Path & path)
|
||||
return reply != 0;
|
||||
}
|
||||
|
||||
bool RemoteStore::isValidStatePath(const Path & path)
|
||||
{
|
||||
writeInt(wopIsValidStatePath, to);
|
||||
writeString(path, to);
|
||||
processStderr();
|
||||
unsigned int reply = readInt(from);
|
||||
return reply != 0;
|
||||
}
|
||||
|
||||
bool RemoteStore::isValidComponentOrStatePath(const Path & path)
|
||||
{
|
||||
writeInt(wopIsValidComponentOrStatePath, to);
|
||||
writeString(path, to);
|
||||
processStderr();
|
||||
unsigned int reply = readInt(from);
|
||||
return reply != 0;
|
||||
}
|
||||
|
||||
PathSet RemoteStore::queryValidPaths()
|
||||
{
|
||||
throw Error("not implemented");
|
||||
}
|
||||
|
||||
|
||||
bool RemoteStore::hasSubstitutes(const Path & path)
|
||||
{
|
||||
@@ -202,35 +261,68 @@ Hash RemoteStore::queryPathHash(const Path & path)
|
||||
return parseHash(htSHA256, hash);
|
||||
}
|
||||
|
||||
|
||||
void RemoteStore::queryReferences(const Path & path,
|
||||
PathSet & references)
|
||||
Path RemoteStore::queryStatePathDrv(const Path & statePath)
|
||||
{
|
||||
writeInt(wopQueryReferences, to);
|
||||
writeInt(wopQueryStatePathDrv, to);
|
||||
writeString(statePath, to);
|
||||
processStderr();
|
||||
return readString(from);
|
||||
}
|
||||
|
||||
void RemoteStore::queryStoreReferences(const Path & path,
|
||||
PathSet & references, const unsigned int revision)
|
||||
{
|
||||
writeInt(wopQueryStoreReferences, to);
|
||||
writeString(path, to);
|
||||
writeBigUnsignedInt(revision, to);
|
||||
processStderr();
|
||||
PathSet references2 = readStorePaths(from);
|
||||
references.insert(references2.begin(), references2.end());
|
||||
}
|
||||
|
||||
|
||||
void RemoteStore::queryReferrers(const Path & path,
|
||||
PathSet & referrers)
|
||||
void RemoteStore::queryStateReferences(const Path & path,
|
||||
PathSet & stateReferences, const unsigned int revision)
|
||||
{
|
||||
writeInt(wopQueryReferrers, to);
|
||||
writeInt(wopQueryStateReferences, to);
|
||||
writeString(path, to);
|
||||
writeBigUnsignedInt(revision, to);
|
||||
processStderr();
|
||||
PathSet stateReferences2 = readStatePaths(from);
|
||||
stateReferences.insert(stateReferences2.begin(), stateReferences2.end());
|
||||
}
|
||||
|
||||
|
||||
void RemoteStore::queryStoreReferrers(const Path & path,
|
||||
PathSet & referrers, const unsigned int revision)
|
||||
{
|
||||
writeInt(wopQueryStoreReferrers, to);
|
||||
writeString(path, to);
|
||||
writeBigUnsignedInt(revision, to);
|
||||
processStderr();
|
||||
PathSet referrers2 = readStorePaths(from);
|
||||
referrers.insert(referrers2.begin(), referrers2.end());
|
||||
|
||||
}
|
||||
|
||||
void RemoteStore::queryStateReferrers(const Path & path,
|
||||
PathSet & stateReferrers, const unsigned int revision)
|
||||
{
|
||||
writeInt(wopQueryStateReferrers, to);
|
||||
writeString(path, to);
|
||||
writeBigUnsignedInt(revision, to);
|
||||
processStderr();
|
||||
PathSet stateReferrers2 = readStatePaths(from);
|
||||
stateReferrers.insert(stateReferrers2.begin(), stateReferrers2.end());
|
||||
}
|
||||
|
||||
Path RemoteStore::queryDeriver(const Path & path)
|
||||
{
|
||||
writeInt(wopQueryDeriver, to);
|
||||
writeString(path, to);
|
||||
processStderr();
|
||||
return readStorePath(from);
|
||||
Path drvPath = readString(from);
|
||||
if (drvPath != "") assertStorePath(drvPath);
|
||||
return drvPath;
|
||||
}
|
||||
|
||||
|
||||
@@ -263,7 +355,6 @@ Path RemoteStore::addTextToStore(const string & suffix, const string & s,
|
||||
writeString(suffix, to);
|
||||
writeString(s, to);
|
||||
writeStringSet(references, to);
|
||||
|
||||
processStderr();
|
||||
return readStorePath(from);
|
||||
}
|
||||
@@ -370,6 +461,187 @@ void RemoteStore::collectGarbage(GCAction action, const PathSet & pathsToDelete,
|
||||
bytesFreed = (((unsigned long long) hi) << 32) | lo;
|
||||
}
|
||||
|
||||
PathSet RemoteStore::queryDerivers(const Path & storePath)
|
||||
{
|
||||
writeInt(wopQueryDerivers, to);
|
||||
writeString(storePath, to);
|
||||
processStderr();
|
||||
return readStorePaths(from);
|
||||
}
|
||||
|
||||
void RemoteStore::setStatePathsInterval(const Path & statePath, const CommitIntervals & intervals)
|
||||
{
|
||||
writeInt(wopSetStatePathsInterval, to);
|
||||
writeString(statePath, to);
|
||||
writeCommitIntervals(intervals, to);
|
||||
processStderr();
|
||||
readInt(from);
|
||||
}
|
||||
|
||||
CommitIntervals RemoteStore::getStatePathsInterval(const Path & statePath)
|
||||
{
|
||||
writeInt(wopGetStatePathsInterval, to);
|
||||
writeString(statePath, to);
|
||||
processStderr();
|
||||
return readCommitIntervals(from);
|
||||
}
|
||||
|
||||
bool RemoteStore::isStateComponent(const Path & path)
|
||||
{
|
||||
writeInt(wopIsStateComponent, to);
|
||||
writeString(path, to);
|
||||
processStderr();
|
||||
unsigned int reply = readInt(from);
|
||||
return reply != 0;
|
||||
}
|
||||
|
||||
void RemoteStore::storePathRequisites(const Path & storeOrstatePath, const bool includeOutputs, PathSet & paths, const bool withComponents, const bool withState, const unsigned int revision)
|
||||
{
|
||||
writeInt(wopStorePathRequisites, to);
|
||||
writeString(storeOrstatePath, to);
|
||||
writeInt(includeOutputs ? 1 : 0, to);
|
||||
writeStringSet(paths, to);
|
||||
writeInt(withComponents ? 1 : 0, to);
|
||||
writeInt(withState ? 1 : 0, to);
|
||||
writeBigUnsignedInt(revision, to);
|
||||
processStderr();
|
||||
readInt(from);
|
||||
}
|
||||
|
||||
void RemoteStore::setStateRevisions(const RevisionClosure & revisions, const Path & rootStatePath, const string & comment)
|
||||
{
|
||||
writeInt(wopSetStateRevisions, to);
|
||||
writeRevisionClosure(revisions, to);
|
||||
writeString(rootStatePath, to);
|
||||
writeString(comment, to);
|
||||
processStderr();
|
||||
readInt(from);
|
||||
}
|
||||
|
||||
bool RemoteStore::queryStateRevisions(const Path & statePath, RevisionClosure & revisions, RevisionClosureTS & timestamps, const unsigned int revision)
|
||||
{
|
||||
writeInt(wopQueryStateRevisions, to);
|
||||
writeString(statePath, to);
|
||||
writeBigUnsignedInt(revision, to);
|
||||
processStderr();
|
||||
RevisionClosure revisions2 = readRevisionClosure(from);
|
||||
RevisionClosureTS timestamps2 = readRevisionClosureTS(from);
|
||||
revisions = revisions2;
|
||||
timestamps = timestamps2; //TODO Is this ok?? Maybe COPY BY VALUE ??
|
||||
unsigned int reply = readInt(from);
|
||||
return reply != 0;
|
||||
}
|
||||
|
||||
bool RemoteStore::queryAvailableStateRevisions(const Path & statePath, RevisionInfos & revisions)
|
||||
{
|
||||
writeInt(wopQueryAvailableStateRevisions, to);
|
||||
writeString(statePath, to);
|
||||
processStderr();
|
||||
RevisionInfos revisions2 = readRevisionInfos(from);
|
||||
revisions = revisions2;
|
||||
unsigned int reply = readInt(from);
|
||||
return reply != 0;
|
||||
}
|
||||
|
||||
Snapshots RemoteStore::commitStatePath(const Path & statePath)
|
||||
{
|
||||
writeInt(wopCommitStatePath, to);
|
||||
writeString(statePath, to);
|
||||
processStderr();
|
||||
return readSnapshots(from);
|
||||
}
|
||||
|
||||
void RemoteStore::scanAndUpdateAllReferences(const Path & statePath, const bool recursive)
|
||||
{
|
||||
writeInt(wopScanAndUpdateAllReferences, to);
|
||||
writeString(statePath, to);
|
||||
writeInt(recursive ? 1 : 0, to);
|
||||
processStderr();
|
||||
readInt(from);
|
||||
}
|
||||
|
||||
bool RemoteStore::getSharedWith(const Path & statePath1, Path & statePath2)
|
||||
{
|
||||
writeInt(wopGetSharedWith, to);
|
||||
writeString(statePath1, to);
|
||||
processStderr();
|
||||
statePath2 = readString(from);
|
||||
unsigned int reply = readInt(from);
|
||||
return reply != 0;
|
||||
}
|
||||
|
||||
PathSet RemoteStore::toNonSharedPathSet(const PathSet & statePaths)
|
||||
{
|
||||
writeInt(wopToNonSharedPathSet, to);
|
||||
writeStringSet(statePaths, to);
|
||||
processStderr();
|
||||
return readStatePaths(from);
|
||||
}
|
||||
|
||||
void RemoteStore::revertToRevision(const Path & statePath, const unsigned int revision_arg, const bool recursive)
|
||||
{
|
||||
writeInt(wopRevertToRevision, to);
|
||||
writeString(statePath, to);
|
||||
writeBigUnsignedInt(revision_arg, to);
|
||||
writeInt(recursive ? 1 : 0, to);
|
||||
processStderr();
|
||||
readInt(from);
|
||||
}
|
||||
|
||||
void RemoteStore::shareState(const Path & from_arg, const Path & to_arg, const bool snapshot)
|
||||
{
|
||||
writeInt(wopShareState, to);
|
||||
writeString(from_arg, to);
|
||||
writeString(to_arg, to);
|
||||
writeInt(snapshot ? 1 : 0, to);
|
||||
processStderr();
|
||||
readInt(from);
|
||||
}
|
||||
|
||||
void RemoteStore::unShareState(const Path & path, const bool branch, const bool restoreOld)
|
||||
{
|
||||
writeInt(wopUnShareState, to);
|
||||
writeString(path, to);
|
||||
writeInt(branch ? 1 : 0, to);
|
||||
writeInt(restoreOld ? 1 : 0, to);
|
||||
processStderr();
|
||||
readInt(from);
|
||||
}
|
||||
|
||||
Path RemoteStore::lookupStatePath(const Path & storePath, const string & identifier, const string & user)
|
||||
{
|
||||
writeInt(wopLookupStatePath, to);
|
||||
writeString(storePath, to);
|
||||
writeString(identifier, to);
|
||||
writeString(user, to);
|
||||
processStderr();
|
||||
return readStatePath(from);
|
||||
}
|
||||
|
||||
void RemoteStore::setStateOptions(const Path & statePath, const string & user, const string & group, int chmod, const string & runtimeArgs)
|
||||
{
|
||||
writeInt(wopSetStateOptions, to);
|
||||
writeString(statePath, to);
|
||||
writeString(user, to);
|
||||
writeString(group, to);
|
||||
writeInt(chmod, to);
|
||||
writeString(runtimeArgs, to);
|
||||
processStderr();
|
||||
readInt(from);
|
||||
}
|
||||
|
||||
void RemoteStore::getStateOptions(const Path & statePath, string & user, string & group, int & chmod, string & runtimeArgs)
|
||||
{
|
||||
writeInt(wopGetStateOptions, to);
|
||||
writeString(statePath, to);
|
||||
processStderr();
|
||||
user = readString(from);
|
||||
group = readString(from);
|
||||
chmod = readInt(from);
|
||||
runtimeArgs = readString(from);
|
||||
readInt(from);
|
||||
}
|
||||
|
||||
|
||||
void RemoteStore::processStderr(Sink * sink, Source * source)
|
||||
{
|
||||
|
||||
@@ -26,12 +26,24 @@ public:
|
||||
/* Implementations of abstract store API methods. */
|
||||
|
||||
bool isValidPath(const Path & path);
|
||||
|
||||
bool isValidStatePath(const Path & path);
|
||||
|
||||
bool isValidComponentOrStatePath(const Path & path);
|
||||
|
||||
PathSet queryValidPaths();
|
||||
|
||||
Hash queryPathHash(const Path & path);
|
||||
|
||||
Path queryStatePathDrv(const Path & statePath);
|
||||
|
||||
void queryReferences(const Path & path, PathSet & references);
|
||||
void queryStoreReferences(const Path & path, PathSet & references, const unsigned int revision);
|
||||
|
||||
void queryReferrers(const Path & path, PathSet & referrers);
|
||||
void queryStateReferences(const Path & storePath, PathSet & stateReferences, const unsigned int revision);
|
||||
|
||||
void queryStoreReferrers(const Path & path, PathSet & referrers, const unsigned int revision);
|
||||
|
||||
void queryStateReferrers(const Path & path, PathSet & stateReferrers, const unsigned int revision);
|
||||
|
||||
Path queryDeriver(const Path & path);
|
||||
|
||||
@@ -65,12 +77,49 @@ public:
|
||||
|
||||
void collectGarbage(GCAction action, const PathSet & pathsToDelete,
|
||||
bool ignoreLiveness, PathSet & result, unsigned long long & bytesFreed);
|
||||
|
||||
|
||||
void setStatePathsInterval(const Path & statePath, const CommitIntervals & intervals);
|
||||
|
||||
CommitIntervals getStatePathsInterval(const Path & statePath);
|
||||
|
||||
bool isStateComponent(const Path & path);
|
||||
|
||||
void storePathRequisites(const Path & storeOrstatePath, const bool includeOutputs, PathSet & paths, const bool withComponents, const bool withState, const unsigned int revision);
|
||||
|
||||
void setStateRevisions(const RevisionClosure & revisions, const Path & rootStatePath, const string & comment);
|
||||
|
||||
bool queryStateRevisions(const Path & statePath, RevisionClosure & revisions, RevisionClosureTS & timestamps, const unsigned int revision);
|
||||
|
||||
bool queryAvailableStateRevisions(const Path & statePath, RevisionInfos & revisions);
|
||||
|
||||
Snapshots commitStatePath(const Path & statePath);
|
||||
|
||||
PathSet queryDerivers(const Path & storePath);
|
||||
|
||||
void scanAndUpdateAllReferences(const Path & statePath, const bool recursive);
|
||||
|
||||
bool getSharedWith(const Path & statePath1, Path & statePath2);
|
||||
|
||||
PathSet toNonSharedPathSet(const PathSet & statePaths);
|
||||
|
||||
void revertToRevision(const Path & statePath, const unsigned int revision_arg, const bool recursive);
|
||||
|
||||
void shareState(const Path & from, const Path & to, const bool snapshot);
|
||||
|
||||
void unShareState(const Path & path, const bool branch, const bool restoreOld);
|
||||
|
||||
Path lookupStatePath(const Path & storePath, const string & identifier, const string & user);
|
||||
|
||||
void setStateOptions(const Path & statePath, const string & user, const string & group, int chmod, const string & runtimeArgs);
|
||||
|
||||
void getStateOptions(const Path & statePath, string & user, string & group, int & chmod, string & runtimeArgs);
|
||||
|
||||
private:
|
||||
AutoCloseFD fdSocket;
|
||||
FdSink to;
|
||||
FdSource from;
|
||||
Pid child;
|
||||
unsigned int daemonVersion;
|
||||
|
||||
void processStderr(Sink * sink = 0, Source * source = 0);
|
||||
|
||||
|
||||
@@ -21,6 +21,13 @@ bool isInStore(const Path & path)
|
||||
&& path[nixStore.size()] == '/';
|
||||
}
|
||||
|
||||
bool isInStateStore(const Path & path)
|
||||
{
|
||||
return path[0] == '/'
|
||||
&& string(path, 0, nixStoreState.size()) == nixStoreState
|
||||
&& path.size() >= nixStoreState.size() + 2
|
||||
&& path[nixStoreState.size()] == '/';
|
||||
}
|
||||
|
||||
bool isStorePath(const Path & path)
|
||||
{
|
||||
@@ -28,18 +35,36 @@ bool isStorePath(const Path & path)
|
||||
&& path.find('/', nixStore.size() + 1) == Path::npos;
|
||||
}
|
||||
|
||||
bool isStatePath(const Path & path)
|
||||
{
|
||||
return isInStateStore(path)
|
||||
&& path.find('/', nixStoreState.size() + 1) == Path::npos;
|
||||
}
|
||||
|
||||
void assertStorePath(const Path & path)
|
||||
{
|
||||
if (!isStorePath(path))
|
||||
throw Error(format("path `%1%' is not in the Nix store") % path);
|
||||
throw Error(format("component path `%1%' is not in the Nix store") % path);
|
||||
}
|
||||
|
||||
void assertStatePath(const Path & path)
|
||||
{
|
||||
if (!isStatePath(path))
|
||||
throw Error(format("state path `%1%' is not in the Nix state-store") % path);
|
||||
}
|
||||
|
||||
void assertStoreOrStatePath(const Path & path)
|
||||
{
|
||||
if (!isStorePath(path) && !isStatePath(path))
|
||||
throw Error(format("path `%1%' is not a store or state path") % path);
|
||||
}
|
||||
|
||||
|
||||
|
||||
Path toStorePath(const Path & path)
|
||||
{
|
||||
if (!isInStore(path))
|
||||
throw Error(format("path `%1%' is not in the Nix store") % path);
|
||||
throw Error(format("path `%1%' is not in the Nix store (2)") % path);
|
||||
Path::size_type slash = path.find('/', nixStore.size() + 1);
|
||||
if (slash == Path::npos)
|
||||
return path;
|
||||
@@ -47,6 +72,45 @@ Path toStorePath(const Path & path)
|
||||
return Path(path, 0, slash);
|
||||
}
|
||||
|
||||
Path toStoreOrStatePath(const Path & path)
|
||||
{
|
||||
bool isStorePath = isInStore(path);
|
||||
bool isStateStore = isInStateStore(path);
|
||||
|
||||
if (!isStorePath && !isStateStore)
|
||||
throw Error(format("path `%1%' is not in the Nix store or Nix state store") % path);
|
||||
|
||||
Path::size_type slash;
|
||||
if(isStorePath)
|
||||
slash = path.find('/', nixStore.size() + 1);
|
||||
else
|
||||
slash = path.find('/', nixStoreState.size() + 1);
|
||||
|
||||
if (slash == Path::npos)
|
||||
return path;
|
||||
else
|
||||
return Path(path, 0, slash);
|
||||
}
|
||||
|
||||
Path followLinksToStore(const Path & _path)
|
||||
{
|
||||
Path path = absPath(_path);
|
||||
while (!isInStore(path)) {
|
||||
if (!isLink(path)) break;
|
||||
string target = readLink(path);
|
||||
path = absPath(target, dirOf(path));
|
||||
}
|
||||
if (!isInStore(path))
|
||||
throw Error(format("path `%1%' is not in the Nix store") % path);
|
||||
return path;
|
||||
}
|
||||
|
||||
|
||||
Path followLinksToStorePath(const Path & path)
|
||||
{
|
||||
return toStorePath(followLinksToStore(path));
|
||||
}
|
||||
|
||||
|
||||
void checkStoreName(const string & name)
|
||||
{
|
||||
@@ -67,8 +131,7 @@ void checkStoreName(const string & name)
|
||||
}
|
||||
|
||||
|
||||
Path makeStorePath(const string & type,
|
||||
const Hash & hash, const string & suffix)
|
||||
Path makeStorePath(const string & type, const Hash & hash, const string & suffix)
|
||||
{
|
||||
/* e.g., "source:sha256:1abc...:/nix/store:foo.tar.gz" */
|
||||
string s = type + ":sha256:" + printHash(hash) + ":"
|
||||
@@ -77,10 +140,50 @@ Path makeStorePath(const string & type,
|
||||
checkStoreName(suffix);
|
||||
|
||||
return nixStore + "/"
|
||||
+ printHash32(compressHash(hashString(htSHA256, s), 20))
|
||||
+ printHash32(compressHash(hashString(htSHA256, s), 20)) //TODO maybe also add a suffix_stateIdentifier when: there is state & no runtime state args & ... ?
|
||||
+ "-" + suffix;
|
||||
}
|
||||
|
||||
Path makeStatePath(const string & componentHash, const string & suffix, const string & stateIdentifier, const string & username)
|
||||
{
|
||||
string suffix_stateIdentifier = stateIdentifier;
|
||||
suffix_stateIdentifier = "-" + suffix_stateIdentifier;
|
||||
|
||||
/* e.g., "source:sha256:1abc...:/nix/store:foo.tar.gz" */
|
||||
string s = ":sha256:" + componentHash + ":"
|
||||
+ nixStoreState + ":" + suffix + ":" + stateIdentifier + ":" + username;
|
||||
|
||||
checkStoreName(suffix);
|
||||
checkStoreName(stateIdentifier);
|
||||
|
||||
return nixStoreState + "/"
|
||||
+ printHash32(compressHash(hashString(htSHA256, s), 20))
|
||||
+ "-" + suffix + suffix_stateIdentifier;
|
||||
}
|
||||
|
||||
void checkStatePath(const Derivation & drv)
|
||||
{
|
||||
Path drvPath = drv.stateOutputs.find("state")->second.statepath;
|
||||
|
||||
string componentHash = drv.stateOutputs.find("state")->second.componentHash;
|
||||
string suffix = drv.env.find("name")->second;
|
||||
string stateIdentifier = drv.stateOutputs.find("state")->second.stateIdentifier;
|
||||
string drvUser = drv.stateOutputs.find("state")->second.username;
|
||||
|
||||
string callingUser = queryCallingUsername();
|
||||
|
||||
//Check name (TODO how about sharing of drvs between users?) (user is filled in on the fly)
|
||||
if(drvUser != callingUser)
|
||||
throw Error(format("The calling user does not match the user specified in the drv '%1%'") % drvPath);
|
||||
|
||||
Path calculatedPath = makeStatePath(componentHash, suffix, stateIdentifier, callingUser);
|
||||
|
||||
printMsg(lvlError, format("Checking statePath validity: %1% %2%") % drvPath % calculatedPath);
|
||||
|
||||
//Check Calculated path
|
||||
if(drvPath != calculatedPath)
|
||||
throw Error(format("The statepath from the Derivation does not match the recalculated statepath, are u trying to spoof the statepath?"));
|
||||
}
|
||||
|
||||
Path makeFixedOutputPath(bool recursive,
|
||||
string hashAlgo, Hash hash, string name)
|
||||
@@ -127,24 +230,86 @@ Path computeStorePathForText(const string & suffix, const string & s,
|
||||
type += ":";
|
||||
type += *i;
|
||||
}
|
||||
|
||||
return makeStorePath(type, hash, suffix);
|
||||
}
|
||||
|
||||
/* Return a string accepted by decodeValidPathInfo() that
|
||||
registers the specified paths as valid. Note: it's the
|
||||
responsibility of the caller to provide a closure. */
|
||||
string makeValidityRegistration(const PathSet & paths, // TODO !!!!!!!!!!!!!!!!!!!!!! makeValidityRegistration CHECK CALLS FOR STATE PASSINGS?
|
||||
bool showDerivers, bool showHash)
|
||||
{
|
||||
string s = "";
|
||||
|
||||
for (PathSet::iterator i = paths.begin(); i != paths.end(); ++i) {
|
||||
s += *i + "\n";
|
||||
|
||||
ValidPathInfo decodeValidPathInfo(std::istream & str)
|
||||
if (showHash)
|
||||
s += printHash(store->queryPathHash(*i)) + "\n";
|
||||
|
||||
Path deriver = showDerivers ? store->queryDeriver(*i) : "";
|
||||
s += deriver + "\n";
|
||||
|
||||
PathSet references;
|
||||
store->queryStoreReferences(*i, references, 0); //TODO !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! WE MAY NEED STATE???
|
||||
|
||||
s += (format("%1%\n") % references.size()).str();
|
||||
|
||||
for (PathSet::iterator j = references.begin();
|
||||
j != references.end(); ++j)
|
||||
s += *j + "\n";
|
||||
}
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
|
||||
ValidPathInfo decodeValidPathInfo(std::istream & str, bool hashGiven)
|
||||
{
|
||||
ValidPathInfo info;
|
||||
|
||||
getline(str, info.path);
|
||||
if (str.eof()) { info.path = ""; return info; }
|
||||
|
||||
if (hashGiven) {
|
||||
string s;
|
||||
getline(str, s);
|
||||
info.hash = parseHash(htSHA256, s);
|
||||
}
|
||||
|
||||
getline(str, info.deriver);
|
||||
|
||||
string s; int n;
|
||||
|
||||
getline(str, s);
|
||||
if (!string2Int(s, n)) throw Error("number expected");
|
||||
if (!string2Int(s, n))
|
||||
throw Error("number expected");
|
||||
while (n--) {
|
||||
getline(str, s);
|
||||
info.references.insert(s);
|
||||
}
|
||||
if (!str || str.eof()) throw Error("missing input");
|
||||
|
||||
if(store->isStateComponent(info.path)){
|
||||
|
||||
getline(str, s);
|
||||
if (!string2Int(s, n))
|
||||
throw Error("number expected");
|
||||
while (n--) {
|
||||
getline(str, s);
|
||||
info.stateReferences.insert(s);
|
||||
}
|
||||
|
||||
unsigned int u;
|
||||
getline(str, s);
|
||||
if (!string2UnsignedInt(s, u))
|
||||
throw Error("number expected");
|
||||
info.revision = u;
|
||||
}
|
||||
|
||||
if (!str || str.eof())
|
||||
throw Error("missing input");
|
||||
|
||||
return info;
|
||||
}
|
||||
|
||||
|
||||
@@ -8,6 +8,8 @@
|
||||
|
||||
#include "hash.hh"
|
||||
#include "serialise.hh"
|
||||
#include "derivations.hh"
|
||||
#include "db.hh"
|
||||
|
||||
|
||||
namespace nix {
|
||||
@@ -34,19 +36,40 @@ public:
|
||||
|
||||
/* Checks whether a path is valid. */
|
||||
virtual bool isValidPath(const Path & path) = 0;
|
||||
|
||||
/* Checks whether a state-path is valid. */
|
||||
virtual bool isValidStatePath(const Path & path) = 0;
|
||||
|
||||
/* TODO */
|
||||
virtual bool isValidComponentOrStatePath(const Path & path) = 0;
|
||||
|
||||
/* Query the set of valid paths. */
|
||||
virtual PathSet queryValidPaths() = 0;
|
||||
|
||||
/* Queries the hash of a valid path. */
|
||||
virtual Hash queryPathHash(const Path & path) = 0;
|
||||
|
||||
/* Queries the derivation Path of a valid state path. */
|
||||
virtual Path queryStatePathDrv(const Path & statePath) = 0;
|
||||
|
||||
/* Queries the set of outgoing FS references for a store path.
|
||||
The result is not cleared. */
|
||||
virtual void queryReferences(const Path & path,
|
||||
PathSet & references) = 0;
|
||||
virtual void queryStoreReferences(const Path & path,
|
||||
PathSet & references, const unsigned int revision) = 0;
|
||||
|
||||
/* Queries the set of outgoing FS state-references for a store path.
|
||||
The result is not cleared. */
|
||||
virtual void queryStateReferences(const Path & path,
|
||||
PathSet & stateReferences, const unsigned int revision) = 0;
|
||||
|
||||
/* Queries the set of incoming FS references for a store path.
|
||||
The result is not cleared. */
|
||||
virtual void queryReferrers(const Path & path,
|
||||
PathSet & referrers) = 0;
|
||||
virtual void queryStoreReferrers(const Path & path,
|
||||
PathSet & referrers, const unsigned int revision) = 0;
|
||||
|
||||
/* Queries the set of incoming FS state-references for a store path.
|
||||
The result is not cleared. */
|
||||
virtual void queryStateReferrers(const Path & path, PathSet & stateReferrers, const unsigned int revision) = 0;
|
||||
|
||||
/* Query the deriver of a store path. Return the empty string if
|
||||
no deriver has been set. */
|
||||
@@ -161,6 +184,65 @@ public:
|
||||
`bytesFreed'. */
|
||||
virtual void collectGarbage(GCAction action, const PathSet & pathsToDelete,
|
||||
bool ignoreLiveness, PathSet & result, unsigned long long & bytesFreed) = 0;
|
||||
|
||||
/* TODO */
|
||||
virtual void setStatePathsInterval(const Path & statePath, const CommitIntervals & intervals) = 0;
|
||||
|
||||
/* TODO */
|
||||
virtual CommitIntervals getStatePathsInterval(const Path & statePath) = 0;
|
||||
|
||||
/* Checks whether a path is a component path that has a statePath. */
|
||||
virtual bool isStateComponent(const Path & path) = 0;
|
||||
|
||||
/* TODO */
|
||||
virtual void storePathRequisites(const Path & storeOrstatePath, const bool includeOutputs, PathSet & paths, const bool withComponents, const bool withState, const unsigned int revision) = 0;
|
||||
|
||||
/* TODO */
|
||||
virtual void setStateRevisions(const RevisionClosure & revisions, const Path & rootStatePath, const string & comment) = 0;
|
||||
|
||||
/*
|
||||
* TODO Not sure wheter this comment is 100% correct
|
||||
* Querys all available state revision closures.
|
||||
* (e.g. a references scan can change this)
|
||||
*/
|
||||
virtual bool queryStateRevisions(const Path & statePath, RevisionClosure & revisions, RevisionClosureTS & timestamps, const unsigned int revision) = 0;
|
||||
|
||||
/* TODO */
|
||||
virtual bool queryAvailableStateRevisions(const Path & statePath, RevisionInfos & revisions) = 0;
|
||||
|
||||
/* TODO */
|
||||
virtual Snapshots commitStatePath(const Path & statePath) = 0;
|
||||
|
||||
/* TODO */
|
||||
virtual PathSet queryDerivers(const Path & storePath) = 0;
|
||||
|
||||
/* TODO */
|
||||
virtual void scanAndUpdateAllReferences(const Path & statePath, const bool recursive) = 0;
|
||||
|
||||
/* TODO */
|
||||
virtual bool getSharedWith(const Path & statePath1, Path & statePath2) = 0;
|
||||
|
||||
/* TODO */
|
||||
virtual PathSet toNonSharedPathSet(const PathSet & statePaths) = 0;
|
||||
|
||||
/* TODO */
|
||||
virtual void revertToRevision(const Path & statePath, const unsigned int revision_arg, const bool recursive) = 0;
|
||||
|
||||
/* TODO */
|
||||
virtual void shareState(const Path & from, const Path & to, const bool snapshot) = 0;
|
||||
|
||||
/* TODO */
|
||||
virtual void unShareState(const Path & path, const bool branch, const bool restoreOld) = 0;
|
||||
|
||||
/* TODO */
|
||||
virtual Path lookupStatePath(const Path & storePath, const string & identifier, const string & user) = 0;
|
||||
|
||||
/* TODO */
|
||||
virtual void setStateOptions(const Path & statePath, const string & user, const string & group, int chmod, const string & runtimeArgs) = 0;
|
||||
|
||||
/* TODO */
|
||||
virtual void getStateOptions(const Path & statePath, string & user, string & group, int & chmod, string & runtimeArgs) = 0;
|
||||
|
||||
};
|
||||
|
||||
|
||||
@@ -168,15 +250,31 @@ public:
|
||||
|
||||
/* Throw an exception if `path' is not directly in the Nix store. */
|
||||
void assertStorePath(const Path & path);
|
||||
void assertStatePath(const Path & path);
|
||||
void assertStoreOrStatePath(const Path & path);
|
||||
|
||||
bool isInStore(const Path & path);
|
||||
bool isStorePath(const Path & path);
|
||||
|
||||
bool isInStateStore(const Path & path);
|
||||
bool isStatePath(const Path & path);
|
||||
|
||||
|
||||
void checkStoreName(const string & name);
|
||||
|
||||
|
||||
/* Chop off the parts after the top-level store name, e.g.,
|
||||
/nix/store/abcd-foo/bar => /nix/store/abcd-foo. */
|
||||
Path toStorePath(const Path & path);
|
||||
Path toStoreOrStatePath(const Path & path);
|
||||
|
||||
/* Follow symlinks until we end up with a path in the Nix store. */
|
||||
Path followLinksToStore(const Path & path);
|
||||
|
||||
|
||||
/* Same as followLinksToStore(), but apply toStorePath() to the
|
||||
result. */
|
||||
Path followLinksToStorePath(const Path & path);
|
||||
|
||||
|
||||
/* Constructs a unique store path name. */
|
||||
@@ -186,6 +284,11 @@ Path makeStorePath(const string & type,
|
||||
Path makeFixedOutputPath(bool recursive,
|
||||
string hashAlgo, Hash hash, string name);
|
||||
|
||||
/* TODO ... */
|
||||
Path makeStatePath(const string & componentHash, const string & suffix, const string & stateIdentifier, const string & user);
|
||||
|
||||
/* TODO ... */
|
||||
void checkStatePath(const Derivation & drv);
|
||||
|
||||
/* This is the preparatory part of addToStore() and addToStoreFixed();
|
||||
it computes the store path to which srcPath is to be copied.
|
||||
@@ -239,6 +342,11 @@ extern boost::shared_ptr<StoreAPI> store;
|
||||
boost::shared_ptr<StoreAPI> openStore(bool reserveSpace = true);
|
||||
|
||||
|
||||
string makeValidityRegistration(const PathSet & paths,
|
||||
bool showDerivers, bool showHash);
|
||||
|
||||
|
||||
/* OLD TODO REMOVE
|
||||
struct ValidPathInfo
|
||||
{
|
||||
Path path;
|
||||
@@ -246,8 +354,19 @@ struct ValidPathInfo
|
||||
Hash hash;
|
||||
PathSet references;
|
||||
};
|
||||
*/
|
||||
struct ValidPathInfo
|
||||
{
|
||||
Path path;
|
||||
Path deriver;
|
||||
Hash hash;
|
||||
PathSet references;
|
||||
PathSet stateReferences;
|
||||
int unsigned revision;
|
||||
};
|
||||
|
||||
ValidPathInfo decodeValidPathInfo(std::istream & str);
|
||||
ValidPathInfo decodeValidPathInfo(std::istream & str,
|
||||
bool hashGiven = false);
|
||||
|
||||
|
||||
}
|
||||
|
||||
845
src/libstore/store-state.cc
Normal file
845
src/libstore/store-state.cc
Normal file
@@ -0,0 +1,845 @@
|
||||
#include <iostream>
|
||||
#include <cstdio>
|
||||
#include <unistd.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/wait.h>
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
|
||||
#include "store-state.hh"
|
||||
#include "globals.hh"
|
||||
#include "util.hh"
|
||||
#include "derivations.hh"
|
||||
#include "store-api.hh"
|
||||
#include "local-store.hh"
|
||||
#include "misc.hh"
|
||||
#include "archive.hh"
|
||||
#include "snapshot.hh"
|
||||
#include "references.hh"
|
||||
|
||||
//for nixDB
|
||||
|
||||
namespace nix {
|
||||
|
||||
|
||||
/*
|
||||
* This gets called before a stateDir might be valid, so we get the data
|
||||
* from the drv and not from the database
|
||||
*/
|
||||
void createSubStateDirsTxn(const Transaction & txn, const DerivationStateOutputDirs & stateOutputDirs, const DerivationStateOutputs & stateOutputs)
|
||||
{
|
||||
Path statePath = stateOutputs.find("state")->second.statepath;
|
||||
string stateDir = statePath;
|
||||
|
||||
PathSet intervalPaths;
|
||||
|
||||
for (DerivationStateOutputDirs::const_reverse_iterator i = stateOutputDirs.rbegin(); i != stateOutputDirs.rend(); ++i){
|
||||
DerivationStateOutputDir d = i->second;
|
||||
|
||||
string thisdir = d.path;
|
||||
Path fullstatedir = stateDir + "/" + thisdir;
|
||||
|
||||
//If it is a file: continue
|
||||
if(thisdir.substr(thisdir.length() -1 , thisdir.length()) != "/"){
|
||||
continue;
|
||||
}
|
||||
|
||||
ensureDirExists(fullstatedir);
|
||||
|
||||
if(d.type == "interval"){
|
||||
intervalPaths.insert(fullstatedir);
|
||||
}
|
||||
}
|
||||
|
||||
setChown(statePath, queryCallingUsername(), "nixbld", true); //Set all dirs in the statePath recursively to their owners
|
||||
printMsg(lvlTalkative, format("Set CHOWN '%1%'") % (statePath + "-" + queryCallingUsername()));
|
||||
}
|
||||
|
||||
/*
|
||||
* TODO
|
||||
*/
|
||||
void updatePaths()
|
||||
{
|
||||
//set in db
|
||||
|
||||
//update setStatePathsIntervalTxn(txn, intervalPaths, empty, true);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Input: store (or statePath?)
|
||||
* Returns all the drv's of the statePaths (in)directly referenced.
|
||||
*/
|
||||
//TODO TXN
|
||||
PathSet getAllStateDerivationsRecursivelyTxn(const Transaction & txn, const Path & storePath, const int revision)
|
||||
{
|
||||
//Get recursively all state paths
|
||||
PathSet statePaths;
|
||||
storePathRequisitesTxn(noTxn, storePath, false, statePaths, false, true, revision);
|
||||
|
||||
//Find the matching drv with the statePath
|
||||
PathSet derivations;
|
||||
for (PathSet::iterator i = statePaths.begin(); i != statePaths.end(); ++i)
|
||||
derivations.insert(queryStatePathDrvTxn(txn,*i));
|
||||
|
||||
return derivations;
|
||||
}
|
||||
|
||||
|
||||
|
||||
void revertToRevisionTxn(const Transaction & txn, const Path & statePath, const int revision_arg, const bool recursive)
|
||||
{
|
||||
//Unshare the path
|
||||
Path statePath_ns = toNonSharedPathTxn(txn, statePath);
|
||||
|
||||
//get a new timestamp for the references update
|
||||
unsigned int newTimestamp = getTimeStamp();
|
||||
|
||||
//Get the revisions recursively to also roll them back
|
||||
RevisionClosure getRivisions;
|
||||
RevisionClosureTS getTimestamps;
|
||||
queryStateRevisionsTxn(txn, statePath_ns, getRivisions, getTimestamps, revision_arg);
|
||||
|
||||
|
||||
//If not recursive: filter out all paths execpt statePath_ns
|
||||
if(!recursive)
|
||||
{
|
||||
Snapshots currentSS = getRivisions[statePath_ns]; //save SS of statePath_ns
|
||||
getRivisions.clear(); //clear all
|
||||
getRivisions[statePath_ns] = currentSS; //insert
|
||||
|
||||
unsigned int currentTS = getTimestamps[statePath_ns]; //same as above
|
||||
getTimestamps.clear();
|
||||
getTimestamps[statePath_ns] = currentTS;
|
||||
}
|
||||
|
||||
|
||||
//Revert each statePath in the list
|
||||
for (RevisionClosure::iterator i = getRivisions.begin(); i != getRivisions.end(); ++i){
|
||||
Path statePath = (*i).first;
|
||||
Snapshots revisioned_paths = (*i).second;
|
||||
unsigned int timestamp = getTimestamps[statePath];
|
||||
|
||||
for (Snapshots::iterator j = revisioned_paths.begin(); j != revisioned_paths.end(); ++j){
|
||||
Path revertPathOrFile = (*j).first;
|
||||
unsigned int epoch = (*j).second;
|
||||
|
||||
if(epoch == 0) //Path was deleted so were done
|
||||
continue;
|
||||
|
||||
printMsg(lvlError, format("Reverting '%1%@%2%'") % revertPathOrFile % unsignedInt2String(epoch));
|
||||
|
||||
//Dir: Remove a slash at the end, we create /nix/state/...../cachedir@1234/
|
||||
//File: Or create /nix/state/..../logfile.txt@1234
|
||||
string revertPathOrFile_e;
|
||||
if(revertPathOrFile.substr(revertPathOrFile.length() -1 , revertPathOrFile.length()) == "/")
|
||||
revertPathOrFile_e = revertPathOrFile.substr(0 , revertPathOrFile.length() -1) + "@" + unsignedInt2String(epoch) + "/";
|
||||
else
|
||||
revertPathOrFile_e = revertPathOrFile + "@" + unsignedInt2String(epoch);
|
||||
|
||||
rsyncPaths(revertPathOrFile_e, revertPathOrFile, false);
|
||||
}
|
||||
|
||||
|
||||
//*** Now also revert state _references_ to the specific revision (the revision is already converted to a timestamp here)
|
||||
//Query the references of the old revision (already converted to a timestamp)
|
||||
PathSet state_references;
|
||||
queryXReferencesTxn(txn, statePath, state_references, true, 0, timestamp);
|
||||
PathSet state_stateReferences;
|
||||
queryXReferencesTxn(txn, statePath, state_stateReferences, false, 0, timestamp);
|
||||
//Now set these old references as the new references at the new (just created) Timestamp
|
||||
setStateComponentReferencesTxn(txn, statePath, Strings(state_references.begin(), state_references.end()), 0, newTimestamp);
|
||||
setStateStateReferencesTxn(txn, statePath, Strings(state_stateReferences.begin(), state_stateReferences.end()), 0, newTimestamp);
|
||||
|
||||
//Now set these old state-items as the new state-items at the new (just created) Timestamp
|
||||
StateInfos infos;
|
||||
getVersionedStateEntriesTxn(txn, statePath, infos, 0, timestamp);
|
||||
setVersionedStateEntriesTxn(txn, statePath, infos, 0, newTimestamp);
|
||||
|
||||
//Now set these old staterights as the new staterights at the new (just created) Timestamp
|
||||
//void getStateOptionsTxn(const Transaction & txn, const Path & statePath, string & user, string & group, int & chmod);
|
||||
//void setStateOptionsTxn(const Transaction & txn, const Path & statePath, const string & user, const string & group, int chmod);
|
||||
//TODO !!!!!!!!!??
|
||||
|
||||
if(revision_arg == 0)
|
||||
printMsg(lvlError, format("Reverted state of '%1%' to the latest revision") % statePath); //TODO lookup the number
|
||||
else
|
||||
printMsg(lvlError, format("Reverted state of '%1%' to revision '%2%'") % statePath % revision_arg);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
Snapshots commitStatePathTxn(const Transaction & txn, const Path & statePath)
|
||||
{
|
||||
if(!isValidStatePathTxn(txn, statePath))
|
||||
throw Error(format("path `%1%' is not a valid state path") % statePath);
|
||||
|
||||
printMsg(lvlError, format("Snapshotting statePath: %1%") % statePath);
|
||||
|
||||
//Get all paths from the db
|
||||
StateInfos infos;
|
||||
getVersionedStateEntriesTxn(txn, statePath, infos);
|
||||
|
||||
//Get all interval counters
|
||||
CommitIntervals intervals = getStatePathsIntervalTxn(txn, statePath);
|
||||
|
||||
Snapshots revisions_list;
|
||||
|
||||
for (StateInfos::const_iterator i = infos.begin(); i != infos.end(); ++i){
|
||||
|
||||
string thisdir = (*i).path;
|
||||
string type = (*i).type;
|
||||
unsigned int interval = (*i).interval;
|
||||
|
||||
//printMsg(lvlError, format("maybe ssing %1% %2%") % thisdir % type);
|
||||
|
||||
if(type == "none"){
|
||||
continue;
|
||||
}
|
||||
|
||||
if(type == "interval"){
|
||||
unsigned int interval_counter = intervals[thisdir];
|
||||
|
||||
//update the interval
|
||||
intervals[thisdir] = (interval_counter + 1);
|
||||
|
||||
//We continue if we dont have to commit now
|
||||
if(interval_counter % interval != 0)
|
||||
continue;
|
||||
}
|
||||
else if(type == "full"){ }
|
||||
else if(type == "manual"){ continue; } //TODO !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
|
||||
else
|
||||
throw Error(format("Type '%1%' is not handled in nix-state") % type);
|
||||
|
||||
/////////// We got here so we need to commit
|
||||
|
||||
//We get all info from the orignal path, but we snapshot on the shared path
|
||||
Path sharedWith = toNonSharedPathTxn(txn, statePath);
|
||||
string fullstatedir = sharedWith + "/" + thisdir;
|
||||
if(thisdir == "/") //exception for the root dir
|
||||
fullstatedir = sharedWith + "/";
|
||||
|
||||
unsigned int revision_number;
|
||||
if(pathExists(fullstatedir) || FileExist(fullstatedir)){
|
||||
revision_number = take_snapshot(fullstatedir);
|
||||
printMsg(lvlError, format("Snapshotted '%1%@%2%'") % fullstatedir % revision_number);
|
||||
}
|
||||
else
|
||||
revision_number = 0; //deleted, so we assign 0 to indicate that
|
||||
|
||||
revisions_list[fullstatedir] = revision_number;
|
||||
//printMsg(lvlError, format("FSD %1% RN %2%") % fullstatedir % revision_number);
|
||||
}
|
||||
|
||||
//Update the intervals again
|
||||
setStatePathsIntervalTxn(txn, statePath, intervals);
|
||||
|
||||
return revisions_list;
|
||||
}
|
||||
|
||||
//TODO include this call in the validate function
|
||||
void scanAndUpdateAllReferencesTxn(const Transaction & txn, const Path & statePath
|
||||
, PathSet & newFoundComponentReferences, PathSet & newFoundStateReferences) //only for recursion
|
||||
{
|
||||
//Check if is a state Path
|
||||
if(! isValidStatePathTxn(txn, statePath))
|
||||
throw Error(format("This path '%1%' is not a state path") % statePath);
|
||||
|
||||
//printMsg(lvlError, format("scanAndUpdateAllReferencesTxn: '%1%' - %2%") % statePath % revision);
|
||||
|
||||
//Check if path is not a shared path
|
||||
Path statePath_ns = toNonSharedPathTxn(txn, statePath);
|
||||
|
||||
//get all possible state and component references
|
||||
PathSet allComponentPaths;
|
||||
PathSet allStatePaths;
|
||||
queryAllValidPathsTxn(txn, allComponentPaths, allStatePaths);
|
||||
|
||||
//Remove derivation paths
|
||||
PathSet allComponentPaths2; //without derivations
|
||||
for (PathSet::iterator i = allComponentPaths.begin(); i != allComponentPaths.end(); ++i){
|
||||
string path = *i;
|
||||
if(path.substr(path.length() - 4,path.length()) != drvExtension) //TODO HACK: we should have a typed table or a seperate table .... drvExtension == ".drv"
|
||||
allComponentPaths2.insert(path);
|
||||
}
|
||||
|
||||
//TODO maybe only scan in the changeset (patch) for new references? (this will be difficult and depending on the underlying versioning system)
|
||||
|
||||
//Scan in for (new) component and state references
|
||||
PathSet state_references = scanForReferences(statePath_ns, allComponentPaths2);
|
||||
PathSet state_stateReferences = scanForStateReferences(statePath_ns, allStatePaths);
|
||||
|
||||
//Retrieve old references
|
||||
PathSet old_references;
|
||||
PathSet old_state_references;
|
||||
queryXReferencesTxn(txn, statePath_ns, old_references, true, 0); //get the latsest references
|
||||
queryXReferencesTxn(txn, statePath_ns, old_state_references, false, 0);
|
||||
|
||||
//Check for added and removed paths
|
||||
PathSet diff_references_removed;
|
||||
PathSet diff_references_added;
|
||||
pathSets_difference(state_references, old_references, diff_references_removed, diff_references_added);
|
||||
PathSet diff_state_references_removed;
|
||||
PathSet diff_state_references_added;
|
||||
pathSets_difference(state_stateReferences, old_state_references, diff_state_references_removed, diff_state_references_added);
|
||||
|
||||
//Set PathSet's for the caller of this function
|
||||
newFoundComponentReferences = diff_references_added;
|
||||
newFoundStateReferences = diff_state_references_added;
|
||||
|
||||
//Print error, but we could also throw an error.
|
||||
if(diff_references_added.size() != 0)
|
||||
for (PathSet::iterator i = diff_references_added.begin(); i != diff_references_added.end(); ++i)
|
||||
printMsg(lvlError, format("Added component reference found!: '%1%' in state path '%2%'") % (*i) % statePath_ns);
|
||||
if(diff_references_removed.size() != 0)
|
||||
for (PathSet::iterator i = diff_references_removed.begin(); i != diff_references_removed.end(); ++i)
|
||||
printMsg(lvlError, format("Removed component reference found!: '%1%' in state path '%2%'") % (*i) % statePath_ns);
|
||||
if(diff_state_references_added.size() != 0)
|
||||
for (PathSet::iterator i = diff_state_references_added.begin(); i != diff_state_references_added.end(); ++i)
|
||||
printMsg(lvlError, format("Added state reference found!: '%1%' in state path '%2%'") % (*i) % statePath_ns);
|
||||
if(diff_state_references_removed.size() != 0)
|
||||
for (PathSet::iterator i = diff_state_references_removed.begin(); i != diff_state_references_removed.end(); ++i)
|
||||
printMsg(lvlError, format("Removed state reference found!: '%1%' in state path '%2%'") % (*i) % statePath_ns);
|
||||
|
||||
//We always set the referernces so we know they were scanned (maybe the same) at a certain time
|
||||
printMsg(lvlError, format("Updating new references for statepath: '%1%'") % statePath_ns);
|
||||
Path drvPath = queryStatePathDrvTxn(txn, statePath_ns);
|
||||
registerValidPath(txn,
|
||||
statePath_ns,
|
||||
Hash(), //emtpy hash
|
||||
state_references,
|
||||
state_stateReferences,
|
||||
drvPath,
|
||||
0); //Set at a new timestamp
|
||||
}
|
||||
|
||||
void scanAndUpdateAllReferencesRecusivelyTxn(const Transaction & txn, const Path & statePath)
|
||||
{
|
||||
if(! isValidStatePathTxn(txn, statePath))
|
||||
throw Error(format("This path '%1%' is not a state path") % statePath);
|
||||
|
||||
//get all state current state references recursively
|
||||
PathSet statePaths;
|
||||
storePathRequisitesTxn(txn, statePath, false, statePaths, false, true, 0); //Get all current state dependencies
|
||||
|
||||
//Add own statePath (may already be in there, but its a set, so no doubles)
|
||||
statePaths.insert(statePath);
|
||||
|
||||
//We dont need to sort since the db does that
|
||||
//call scanForAllReferences again on all statePaths
|
||||
for (PathSet::iterator i = statePaths.begin(); i != statePaths.end(); ++i){
|
||||
|
||||
//Scan, update, call recursively
|
||||
PathSet newFoundComponentReferences;
|
||||
PathSet newFoundStateReferences;
|
||||
scanAndUpdateAllReferencesTxn(txn, *i, newFoundComponentReferences, newFoundStateReferences);
|
||||
|
||||
//Call the function recursively again on all newly found references //TODO test if this works
|
||||
PathSet allNewReferences = pathSets_union(newFoundComponentReferences, newFoundStateReferences);
|
||||
for (PathSet::iterator j = allNewReferences.begin(); j != allNewReferences.end(); ++j)
|
||||
scanAndUpdateAllReferencesRecusivelyTxn(txn, *j);
|
||||
}
|
||||
}
|
||||
|
||||
void rsyncPaths(const Path & from, const Path & to, const bool addSlashes) //TODO bool shellexpansion, bool failure for nix-env
|
||||
{
|
||||
//TODO Could be a symlink (to a non-existing dir)
|
||||
/*
|
||||
if(!DirectoryExist(from))
|
||||
throw Error(format("Path `%1%' doenst exist ...") % from);
|
||||
if(!DirectoryExist(to))
|
||||
throw Error(format("Path `%1%' doenst exist ...") % to);
|
||||
*/
|
||||
|
||||
//TODO IF IS FILE: REMOVE THE FILE todo MOVE THIS INTO RSYNCPATHS???
|
||||
//TODO IF IS DIR: REMOVE THE DIR
|
||||
|
||||
|
||||
Path from2 = from;
|
||||
Path to2 = to;
|
||||
if(addSlashes){
|
||||
//We add a slash / to the end to ensure the contents is copyed
|
||||
if(from2[from2.length() - 1] != '/')
|
||||
from2 = from2 + "/";
|
||||
if(to2[to2.length() - 1] != '/')
|
||||
to2 = to2 + "/";
|
||||
}
|
||||
|
||||
printMsg(lvlError, format("Rsync from: '%1%' to: '%2%'") % from2 % to2);
|
||||
|
||||
//Rsync from --> to and also with '-avHx --delete'
|
||||
//This makes the paths completely equal (also deletes) and retains times ownership etc.
|
||||
Strings p_args;
|
||||
p_args.push_back("-avHx");
|
||||
p_args.push_back("--delete");
|
||||
p_args.push_back(from2);
|
||||
p_args.push_back(to2);
|
||||
runProgram_AndPrintOutput(nixRsync, true, p_args, "rsync");
|
||||
}
|
||||
|
||||
// ******************************************************* DB FUNCTIONS ********************************************************************
|
||||
|
||||
/* State specific db functions */
|
||||
|
||||
Path mergeToDBKey(const string & s1, const string & s2)
|
||||
{
|
||||
string prefix = "-KEY-";
|
||||
return s1 + prefix + s2;
|
||||
}
|
||||
Path mergeToDBRevKey(const Path & statePath, const unsigned int intvalue)
|
||||
{
|
||||
return mergeToDBKey(statePath, unsignedInt2String(intvalue));
|
||||
}
|
||||
|
||||
void splitDBKey(const string & s, string & s1, string & s2)
|
||||
{
|
||||
string prefix = "-KEY-";
|
||||
size_t getPos = s.find_last_of(prefix);
|
||||
|
||||
if(getPos == string::npos)
|
||||
throw Error(format("No prefx '%1%' found in '%2%' so we cannot split") % prefix % s);
|
||||
|
||||
int pos = getPos;
|
||||
s1 = s.substr(0, pos - prefix.length() + 1);
|
||||
s2 = s.substr(pos+1, s.length());
|
||||
}
|
||||
void splitDBRevKey(const Path & revisionedStatePath, Path & statePath, unsigned int & intvalue)
|
||||
{
|
||||
string s2;
|
||||
splitDBKey(revisionedStatePath, statePath, s2);
|
||||
|
||||
bool succeed = string2UnsignedInt(s2, intvalue);
|
||||
if(!succeed)
|
||||
throw Error(format("Malformed revision value of path '%1%'") % revisionedStatePath);
|
||||
}
|
||||
|
||||
unsigned int getNewRevisionNumber(Database & nixDB, const Transaction & txn, TableId table,
|
||||
const Path & statePath)
|
||||
{
|
||||
//query
|
||||
string data;
|
||||
bool notEmpty = nixDB.queryString(txn, table, statePath, data);
|
||||
|
||||
if(!notEmpty){
|
||||
nixDB.setString(txn, table, statePath, int2String(1));
|
||||
return 1; //we begin counting from 1 since 0 is a special value representing the last revision
|
||||
}
|
||||
|
||||
unsigned int revision;
|
||||
bool succeed = string2UnsignedInt(data, revision);
|
||||
if(!succeed)
|
||||
throw Error(format("Malformed revision counter value of path '%1%'") % statePath);
|
||||
|
||||
revision++;
|
||||
nixDB.setString(txn, table, statePath, unsignedInt2String(revision));
|
||||
|
||||
return revision;
|
||||
}
|
||||
|
||||
bool lookupHighestRevivison(const Strings & keys, const Path & statePath, string & key, unsigned int lowerthan)
|
||||
{
|
||||
unsigned int highestRev = 0;
|
||||
|
||||
//Lookup which key we need
|
||||
for (Strings::const_iterator i = keys.begin(); i != keys.end(); ++i) {
|
||||
|
||||
if((*i).substr(0, statePath.length()) != statePath || (*i).length() == statePath.length()) //dont check the new-revision key or other keys
|
||||
continue;
|
||||
|
||||
//printMsg(lvlError, format("'%1%' - '%2%'") % *i % statePath);
|
||||
Path getStatePath;
|
||||
unsigned int getRevision;
|
||||
splitDBRevKey(*i, getStatePath, getRevision);
|
||||
if(getRevision > highestRev){
|
||||
|
||||
if(lowerthan != 0){
|
||||
if(getRevision <= lowerthan) //if we have an uppper limit, see to it that we downt go over it
|
||||
highestRev = getRevision;
|
||||
}
|
||||
else
|
||||
highestRev = getRevision;
|
||||
}
|
||||
}
|
||||
|
||||
if(highestRev == 0) //no records found
|
||||
return false;
|
||||
|
||||
key = mergeToDBRevKey(statePath, highestRev); //final key that matches revision + statePath
|
||||
return true;
|
||||
}
|
||||
|
||||
bool revisionToTimeStamp(Database & nixDB, const Transaction & txn, TableId revisions_table, const Path & statePath, const int revision, unsigned int & timestamp)
|
||||
{
|
||||
string key = mergeToDBRevKey(statePath, revision);
|
||||
Strings references;
|
||||
bool notempty = nixDB.queryStrings(txn, revisions_table, key, references);
|
||||
|
||||
if(notempty){
|
||||
Path empty;
|
||||
splitDBRevKey(*(references.begin()), empty, timestamp); //extract the timestamp
|
||||
//printMsg(lvlError, format("PRINT '%1%'") % timestamp);
|
||||
return true;
|
||||
}
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
void setStateReferences(Database & nixDB, const Transaction & txn, TableId references_table, TableId revisions_table,
|
||||
const Path & statePath, const Strings & references, const unsigned int revision, const unsigned int timestamp)
|
||||
{
|
||||
//printMsg(lvlError, format("setStateReferences '%1%' for '%2%'") % references_table % statePath);
|
||||
|
||||
//Find the timestamp if we need
|
||||
unsigned int timestamp2 = timestamp;
|
||||
if(revision == 0 && timestamp == 0)
|
||||
timestamp2 = getTimeStamp();
|
||||
else if(revision != 0 && timestamp == 0){
|
||||
bool found = revisionToTimeStamp(nixDB, txn, revisions_table, statePath, revision, timestamp2);
|
||||
if(!found)
|
||||
throw Error(format("Revision '%1%' cannot be matched to a timestamp...") % revision);
|
||||
}
|
||||
|
||||
//for (Strings::const_iterator i = references.begin(); i != references.end(); ++i)
|
||||
// printMsg(lvlError, format("setStateReferences::: '%1%'") % *i);
|
||||
|
||||
//Warning if it already exists
|
||||
Strings empty;
|
||||
if( nixDB.queryStrings(txn, references_table, mergeToDBRevKey(statePath, timestamp2), empty) )
|
||||
printMsg(lvlError, format("Warning: The timestamp '%1%' (now: '%5%') / revision '%4%' already exists for set-references of path '%2%' with db '%3%'")
|
||||
% timestamp2 % statePath % references_table % revision % getTimeStamp());
|
||||
|
||||
//Create the key
|
||||
string key = mergeToDBRevKey(statePath, timestamp2);
|
||||
|
||||
//Insert
|
||||
nixDB.setStrings(txn, references_table, key, references, false); //The false makes sure also empty references are set
|
||||
}
|
||||
|
||||
bool queryStateReferences(Database & nixDB, const Transaction & txn, TableId references_table, TableId revisions_table,
|
||||
const Path & statePath, Strings & references, const unsigned int revision, const unsigned int timestamp)
|
||||
{
|
||||
//printMsg(lvlError, format("queryStateReferences '%1%' with revision '%2%'") % references_table % revision);
|
||||
|
||||
//Convert revision to timestamp number useing the revisions_table
|
||||
unsigned int timestamp2 = timestamp;
|
||||
if(timestamp == 0 && revision != 0){
|
||||
bool found = revisionToTimeStamp(nixDB, txn, revisions_table, statePath, revision, timestamp2);
|
||||
if(!found) //we are asked for references of some revision, but there are no references registered yet, so we return false;
|
||||
return false;
|
||||
}
|
||||
|
||||
Strings keys;
|
||||
nixDB.enumTable(txn, references_table, keys);
|
||||
|
||||
//Mabye we need the latest timestamp?
|
||||
string key = "";
|
||||
if(timestamp2 == 0){
|
||||
bool foundsomething = lookupHighestRevivison(keys, statePath, key);
|
||||
if(!foundsomething)
|
||||
return false;
|
||||
else
|
||||
return nixDB.queryStrings(txn, references_table, key, references);
|
||||
}
|
||||
|
||||
//If a specific key is given: check if this timestamp exists key in the table
|
||||
key = mergeToDBRevKey(statePath, timestamp2);
|
||||
bool found = false;
|
||||
for (Strings::const_iterator i = keys.begin(); i != keys.end(); ++i) {
|
||||
if(key == *i){
|
||||
found = true;
|
||||
key = mergeToDBRevKey(statePath, timestamp2);
|
||||
}
|
||||
}
|
||||
|
||||
//If it doesn't exist in the table then find the highest key lower than it
|
||||
if(!found){
|
||||
bool foundsomething = lookupHighestRevivison(keys, statePath, key, 0);
|
||||
if(!foundsomething)
|
||||
return false;
|
||||
//printMsg(lvlError, format("Warning: References for timestamp '%1%' not was not found, so taking the highest rev-key possible for statePath '%2%'") % timestamp2 % statePath);
|
||||
}
|
||||
|
||||
return nixDB.queryStrings(txn, references_table, key, references); //now that we have the key, we can query the references
|
||||
}
|
||||
|
||||
void invalidateAllStateReferences(Database & nixDB, const Transaction & txn, TableId references_table, const Path & statePath)
|
||||
{
|
||||
//Remove all references
|
||||
Strings keys;
|
||||
nixDB.enumTable(txn, references_table, keys);
|
||||
for (Strings::const_iterator i = keys.begin(); i != keys.end(); ++i){
|
||||
if((*i).substr(0, statePath.length()) == statePath)
|
||||
nixDB.delPair(txn, references_table, *i);
|
||||
}
|
||||
}
|
||||
|
||||
void removeAllStatePathRevisions(Database & nixDB, const Transaction & txn, TableId revisions_table,
|
||||
TableId revisions_comments, TableId snapshots_table, TableId statecounters, const Path & statePath)
|
||||
{
|
||||
//Remove all revisions
|
||||
nixDB.delPair(txn, revisions_table, statePath);
|
||||
RevisionInfos revisions;
|
||||
queryAvailableStateRevisions(nixDB, txn, revisions_table, revisions_comments, statePath, revisions);
|
||||
|
||||
for (RevisionInfos::iterator i = revisions.begin(); i != revisions.end(); ++i){
|
||||
unsigned int rev = (*i).first;
|
||||
nixDB.delPair(txn, revisions_table, mergeToDBRevKey(statePath, rev));
|
||||
|
||||
//Remove all comments
|
||||
nixDB.delPair(txn, revisions_comments, mergeToDBRevKey(statePath, rev));
|
||||
}
|
||||
|
||||
//Remove all snapshots
|
||||
Strings keys;
|
||||
nixDB.enumTable(txn, snapshots_table, keys);
|
||||
for (Strings::const_iterator i = keys.begin(); i != keys.end(); ++i){
|
||||
if((*i).substr(0, statePath.length()) == statePath)
|
||||
nixDB.delPair(txn, snapshots_table, *i);
|
||||
}
|
||||
|
||||
//Remove all state-counters
|
||||
keys.clear();
|
||||
nixDB.enumTable(txn, statecounters, keys);
|
||||
for (Strings::const_iterator i = keys.begin(); i != keys.end(); ++i){
|
||||
if((*i).substr(0, statePath.length()) == statePath)
|
||||
nixDB.delPair(txn, statecounters, *i);
|
||||
}
|
||||
}
|
||||
|
||||
void setStateRevisions(Database & nixDB, const Transaction & txn, TableId revisions_table, TableId revisions_comments,
|
||||
TableId snapshots_table, const RevisionClosure & revisions, const Path & rootStatePath, const string & comment)
|
||||
{
|
||||
if( !isStatePath(rootStatePath) ) //weak check on statePath
|
||||
throw Error(format("StatePath '%1%' is not a statepath") % rootStatePath);
|
||||
|
||||
unsigned int timestamp = getTimeStamp();
|
||||
|
||||
//Insert all ss_epochs into snapshots_table with the current ts.
|
||||
for (RevisionClosure::const_iterator i = revisions.begin(); i != revisions.end(); ++i){
|
||||
string key = mergeToDBRevKey((*i).first, timestamp);
|
||||
Strings data;
|
||||
//the map<> takes care of the sorting on the Path
|
||||
for (Snapshots::const_iterator j = (*i).second.begin(); j != (*i).second.end(); ++j)
|
||||
data.push_back(mergeToDBRevKey((*j).first, (*j).second));
|
||||
nixDB.setStrings(txn, snapshots_table, key, data);
|
||||
}
|
||||
|
||||
//Insert for each statePath a new revision record linked to the ss_epochs
|
||||
for (RevisionClosure::const_iterator i = revisions.begin(); i != revisions.end(); ++i){
|
||||
Path statePath = (*i).first;
|
||||
|
||||
unsigned int revision = getNewRevisionNumber(nixDB, txn, revisions_table, statePath); //get a new revision number
|
||||
|
||||
string key = mergeToDBRevKey(statePath, revision);
|
||||
|
||||
//get all its requisites
|
||||
PathSet statePath_references;
|
||||
storePathRequisitesTxn(txn, statePath, false, statePath_references, false, true, 0);
|
||||
statePath_references.insert(statePath);
|
||||
|
||||
//save in db
|
||||
Strings data;
|
||||
for (PathSet::const_iterator j = statePath_references.begin(); j != statePath_references.end(); ++j)
|
||||
data.push_back(mergeToDBRevKey(*j, timestamp));
|
||||
|
||||
nixDB.setStrings(txn, revisions_table, key, data, false); //The false makes sure also empty revisions are set
|
||||
|
||||
//save the date and comments
|
||||
Strings metadata;
|
||||
metadata.push_back(unsignedInt2String(timestamp));
|
||||
|
||||
//get all paths that point to the same state (using shareing) and check if one of them equals the rootStatePath
|
||||
PathSet sharedWith = getSharedWithPathSetRecTxn(txn, statePath);
|
||||
if(statePath == rootStatePath || sharedWith.find(rootStatePath) != sharedWith.end())
|
||||
metadata.push_back(comment);
|
||||
else
|
||||
metadata.push_back("Part of the snashot closure for " + rootStatePath);
|
||||
nixDB.setStrings(txn, revisions_comments, key, metadata);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
bool queryStateRevisions(Database & nixDB, const Transaction & txn, TableId revisions_table, TableId snapshots_table,
|
||||
const Path & statePath, RevisionClosure & revisions, RevisionClosureTS & timestamps, const unsigned int root_revision)
|
||||
{
|
||||
string key;
|
||||
|
||||
if(root_revision == 0){
|
||||
Strings keys;
|
||||
nixDB.enumTable(txn, revisions_table, keys); //get all revisions
|
||||
bool foundsomething = lookupHighestRevivison(keys, statePath, key);
|
||||
if(!foundsomething)
|
||||
return false;
|
||||
}
|
||||
else
|
||||
key = mergeToDBRevKey(statePath, root_revision);
|
||||
|
||||
//Get references pointing to snapshots_table from revisions_table with root_revision
|
||||
Strings statePaths;
|
||||
bool notempty = nixDB.queryStrings(txn, revisions_table, key, statePaths);
|
||||
|
||||
if(!notempty)
|
||||
throw Error(format("Root revision '%1%' not found of statePath '%2%'") % unsignedInt2String(root_revision) % statePath);
|
||||
|
||||
//For each statePath add the revisions
|
||||
for (Strings::iterator i = statePaths.begin(); i != statePaths.end(); ++i){
|
||||
|
||||
Path getStatePath;
|
||||
unsigned int getTimestamp;
|
||||
splitDBRevKey(*i, getStatePath, getTimestamp);
|
||||
|
||||
Strings snapshots_s;
|
||||
Snapshots snapshots;
|
||||
nixDB.queryStrings(txn, snapshots_table, *i, snapshots_s);
|
||||
int counter=0;
|
||||
for (Strings::iterator j = snapshots_s.begin(); j != snapshots_s.end(); ++j){
|
||||
|
||||
Path snapshottedPath;
|
||||
unsigned int revision;
|
||||
splitDBRevKey(*j, snapshottedPath, revision);
|
||||
|
||||
snapshots[snapshottedPath] = revision;
|
||||
counter++;
|
||||
}
|
||||
|
||||
revisions[getStatePath] = snapshots;
|
||||
timestamps[getStatePath] = getTimestamp;
|
||||
}
|
||||
|
||||
return notempty;
|
||||
}
|
||||
|
||||
//TODO include comments into revisions?
|
||||
bool queryAvailableStateRevisions(Database & nixDB, const Transaction & txn, TableId revisions_table, TableId revisions_comments,
|
||||
const Path & statePath, RevisionInfos & revisions)
|
||||
{
|
||||
Strings keys;
|
||||
nixDB.enumTable(txn, revisions_table, keys); //get all revisions
|
||||
|
||||
for (Strings::const_iterator i = keys.begin(); i != keys.end(); ++i) {
|
||||
|
||||
if((*i).substr(0, statePath.length()) != statePath || (*i).length() == statePath.length()) //dont check the new-revision key or other keys
|
||||
continue;
|
||||
|
||||
Path getStatePath;
|
||||
unsigned int getRevision;
|
||||
splitDBRevKey(*i, getStatePath, getRevision);
|
||||
|
||||
//save the date and comments
|
||||
RevisionInfo rev;
|
||||
Strings metadata;
|
||||
nixDB.queryStrings(txn, revisions_comments, *i, metadata);
|
||||
unsigned int ts;
|
||||
bool succeed = string2UnsignedInt(*(metadata.begin()), ts);
|
||||
if(!succeed)
|
||||
throw Error(format("Malformed timestamp in the revisions table of path '%1%'") % *i);
|
||||
rev.timestamp = ts;
|
||||
metadata.pop_front();
|
||||
rev.comment = *(metadata.begin());
|
||||
revisions[getRevision] = rev;
|
||||
}
|
||||
|
||||
if(revisions.empty())
|
||||
return false;
|
||||
else
|
||||
return true;
|
||||
}
|
||||
|
||||
void setVersionedStateEntries(Database & nixDB, const Transaction & txn, TableId versionItems, TableId revisions_table,
|
||||
const Path & statePath, const StateInfos & infos, const unsigned int revision, const unsigned int timestamp)
|
||||
{
|
||||
if( !isValidStatePathTxn(txn, statePath) )
|
||||
throw Error(format("path '%1%' is not a statepath") % statePath);
|
||||
|
||||
//TODO THIS IS THE SAME CODE AS IN SETSTATEREFERENCES !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
|
||||
//Find the timestamp if we need
|
||||
unsigned int timestamp2 = timestamp;
|
||||
if(revision == 0 && timestamp == 0)
|
||||
timestamp2 = getTimeStamp();
|
||||
else if(revision != 0 && timestamp == 0){
|
||||
bool found = revisionToTimeStamp(nixDB, txn, revisions_table, statePath, revision, timestamp2);
|
||||
if(!found)
|
||||
throw Error(format("Revision '%1%' cannot be matched to a timestamp...") % revision);
|
||||
}
|
||||
|
||||
Strings ss;
|
||||
for (StateInfos::const_iterator i = infos.begin(); i != infos.end(); ++i) {
|
||||
StateInfo si = *i;
|
||||
ss.push_back(mergeToDBKey(si.path, mergeToDBKey(si.type, unsignedInt2String(si.interval))));
|
||||
}
|
||||
nixDB.setStrings(txn, versionItems, statePath, ss);
|
||||
}
|
||||
|
||||
bool getVersionedStateEntries(Database & nixDB, const Transaction & txn, TableId versionItems, TableId revisions_table,
|
||||
const Path & statePath, StateInfos & infos, const unsigned int revision, const unsigned int timestamp)
|
||||
{
|
||||
if( !isValidStatePathTxn(txn, statePath) )
|
||||
throw Error(format("path '%1%' is not a statepath") % statePath);
|
||||
|
||||
//TODO THIS IS THE SAME CODE AS IN QUERY STATEREFERRERS !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!1
|
||||
//Convert revision to timestamp number useing the revisions_table
|
||||
unsigned int timestamp2 = timestamp;
|
||||
if(timestamp == 0 && revision != 0){
|
||||
bool found = revisionToTimeStamp(nixDB, txn, revisions_table, statePath, revision, timestamp2);
|
||||
if(!found) //we are asked for references of some revision, but there are no references registered yet, so we return false;
|
||||
return false;
|
||||
}
|
||||
|
||||
Strings ss;
|
||||
bool found = nixDB.queryStrings(txn, versionItems, statePath, ss);
|
||||
if(!found)
|
||||
return false;
|
||||
|
||||
for (Strings::const_iterator i = ss.begin(); i != ss.end(); ++i) {
|
||||
|
||||
StateInfo si;
|
||||
|
||||
string s1;
|
||||
string interval;
|
||||
splitDBKey(*i, s1, interval);
|
||||
bool succeed = string2UnsignedInt(interval, si.interval);
|
||||
if(!succeed)
|
||||
throw Error(format("Malformed TS value of record '%1%'") % *i);
|
||||
|
||||
string path;
|
||||
string type;
|
||||
splitDBKey(s1, si.path, si.type);
|
||||
|
||||
infos.push_back(si);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void setStateOptions(Database & nixDB, const Transaction & txn, TableId stateOptions, const Path & statePath,
|
||||
const string & user, const string & group, int chmod, const string & runtimeArgs)
|
||||
{
|
||||
string value = mergeToDBKey(user,mergeToDBKey(group, mergeToDBKey(int2String(chmod), runtimeArgs)));
|
||||
nixDB.setString(txn, stateOptions, statePath, value);
|
||||
}
|
||||
|
||||
void getStateOptions(Database & nixDB, const Transaction & txn, TableId stateOptions, const Path & statePath,
|
||||
string & user, string & group, int & chmod, string & runtimeArgs)
|
||||
{
|
||||
string value;
|
||||
bool notEmpty = nixDB.queryString(txn, stateOptions, statePath, value);
|
||||
if(!notEmpty)
|
||||
throw Error(format("No rights found for path '%1%'") % statePath);
|
||||
|
||||
string s1;
|
||||
splitDBKey(value, s1, runtimeArgs);
|
||||
|
||||
string s2;
|
||||
string chmod_s;
|
||||
splitDBKey(s1, s2, chmod_s);
|
||||
|
||||
bool succeed = string2Int(chmod_s, chmod);
|
||||
if(!succeed)
|
||||
throw Error(format("Malformed chmod value of path '%1%'") % statePath);
|
||||
|
||||
splitDBKey(s2, user, group);
|
||||
}
|
||||
|
||||
}
|
||||
102
src/libstore/store-state.hh
Normal file
102
src/libstore/store-state.hh
Normal file
@@ -0,0 +1,102 @@
|
||||
#ifndef __STORESTATE_H
|
||||
#define __STORESTATE_H
|
||||
|
||||
#include "derivations.hh"
|
||||
#include "types.hh"
|
||||
#include "db.hh"
|
||||
|
||||
namespace nix {
|
||||
|
||||
/* Create a state directory. */
|
||||
void createSubStateDirsTxn(const Transaction & txn, const DerivationStateOutputDirs & stateOutputDirs, const DerivationStateOutputs & stateOutputs);
|
||||
|
||||
/* TODO */
|
||||
Snapshots commitStatePathTxn(const Transaction & txn, const Path & statePath);
|
||||
|
||||
/* TODO */
|
||||
//void updateRevisionsRecursivelyTxn(const Transaction & txn, const Path & statePath);
|
||||
|
||||
/* TODO */
|
||||
//int readRevisionNumber(Path statePath);
|
||||
|
||||
|
||||
void scanAndUpdateAllReferencesTxn(const Transaction & txn, const Path & statePath
|
||||
, PathSet & newFoundComponentReferences, PathSet & newFoundStateReferences);
|
||||
|
||||
void scanAndUpdateAllReferencesRecusivelyTxn(const Transaction & txn, const Path & statePath);
|
||||
|
||||
/* revision 0 == latest ????? */
|
||||
void revertToRevisionTxn(const Transaction & txn, const Path & statePath, const int revision_arg, const bool recursive);
|
||||
|
||||
/* Copy all files and folders recursively (also the hidden ones) from the dir from/... to the dir to/... and delete the rest in to/ (Rsync) */
|
||||
void rsyncPaths(const Path & from, const Path & to, const bool addSlashes);
|
||||
|
||||
|
||||
// **************************************** *******************************************
|
||||
|
||||
|
||||
/* TODO */
|
||||
bool lookupHighestRevivison(const Strings & keys, const Path & statePath, string & key, unsigned int lowerthan = 0);
|
||||
|
||||
/* TODO */
|
||||
unsigned int getNewRevisionNumber(Database & nixDB, const Transaction & txn, TableId table, const Path & statePath);
|
||||
|
||||
/* TODO */
|
||||
Path mergeToDBKey(const string & s1, const string & s2);
|
||||
Path mergeToDBRevKey(const Path & statePath, const unsigned int intvalue);
|
||||
|
||||
/* TODO */
|
||||
void splitDBKey(const string & s, string & s1, string & s2);
|
||||
void splitDBRevKey(const Path & revisionedStatePath, Path & statePath, unsigned int & intvalue);
|
||||
|
||||
|
||||
|
||||
/* TODO */
|
||||
bool revisionToTimeStamp(Database & nixDB, const Transaction & txn, TableId revisions_table, const Path & statePath, const int revision, unsigned int & timestamp);
|
||||
|
||||
/* Set the stateReferences for a specific revision (and older until the next higher revision number in the table) */
|
||||
void setStateReferences(Database & nixDB, const Transaction & txn, TableId references_table, TableId revisions_table,
|
||||
const Path & statePath, const Strings & references, const unsigned int revision = 0, const unsigned int timestamp = 0);
|
||||
|
||||
/* Returns the references for a specific revision (and older until the next higher revision number in the table) */
|
||||
bool queryStateReferences(Database & nixDB, const Transaction & txn, TableId references_table, TableId revisions_table,
|
||||
const Path & statePath, Strings & references, const unsigned int revision = 0, const unsigned int timestamp = 0);
|
||||
|
||||
/* Get the revision number of the statePath and the revision numbers of all state paths in the references closure */
|
||||
void setStateRevisions(Database & nixDB, const Transaction & txn, TableId revisions_table, TableId revisions_comments,
|
||||
TableId snapshots_table, const RevisionClosure & revisions, const Path & rootStatePath, const string & comment);
|
||||
|
||||
/* Returns all the revision numbers of the state references closure of the given state path */
|
||||
bool queryStateRevisions(Database & nixDB, const Transaction & txn, TableId revisions_table, TableId snapshots_table,
|
||||
const Path & statePath, RevisionClosure & revisions, RevisionClosureTS & timestamps, const unsigned int root_revision = 0);
|
||||
|
||||
/* Returns all available revision numbers of the given state path */
|
||||
bool queryAvailableStateRevisions(Database & nixDB, const Transaction & txn, TableId revisions_table, TableId revisions_comments,
|
||||
const Path & statePath, RevisionInfos & revisions);
|
||||
|
||||
/**/
|
||||
void invalidateAllStateReferences(Database & nixDB, const Transaction & txn, TableId references_table, const Path & statePath);
|
||||
|
||||
/**/
|
||||
void removeAllStatePathRevisions(Database & nixDB, const Transaction & txn, TableId revisions_table,
|
||||
TableId revisions_comments, TableId snapshots_table, TableId statecounters, const Path & statePath);
|
||||
|
||||
/**/
|
||||
void setVersionedStateEntries(Database & nixDB, const Transaction & txn, TableId versionItems, TableId revisions_table,
|
||||
const Path & statePath, const StateInfos & infos, const unsigned int revision = 0, const unsigned int timestamp = 0);
|
||||
|
||||
/**/
|
||||
bool getVersionedStateEntries(Database & nixDB, const Transaction & txn, TableId versionItems, TableId revisions_table,
|
||||
const Path & statePath, StateInfos & infos, const unsigned int revision = 0, const unsigned int timestamp = 0);
|
||||
|
||||
/**/
|
||||
void setStateOptions(Database & nixDB, const Transaction & txn, TableId stateOptions, const Path & statePath, const string & user, const string & group, int chmod, const string & runtimeArgs);
|
||||
|
||||
/**/
|
||||
void getStateOptions(Database & nixDB, const Transaction & txn, TableId stateOptions, const Path & statePath, string & user, string & group, int & chmod, string & runtimeArgs);
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
#endif /* !__STORESTATE_H */
|
||||
@@ -8,30 +8,54 @@ namespace nix {
|
||||
#define WORKER_MAGIC_1 0x6e697863
|
||||
#define WORKER_MAGIC_2 0x6478696f
|
||||
|
||||
#define PROTOCOL_VERSION 0x101
|
||||
#define PROTOCOL_VERSION 0x102
|
||||
#define GET_PROTOCOL_MAJOR(x) ((x) & 0xff00)
|
||||
#define GET_PROTOCOL_MINOR(x) ((x) & 0x00ff)
|
||||
|
||||
|
||||
typedef enum {
|
||||
wopQuit = 0,
|
||||
wopIsValidPath,
|
||||
wopHasSubstitutes = 3,
|
||||
wopQuit = 0, //0
|
||||
wopIsValidPath, //1
|
||||
wopHasSubstitutes = 3, //3
|
||||
wopIsValidStatePath,
|
||||
wopIsValidComponentOrStatePath,
|
||||
wopQueryPathHash,
|
||||
wopQueryReferences,
|
||||
wopQueryReferrers,
|
||||
wopAddToStore,
|
||||
wopAddTextToStore,
|
||||
wopBuildDerivations,
|
||||
wopEnsurePath,
|
||||
wopQueryStatePathDrv,
|
||||
wopQueryStoreReferences,
|
||||
wopQueryStateReferences,
|
||||
wopQueryStoreReferrers, //10
|
||||
wopQueryStateReferrers,
|
||||
wopAddToStore,
|
||||
wopAddTextToStore, //13
|
||||
wopBuildDerivations, //14
|
||||
wopEnsurePath, //15
|
||||
wopAddTempRoot,
|
||||
wopAddIndirectRoot,
|
||||
wopSyncWithGC,
|
||||
wopFindRoots,
|
||||
wopCollectGarbage,
|
||||
wopExportPath,
|
||||
wopCollectGarbage, //20
|
||||
wopExportPath,
|
||||
wopImportPath,
|
||||
wopQueryDeriver,
|
||||
wopSetOptions,
|
||||
wopQueryDerivers,
|
||||
wopSetStatePathsInterval,
|
||||
wopGetStatePathsInterval,
|
||||
wopIsStateComponent,
|
||||
wopStorePathRequisites,
|
||||
wopSetStateRevisions,
|
||||
wopQueryStateRevisions, //30
|
||||
wopQueryAvailableStateRevisions,
|
||||
wopCommitStatePath,
|
||||
wopScanAndUpdateAllReferences,
|
||||
wopGetSharedWith,
|
||||
wopToNonSharedPathSet,
|
||||
wopRevertToRevision,
|
||||
wopShareState,
|
||||
wopUnShareState,
|
||||
wopLookupStatePath,
|
||||
wopSetStateOptions,
|
||||
wopGetStateOptions,
|
||||
wopSetOptions, //40
|
||||
} WorkerOp;
|
||||
|
||||
|
||||
@@ -51,7 +75,11 @@ typedef enum {
|
||||
|
||||
|
||||
Path readStorePath(Source & from);
|
||||
Path readStatePath(Source & from);
|
||||
Path readStoreOrStatePath(Source & from);
|
||||
|
||||
PathSet readStorePaths(Source & from);
|
||||
PathSet readStatePaths(Source & from);
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -3,14 +3,16 @@ pkglib_LTLIBRARIES = libutil.la
|
||||
libutil_la_SOURCES = util.cc hash.cc serialise.cc \
|
||||
archive.cc aterm.cc aterm-map.cc xml-writer.cc
|
||||
|
||||
libutil_la_LIBADD = ../boost/format/libformat.la
|
||||
|
||||
pkginclude_HEADERS = util.hh hash.hh serialise.hh \
|
||||
archive.hh aterm.hh aterm-map.hh xml-writer.hh types.hh
|
||||
|
||||
libutil_la_LIBADD = ../boost/format/libformat.la
|
||||
|
||||
if !HAVE_OPENSSL
|
||||
libutil_la_SOURCES += \
|
||||
md5.c md5.h sha1.c sha1.h sha256.c sha256.h md32_common.h
|
||||
endif
|
||||
|
||||
AM_CXXFLAGS = -Wall -I$(srcdir)/.. ${aterm_include}
|
||||
AM_CXXFLAGS = -Wall \
|
||||
-I$(srcdir)/.. ${aterm_include}
|
||||
|
||||
@@ -51,3 +51,15 @@ ATermList nix::toATermList(const StringSet & ss)
|
||||
l = ATinsert(l, toATerm(*i));
|
||||
return l;
|
||||
}
|
||||
|
||||
ATermList nix::toATermList(const SetStringSet & sss)
|
||||
{
|
||||
ATermList l = ATempty;
|
||||
for (SetStringSet::const_reverse_iterator i = sss.rbegin(); i != sss.rend(); ++i){
|
||||
StringSet ss = *i;
|
||||
for (StringSet::const_reverse_iterator j = ss.rbegin(); j != ss.rend(); ++j){
|
||||
l = ATinsert(l, toATerm(*j));
|
||||
}
|
||||
}
|
||||
return l;
|
||||
}
|
||||
|
||||
@@ -44,6 +44,7 @@ ATerm toATerm(const char * s);
|
||||
ATerm toATerm(const string & s);
|
||||
|
||||
ATermList toATermList(const StringSet & ss);
|
||||
ATermList toATermList(const SetStringSet & sss);
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -38,6 +38,11 @@ void writeInt(unsigned int n, Sink & sink)
|
||||
sink(buf, sizeof(buf));
|
||||
}
|
||||
|
||||
void writeBigUnsignedInt(unsigned int n, Sink & sink)
|
||||
{
|
||||
writeString(unsignedInt2String(n), sink); //TODO better way?
|
||||
}
|
||||
|
||||
|
||||
void writeString(const string & s, Sink & sink)
|
||||
{
|
||||
@@ -55,6 +60,61 @@ void writeStringSet(const StringSet & ss, Sink & sink)
|
||||
writeString(*i, sink);
|
||||
}
|
||||
|
||||
void writeIntVector(const IntVector & iv, Sink & sink)
|
||||
{
|
||||
writeInt(iv.size(), sink);
|
||||
for(unsigned int i=0;i < iv.size(); i++)
|
||||
writeString(int2String(iv.at(i)), sink); //TODO !!!!!!!!!!!!!!!!!!! writeInts ???????????
|
||||
}
|
||||
|
||||
void writeRevisionClosure(const RevisionClosure & rc, Sink & sink)
|
||||
{
|
||||
writeInt(rc.size(), sink);
|
||||
for (RevisionClosure::const_iterator i = rc.begin(); i != rc.end(); ++i){
|
||||
writeString((*i).first, sink);
|
||||
writeSnapshots((*i).second, sink);
|
||||
}
|
||||
}
|
||||
|
||||
void writeSnapshots(const Snapshots & ss, Sink & sink)
|
||||
{
|
||||
writeInt(ss.size(), sink);
|
||||
for (Snapshots::const_iterator i = ss.begin(); i != ss.end(); ++i){
|
||||
writeString((*i).first, sink);
|
||||
writeBigUnsignedInt((*i).second, sink);
|
||||
}
|
||||
}
|
||||
|
||||
void writeRevisionClosureTS(const RevisionClosureTS & rc, Sink & sink)
|
||||
{
|
||||
writeInt(rc.size(), sink);
|
||||
for (RevisionClosureTS::const_iterator i = rc.begin(); i != rc.end(); ++i){
|
||||
writeString((*i).first, sink);
|
||||
writeBigUnsignedInt((*i).second, sink);
|
||||
}
|
||||
}
|
||||
|
||||
void writeRevisionInfos(const RevisionInfos & ri, Sink & sink)
|
||||
{
|
||||
writeInt(ri.size(), sink);
|
||||
for (RevisionInfos::const_iterator i = ri.begin(); i != ri.end(); ++i){
|
||||
writeBigUnsignedInt((*i).first, sink);
|
||||
RevisionInfo rvi = (*i).second;
|
||||
writeString(rvi.comment, sink);
|
||||
writeBigUnsignedInt(rvi.timestamp, sink);
|
||||
}
|
||||
}
|
||||
|
||||
void writeCommitIntervals(const CommitIntervals ci, Sink & sink)
|
||||
{
|
||||
writeInt(ci.size(), sink);
|
||||
for (CommitIntervals::const_iterator i = ci.begin(); i != ci.end(); ++i){
|
||||
writeString((*i).first, sink);
|
||||
writeBigUnsignedInt((*i).second, sink);
|
||||
}
|
||||
}
|
||||
|
||||
////////////////
|
||||
|
||||
void readPadding(unsigned int len, Source & source)
|
||||
{
|
||||
@@ -67,7 +127,6 @@ void readPadding(unsigned int len, Source & source)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
unsigned int readInt(Source & source)
|
||||
{
|
||||
unsigned char buf[8];
|
||||
@@ -81,6 +140,15 @@ unsigned int readInt(Source & source)
|
||||
(buf[3] << 24);
|
||||
}
|
||||
|
||||
unsigned int readBigUnsignedInt(Source & source)
|
||||
{
|
||||
string s = readString(source);
|
||||
unsigned int i;
|
||||
if(! string2UnsignedInt(s, i) )
|
||||
throw Error(format("Serialize: readBigUnsignedInt cannot read int: '%1%'") % s);
|
||||
return i;
|
||||
}
|
||||
|
||||
|
||||
string readString(Source & source)
|
||||
{
|
||||
@@ -102,5 +170,82 @@ StringSet readStringSet(Source & source)
|
||||
return ss;
|
||||
}
|
||||
|
||||
IntVector readIntVector(Source & source)
|
||||
{
|
||||
unsigned int count = readInt(source);
|
||||
IntVector iv;
|
||||
while (count--){
|
||||
string s = readString(source); //TODO !!!!!!!!!!!!!!!!!!!! readInt ???????????????
|
||||
int i;
|
||||
if (!string2Int(s, i))
|
||||
throw Error(format("`%1%' is corrupt in readIntVector") % s);
|
||||
iv.push_back(i);
|
||||
}
|
||||
return iv;
|
||||
}
|
||||
|
||||
RevisionClosure readRevisionClosure(Source & source)
|
||||
{
|
||||
unsigned int count = readInt(source);
|
||||
RevisionClosure rc;
|
||||
while (count--){
|
||||
string path = readString(source);
|
||||
Snapshots ss = readSnapshots(source);
|
||||
rc[path] = ss;
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
Snapshots readSnapshots(Source & source)
|
||||
{
|
||||
unsigned int count = readInt(source);
|
||||
Snapshots ss;
|
||||
while (count--){
|
||||
string path = readString(source);
|
||||
unsigned int ri = readBigUnsignedInt(source);
|
||||
ss[path] = ri;
|
||||
}
|
||||
return ss;
|
||||
}
|
||||
|
||||
|
||||
RevisionClosureTS readRevisionClosureTS(Source & source)
|
||||
{
|
||||
unsigned int count = readInt(source);
|
||||
RevisionClosureTS rc;
|
||||
while (count--){
|
||||
string path = readString(source);
|
||||
unsigned int ri = readBigUnsignedInt(source);
|
||||
rc[path] = ri;
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
RevisionInfos readRevisionInfos(Source & source)
|
||||
{
|
||||
unsigned int count = readInt(source);
|
||||
RevisionInfos ri;
|
||||
while (count--){
|
||||
unsigned int revision = readBigUnsignedInt(source);
|
||||
RevisionInfo rvi;
|
||||
rvi.comment = readString(source);
|
||||
rvi.timestamp = readBigUnsignedInt(source);
|
||||
ri[revision] = rvi;
|
||||
}
|
||||
return ri;
|
||||
}
|
||||
|
||||
CommitIntervals readCommitIntervals(Source & source)
|
||||
{
|
||||
unsigned int count = readInt(source);
|
||||
CommitIntervals ci;
|
||||
while (count--){
|
||||
string path = readString(source);
|
||||
unsigned int ri = readBigUnsignedInt(source);
|
||||
ci[path] = ri;
|
||||
}
|
||||
return ci;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -95,14 +95,29 @@ struct StringSource : Source
|
||||
|
||||
void writePadding(unsigned int len, Sink & sink);
|
||||
void writeInt(unsigned int n, Sink & sink);
|
||||
void writeBigUnsignedInt(unsigned int n, Sink & sink);
|
||||
void writeString(const string & s, Sink & sink);
|
||||
void writeStringSet(const StringSet & ss, Sink & sink);
|
||||
|
||||
void writeIntVector(const IntVector & iv, Sink & sink);
|
||||
void writeRevisionClosure(const RevisionClosure & rc, Sink & sink);
|
||||
void writeSnapshots(const Snapshots & ss, Sink & sink);
|
||||
void writeRevisionClosureTS(const RevisionClosureTS & rc, Sink & sink);
|
||||
void writeRevisionInfos(const RevisionInfos & ri, Sink & sink);
|
||||
void writeCommitIntervals(const CommitIntervals ci, Sink & sink);
|
||||
|
||||
void readPadding(unsigned int len, Source & source);
|
||||
unsigned int readInt(Source & source);
|
||||
unsigned int readBigUnsignedInt(Source & source);
|
||||
string readString(Source & source);
|
||||
StringSet readStringSet(Source & source);
|
||||
|
||||
IntVector readIntVector(Source & source);
|
||||
RevisionClosure readRevisionClosure(Source & source);
|
||||
Snapshots readSnapshots(Source & source);
|
||||
RevisionClosureTS readRevisionClosureTS(Source & source);
|
||||
RevisionInfos readRevisionInfos(Source & source);
|
||||
CommitIntervals readCommitIntervals(Source & source);
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
#include <string>
|
||||
#include <list>
|
||||
#include <set>
|
||||
#include <map>
|
||||
|
||||
#include <boost/format.hpp>
|
||||
|
||||
@@ -15,6 +16,7 @@ namespace nix {
|
||||
using std::string;
|
||||
using std::list;
|
||||
using std::set;
|
||||
using std::map;
|
||||
using std::vector;
|
||||
using boost::format;
|
||||
|
||||
@@ -51,14 +53,39 @@ public:
|
||||
|
||||
|
||||
typedef list<string> Strings;
|
||||
typedef list<Strings> StringsList;
|
||||
typedef set<string> StringSet;
|
||||
|
||||
typedef set<StringSet> SetStringSet;
|
||||
|
||||
/* Paths are just strings. */
|
||||
typedef string Path;
|
||||
typedef list<Path> Paths;
|
||||
typedef set<Path> PathSet;
|
||||
|
||||
//state types
|
||||
typedef vector<int> IntVector; //the Strings (list) of StateReferences and this list are connected by position //TODO
|
||||
typedef vector<unsigned int> UnsignedIntVector;
|
||||
typedef map<Path, unsigned int> CommitIntervals;
|
||||
struct RevisionInfo
|
||||
{
|
||||
string comment;
|
||||
unsigned int timestamp;
|
||||
};
|
||||
typedef map<unsigned int, RevisionInfo> RevisionInfos;
|
||||
typedef map<Path, unsigned int> Snapshots; // /nix/state/...../cache -> .... Automatically sorted on Path :)
|
||||
typedef map<Path, Snapshots> RevisionClosure;
|
||||
typedef map<Path, unsigned int> RevisionClosureTS; //Paht with a timestamp about when the revision was made.
|
||||
typedef map<int, Strings> StateReferences;
|
||||
|
||||
|
||||
struct StateInfo
|
||||
{
|
||||
string path;
|
||||
string type;
|
||||
unsigned int interval;
|
||||
};
|
||||
typedef list<StateInfo> StateInfos;
|
||||
|
||||
|
||||
typedef enum {
|
||||
lvlError,
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
#include "config.h"
|
||||
|
||||
#include "util.hh"
|
||||
|
||||
#ifdef __CYGWIN__
|
||||
#include <windows.h>
|
||||
#endif
|
||||
@@ -8,15 +10,15 @@
|
||||
#include <cerrno>
|
||||
#include <cstdio>
|
||||
#include <sstream>
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
#include <cstring>
|
||||
|
||||
#include <sys/stat.h>
|
||||
#include <sys/wait.h>
|
||||
#include <sys/types.h>
|
||||
#include <fcntl.h>
|
||||
|
||||
#include "util.hh"
|
||||
|
||||
|
||||
extern char * * environ;
|
||||
|
||||
|
||||
@@ -105,8 +107,7 @@ Path canonPath(const Path & path, bool resolveSymlinks)
|
||||
/* If s points to a symlink, resolve it and restart (since
|
||||
the symlink target might contain new symlinks). */
|
||||
if (resolveSymlinks && isLink(s)) {
|
||||
followCount++;
|
||||
if (followCount >= maxFollow)
|
||||
if (++followCount >= maxFollow)
|
||||
throw Error(format("infinite symlink recursion in path `%1%'") % path);
|
||||
temp = absPath(readLink(s), dirOf(s))
|
||||
+ string(i, end);
|
||||
@@ -259,7 +260,7 @@ static void _deletePath(const Path & path, unsigned long long & bytesFreed)
|
||||
|
||||
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);
|
||||
|
||||
bytesFreed += st.st_size;
|
||||
|
||||
@@ -269,7 +270,7 @@ static void _deletePath(const Path & path, unsigned long long & bytesFreed)
|
||||
/* Make the directory writable. */
|
||||
if (!(st.st_mode & S_IWUSR)) {
|
||||
if (chmod(path.c_str(), st.st_mode | S_IWUSR) == -1)
|
||||
throw SysError(format("making `%1%' writable") % path);
|
||||
throw SysError(format("making `%1%' writable") % path);
|
||||
}
|
||||
|
||||
for (Strings::iterator i = names.begin(); i != names.end(); ++i)
|
||||
@@ -318,19 +319,19 @@ void makePathReadOnly(const Path & path)
|
||||
}
|
||||
|
||||
|
||||
static Path tempName(const Path & tmpRoot)
|
||||
static Path tempName(const Path & tmpRoot, const Path & prefix)
|
||||
{
|
||||
static int counter = 0;
|
||||
Path tmpRoot2 = canonPath(tmpRoot.empty() ? getEnv("TMPDIR", "/tmp") : tmpRoot, true);
|
||||
return (format("%1%/nix-%2%-%3%") % tmpRoot2 % getpid() % counter++).str();
|
||||
return (format("%1%/%2%-%3%-%4%") % tmpRoot2 % prefix % getpid() % counter++).str();
|
||||
}
|
||||
|
||||
|
||||
Path createTempDir(const Path & tmpRoot)
|
||||
Path createTempDir(const Path & tmpRoot, const Path & prefix)
|
||||
{
|
||||
while (1) {
|
||||
checkInterrupt();
|
||||
Path tmpDir = tempName(tmpRoot);
|
||||
Path tmpDir = tempName(tmpRoot, prefix);
|
||||
if (mkdir(tmpDir.c_str(), 0777) == 0) {
|
||||
/* Explicitly set the group of the directory. This is to
|
||||
work around around problems caused by BSD's group
|
||||
@@ -349,14 +350,16 @@ Path createTempDir(const Path & tmpRoot)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void createDirs(const Path & path)
|
||||
Paths createDirs(const Path & path)
|
||||
{
|
||||
if (path == "/") return;
|
||||
createDirs(dirOf(path));
|
||||
if (!pathExists(path))
|
||||
if (path == "/") return Paths();
|
||||
Paths created = createDirs(dirOf(path));
|
||||
if (!pathExists(path)) {
|
||||
if (mkdir(path.c_str(), 0777) == -1)
|
||||
throw SysError(format("creating directory `%1%'") % path);
|
||||
created.push_back(path);
|
||||
}
|
||||
return created;
|
||||
}
|
||||
|
||||
|
||||
@@ -371,7 +374,8 @@ void writeStringToFile(const Path & path, const string & s)
|
||||
|
||||
|
||||
LogType logType = ltPretty;
|
||||
Verbosity verbosity = lvlInfo;
|
||||
Verbosity verbosity = lvlInfo; //Default
|
||||
//Verbosity verbosity = lvlVomit; //Debugging
|
||||
|
||||
static int nestingLevel = 0;
|
||||
|
||||
@@ -465,7 +469,8 @@ void readFull(int fd, unsigned char * buf, size_t count)
|
||||
if (errno == EINTR) continue;
|
||||
throw SysError("reading from file");
|
||||
}
|
||||
if (res == 0) throw EndOfFile("unexpected end-of-file");
|
||||
if (res == 0)
|
||||
throw EndOfFile("unexpected end-of-file (in readFull so daemon communication)");
|
||||
count -= res;
|
||||
buf += res;
|
||||
}
|
||||
@@ -509,14 +514,25 @@ string drainFD(int fd)
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
AutoDelete::AutoDelete(const string & p) : path(p)
|
||||
AutoDelete::AutoDelete(const string & p, bool recursive) : path(p)
|
||||
{
|
||||
del = true;
|
||||
this->recursive = recursive;
|
||||
}
|
||||
|
||||
AutoDelete::~AutoDelete()
|
||||
{
|
||||
if (del) deletePath(path);
|
||||
try {
|
||||
if (del)
|
||||
if (recursive)
|
||||
deletePath(path);
|
||||
else {
|
||||
if (remove(path.c_str()) == -1)
|
||||
throw SysError(format("cannot unlink `%1%'") % path);
|
||||
}
|
||||
} catch (...) {
|
||||
ignoreException();
|
||||
}
|
||||
}
|
||||
|
||||
void AutoDelete::cancel()
|
||||
@@ -752,10 +768,10 @@ void killUser(uid_t uid)
|
||||
if (errno != EINTR)
|
||||
throw SysError(format("cannot kill processes for uid `%1%'") % uid);
|
||||
}
|
||||
|
||||
|
||||
} catch (std::exception & e) {
|
||||
std::cerr << format("killing processes beloging to uid `%1%': %1%\n")
|
||||
% uid % e.what();
|
||||
std::cerr << format("killing processes beloging to uid `%1%': %1%")
|
||||
% uid % e.what() << std::endl;
|
||||
quickExit(1);
|
||||
}
|
||||
quickExit(0);
|
||||
@@ -774,7 +790,6 @@ void killUser(uid_t uid)
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
string runProgram(Path program, bool searchPath, const Strings & args)
|
||||
{
|
||||
checkInterrupt();
|
||||
@@ -811,7 +826,7 @@ string runProgram(Path program, bool searchPath, const Strings & args)
|
||||
throw SysError(format("executing `%1%'") % program);
|
||||
|
||||
} catch (std::exception & e) {
|
||||
std::cerr << "error: " << e.what() << std::endl;
|
||||
std::cerr << "error: " << e.what() << std::endl; //TODO does not give the full error message
|
||||
}
|
||||
quickExit(1);
|
||||
}
|
||||
@@ -926,6 +941,34 @@ Strings unpackStrings(const string & s)
|
||||
return strings;
|
||||
}
|
||||
|
||||
/*
|
||||
string packRevisionNumbers(const UnsignedIntVector & revs)
|
||||
{
|
||||
string seperator = "|";
|
||||
string d = "";
|
||||
for (UnsignedIntVector::const_iterator i = revs.begin(); i != revs.end(); ++i){
|
||||
d += unsignedInt2String(*i) + seperator;
|
||||
}
|
||||
return d;
|
||||
}
|
||||
|
||||
UnsignedIntVector unpackRevisionNumbers(const string & packed)
|
||||
{
|
||||
string seperator = "|";
|
||||
Strings ss = tokenizeString(packed, seperator);
|
||||
UnsignedIntVector revs;
|
||||
|
||||
for (Strings::const_iterator i = ss.begin(); i != ss.end(); ++i){
|
||||
int rev;
|
||||
bool succeed = string2UnsignedInt(*i, rev);
|
||||
if(!succeed)
|
||||
throw Error(format("Corrupted revisions db entry: `%1%'") % packed);
|
||||
revs.push_back(rev);
|
||||
}
|
||||
|
||||
return revs;
|
||||
}
|
||||
*/
|
||||
|
||||
Strings tokenizeString(const string & s, const string & separators)
|
||||
{
|
||||
@@ -941,14 +984,62 @@ Strings tokenizeString(const string & s, const string & separators)
|
||||
return result;
|
||||
}
|
||||
|
||||
//Splits on a space
|
||||
//Uses quotes: "...." to group multiple spaced arguments into one
|
||||
//Removes the grouping quotes at the call
|
||||
//TODO bug a call with args: a b "c" d will fail
|
||||
//TODO bug a call with args: a bc"d " e will fail
|
||||
Strings tokenizeStringWithQuotes(const string & s)
|
||||
{
|
||||
string separator = " ";
|
||||
Strings result;
|
||||
string::size_type pos = s.find_first_not_of(separator, 0);
|
||||
|
||||
string quote = "\"";
|
||||
string inquotes = "";
|
||||
|
||||
while (pos != string::npos) {
|
||||
string::size_type end = s.find_first_of(separator, pos + 1);
|
||||
if (end == string::npos) end = s.size();
|
||||
string token(s, pos, end - pos);
|
||||
|
||||
pos = s.find_first_not_of(separator, end);
|
||||
|
||||
//if first or last item is a quote, we switch inquotes
|
||||
if(token.substr(0,1) == quote || token.substr(token.size()-1,token.size()) == quote){
|
||||
if(inquotes == ""){ //begin
|
||||
inquotes = token;
|
||||
continue;
|
||||
}
|
||||
else{ //end
|
||||
token = inquotes + token;
|
||||
token = token.substr(1,token.size()-2); //remove the grouping quotes
|
||||
inquotes = "";
|
||||
}
|
||||
}
|
||||
|
||||
if(inquotes != "") //middle
|
||||
inquotes += separator + token;
|
||||
else
|
||||
result.push_back(token); //normal
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
string statusToString(int status)
|
||||
{
|
||||
if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) {
|
||||
if (WIFEXITED(status))
|
||||
return (format("failed with exit code %1%") % WEXITSTATUS(status)).str();
|
||||
else if (WIFSIGNALED(status))
|
||||
return (format("failed due to signal %1%") % WTERMSIG(status)).str();
|
||||
else if (WIFSIGNALED(status)) {
|
||||
int sig = WTERMSIG(status);
|
||||
#if HAVE_STRSIGNAL
|
||||
const char * description = strsignal(sig);
|
||||
return (format("failed due to signal %1% (%2%)") % sig % description).str();
|
||||
#else
|
||||
return (format("failed due to signal %1%") % sig).str();
|
||||
#endif
|
||||
}
|
||||
else
|
||||
return "died abnormally";
|
||||
} else return "succeeded";
|
||||
@@ -976,6 +1067,12 @@ bool string2Int(const string & s, int & n)
|
||||
return str && str.get() == EOF;
|
||||
}
|
||||
|
||||
string unsignedInt2String(unsigned int n)
|
||||
{
|
||||
std::ostringstream str;
|
||||
str << n;
|
||||
return str.str();
|
||||
}
|
||||
|
||||
void ignoreException()
|
||||
{
|
||||
@@ -986,5 +1083,283 @@ void ignoreException()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
bool string2UnsignedInt(const string & s, unsigned int & n)
|
||||
{
|
||||
std::istringstream str(s);
|
||||
str >> n;
|
||||
return str && str.get() == EOF;
|
||||
}
|
||||
|
||||
string bool2string(const bool b)
|
||||
{
|
||||
if(b == true)
|
||||
return "true";
|
||||
else
|
||||
return "false";
|
||||
}
|
||||
|
||||
bool string2bool(const string & s)
|
||||
{
|
||||
if(s == "true")
|
||||
return true;
|
||||
else if(s == "false")
|
||||
return false;
|
||||
else{
|
||||
throw Error(format("cannot convert string: `%1%' to bool") % s);
|
||||
quickExit(1);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
string triml(const string & s) {
|
||||
string news = s;
|
||||
int pos(0);
|
||||
for ( ; news[pos]==' ' || news[pos]=='\t'; ++pos );
|
||||
news.erase(0, pos);
|
||||
return news;
|
||||
}
|
||||
|
||||
string trimr(const string & s) {
|
||||
string news = s;
|
||||
int pos(news.size());
|
||||
for ( ; pos && news[pos-1]==' ' || news[pos]=='\t'; --pos );
|
||||
news.erase(pos, news.size()-pos);
|
||||
return news;
|
||||
}
|
||||
|
||||
string trim(const string & s) {
|
||||
return triml(trimr(s));
|
||||
}
|
||||
|
||||
|
||||
//executes a shell command, captures and prints the output.
|
||||
void runProgram_AndPrintOutput(Path program, bool searchPath, const Strings & args, const string outputPrefix)
|
||||
{
|
||||
string program_output = runProgram(program, searchPath, args);
|
||||
|
||||
//Add the prefix on every line
|
||||
Strings lines = tokenizeString(program_output, "\n");
|
||||
for (Strings::const_iterator i = lines.begin(); i != lines.end(); ++i){
|
||||
if(trim(*i) != "")
|
||||
printMsg(lvlError, format("[%2%]: %1%") % *i % outputPrefix);
|
||||
}
|
||||
}
|
||||
|
||||
//executes a direct shell command (faster)
|
||||
void executeShellCommand(const string & command)
|
||||
{
|
||||
int kidstatus, deadpid;
|
||||
pid_t kidpid = fork();
|
||||
switch (kidpid) {
|
||||
case -1:
|
||||
throw SysError("unable to fork");
|
||||
case 0:
|
||||
try { // child
|
||||
int rv = system(command.c_str());
|
||||
//int rv = execlp(svnbin.c_str(), svnbin.c_str(), ">", tempoutput.c_str(), NULL); //TODO make this work ... ?
|
||||
|
||||
if (rv == -1) {
|
||||
throw SysError("executeShellCommand(..) error");
|
||||
quickExit(99);
|
||||
}
|
||||
quickExit(0);
|
||||
|
||||
} catch (std::exception & e) {
|
||||
std::cerr << format("executeShellCommand(..) child error: %1%\n") % e.what();
|
||||
quickExit(1);
|
||||
}
|
||||
}
|
||||
|
||||
deadpid = waitpid(kidpid, &kidstatus, 0);
|
||||
if (deadpid == -1) {
|
||||
std::cerr << format("state child waitpid error\n");
|
||||
quickExit(1);
|
||||
}
|
||||
}
|
||||
|
||||
unsigned int getTimeStamp()
|
||||
{
|
||||
const time_t now = time(0);
|
||||
unsigned int i = now;
|
||||
return i;
|
||||
}
|
||||
|
||||
//TODO Does this work on windows?
|
||||
bool FileExist(const string FileName)
|
||||
{
|
||||
const char* FileName_C = FileName.c_str();
|
||||
struct stat my_stat;
|
||||
if (stat(FileName_C, &my_stat) != 0) return false;
|
||||
return ((my_stat.st_mode & S_IFREG) != 0);
|
||||
|
||||
/*
|
||||
S_IFMT 0170000 bitmask for the file type bitfields
|
||||
S_IFSOCK 0140000 socket
|
||||
S_IFLNK 0120000 symbolic link
|
||||
S_IFREG 0100000 regular file
|
||||
S_IFBLK 0060000 block device
|
||||
S_IFDIR 0040000 directory
|
||||
S_IFCHR 0020000 character device
|
||||
S_IFIFO 0010000 fifo
|
||||
*/
|
||||
}
|
||||
|
||||
//TODO Does this work on windows?
|
||||
bool DirectoryExist(const string FileName)
|
||||
{
|
||||
const char* FileName_C = FileName.c_str();
|
||||
struct stat my_stat;
|
||||
if (stat(FileName_C, &my_stat) != 0) return false;
|
||||
return ((my_stat.st_mode & S_IFDIR) != 0);
|
||||
}
|
||||
|
||||
//TODO Does this work on windows?
|
||||
bool IsSymlink(const string FileName)
|
||||
{
|
||||
const char* FileName_C = FileName.c_str();
|
||||
struct stat my_stat;
|
||||
if (lstat(FileName_C, &my_stat) != 0) return false;
|
||||
return (S_ISLNK(my_stat.st_mode) != 0);
|
||||
}
|
||||
|
||||
/* adds the second PathSet after the first, but removing doubles from the second (union)
|
||||
* (We assume the first PathSet has no duplicates)
|
||||
* UNTESTED !!!!!!!!!!!!!!
|
||||
*/
|
||||
void pathSets_union_ordered(PathSet & originalPaths, const PathSet & newPaths)
|
||||
{
|
||||
for (PathSet::iterator i = newPaths.begin(); i != newPaths.end(); ++i){
|
||||
if (originalPaths.find(*i) != originalPaths.end())
|
||||
continue;
|
||||
originalPaths.insert(*i);
|
||||
}
|
||||
}
|
||||
|
||||
//merges two PathSets into one, removing doubles (union)
|
||||
PathSet pathSets_union(const PathSet & paths1, const PathSet & paths2)
|
||||
{
|
||||
vector<Path> vector1(paths1.begin(), paths1.end());
|
||||
vector<Path> vector2(paths2.begin(), paths2.end());
|
||||
vector<Path> setResult;
|
||||
|
||||
set_union(vector1.begin(), vector1.end(),vector2.begin(), vector2.end(), back_inserter(setResult)); //Also available: set_symmetric_difference and set_intersection
|
||||
|
||||
PathSet unionPaths;
|
||||
for(unsigned int i=0; i<setResult.size(); i++)
|
||||
unionPaths.insert(setResult[i]);
|
||||
|
||||
return unionPaths;
|
||||
}
|
||||
|
||||
void pathSets_difference(const PathSet & oldpaths, const PathSet & newpaths, PathSet & addedpaths, PathSet & removedpaths)
|
||||
{
|
||||
for (PathSet::iterator i = oldpaths.begin(); i != oldpaths.end(); ++i){
|
||||
bool exists = false;
|
||||
for (PathSet::iterator j = newpaths.begin(); j != newpaths.end(); ++j){
|
||||
if(*i == *j){
|
||||
exists = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(!exists)
|
||||
removedpaths.insert(*i);
|
||||
}
|
||||
|
||||
for (PathSet::iterator i = newpaths.begin(); i != newpaths.end(); ++i){
|
||||
bool exists = false;
|
||||
for (PathSet::iterator j = oldpaths.begin(); j != oldpaths.end(); ++j){
|
||||
if(*i == *j){
|
||||
exists = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(!exists)
|
||||
addedpaths.insert(*i);
|
||||
}
|
||||
}
|
||||
|
||||
void ensureDirExists(const Path & path)
|
||||
{
|
||||
Strings p_args;
|
||||
p_args.push_back("-p");
|
||||
p_args.push_back(path);
|
||||
runProgram_AndPrintOutput("mkdir", true, p_args, "mkdir"); //TODO ensurePath
|
||||
}
|
||||
|
||||
void setChown(const Path & pathOrFile, const string & user, const string & group, bool recursive)
|
||||
{
|
||||
Strings p_args;
|
||||
if(recursive)
|
||||
p_args.push_back("-R");
|
||||
p_args.push_back(user + "." + group);
|
||||
p_args.push_back(pathOrFile);
|
||||
runProgram_AndPrintOutput("chown", true, p_args, "chown");
|
||||
}
|
||||
|
||||
void setChmod(const Path & pathOrFile, const string & chmod)
|
||||
{
|
||||
Strings p_args;
|
||||
p_args.push_back(chmod);
|
||||
p_args.push_back(pathOrFile);
|
||||
runProgram_AndPrintOutput("chmod", true, p_args, "chmod");
|
||||
}
|
||||
|
||||
string padd(const string & s, char c , unsigned int size, bool front)
|
||||
{
|
||||
string ss = s;
|
||||
while (ss.length() < size){
|
||||
if(front)
|
||||
ss = c + ss;
|
||||
else
|
||||
ss = ss + c;
|
||||
}
|
||||
return ss;
|
||||
}
|
||||
|
||||
void symlinkPath(const Path & existingDir, const Path & newLinkName) //TODO bool shellexpansion
|
||||
{
|
||||
/*
|
||||
* Symlink link to the share path
|
||||
* Usage: ln [OPTION]... [-T] TARGET LINK_NAME (1st form)
|
||||
*
|
||||
* we call the shell (/bin/sh -c) so it expands the ~ to a users home dir
|
||||
*
|
||||
* We do -snf for:
|
||||
* -s : symlinking
|
||||
* -f : To remove existing destination files (this does NOT always overwrite the newLinkName !!!!)
|
||||
* -n : Treat destination that is a symlink to a directory as if it were a normal file:
|
||||
* When the destination is an actual directory (not a symlink to one), there is no ambiguity.
|
||||
* The link is created in that directory. But when the specified destination is a symlink to a directory,
|
||||
* there are two ways to treat the user's request. ln can treat the destination just as it would a normal
|
||||
* directory and create the link in it. On the other hand, the destination can be viewed as a non-directory
|
||||
* - as the symlink itself. In that case, ln must delete or backup that symlink before creating the new link.
|
||||
* The default is to treat a destination that is a symlink to a directory just like a directory.
|
||||
*
|
||||
* ((This makes sure that newLinkName is really overwritten)
|
||||
*/
|
||||
|
||||
|
||||
Strings p_args;
|
||||
p_args.push_back("-c");
|
||||
p_args.push_back("ln -snf " + existingDir + " " + newLinkName);
|
||||
runProgram_AndPrintOutput("/bin/sh", true, p_args, "sh-ln");
|
||||
printMsg(lvlError, format("ln -sf %1% %2%") % existingDir % newLinkName);
|
||||
}
|
||||
|
||||
void removeSymlink(const string & path)
|
||||
{
|
||||
if(path[path.length() - 1] == '/')
|
||||
throw Error(format("We dont want to remove the symlink, not the enitre directory, but a / is given for `%1%'") % path);
|
||||
deletePath(path);
|
||||
}
|
||||
|
||||
void ensureStateDir(const Path & statePath, const string & user, const string & group, const string & chmod)
|
||||
{
|
||||
ensureDirExists(statePath);
|
||||
setChown(statePath, user, group);
|
||||
setChmod(statePath, chmod);
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -36,9 +36,18 @@ Path dirOf(const Path & path);
|
||||
following the final `/'. */
|
||||
string baseNameOf(const Path & path);
|
||||
|
||||
/* Return true iff the given path exists. */
|
||||
/* Return true if the given path (dir of file) exists. */
|
||||
bool pathExists(const Path & path);
|
||||
|
||||
/* Return true if the given file exists. */
|
||||
bool FileExist(const string FileName);
|
||||
|
||||
/* Return true if the given filename is a dir. */
|
||||
bool DirectoryExist(const string FileName);
|
||||
|
||||
/* Return true if the given filename is a symlink. */
|
||||
bool IsSymlink(const string FileName);
|
||||
|
||||
/* Read the contents (target) of a symbolic link. The result is not
|
||||
in any way canonicalised. */
|
||||
Path readLink(const Path & path);
|
||||
@@ -70,10 +79,11 @@ void deletePath(const Path & path, unsigned long long & bytesFreed);
|
||||
void makePathReadOnly(const Path & path);
|
||||
|
||||
/* Create a temporary directory. */
|
||||
Path createTempDir(const Path & tmpRoot = "");
|
||||
Path createTempDir(const Path & tmpRoot = "", const Path & prefix = "nix");
|
||||
|
||||
/* Create a directory and all its parents, if necessary. */
|
||||
void createDirs(const Path & path);
|
||||
/* Create a directory and all its parents, if necessary. Returns the
|
||||
list of created directories, in order of creation. */
|
||||
Paths createDirs(const Path & path);
|
||||
|
||||
/* Create a file and write the given text to it. The file is written
|
||||
in binary mode (i.e., no end-of-line conversions). The path should
|
||||
@@ -166,8 +176,9 @@ class AutoDelete
|
||||
{
|
||||
Path path;
|
||||
bool del;
|
||||
bool recursive;
|
||||
public:
|
||||
AutoDelete(const Path & p);
|
||||
AutoDelete(const Path & p, bool recursive = true);
|
||||
~AutoDelete();
|
||||
void cancel();
|
||||
};
|
||||
@@ -263,10 +274,11 @@ MakeError(Interrupted, BaseError)
|
||||
string packStrings(const Strings & strings);
|
||||
Strings unpackStrings(const string & s);
|
||||
|
||||
|
||||
/* String tokenizer. */
|
||||
Strings tokenizeString(const string & s, const string & separators = " \t\n\r");
|
||||
|
||||
/* String tokenizer for commandline agruments. */
|
||||
//Strings tokenizeStringWithQuotes(const string & s); //TODO maybe remove the function
|
||||
|
||||
/* Convert the exit status of a child as returned by wait() into an
|
||||
error string. */
|
||||
@@ -279,13 +291,60 @@ bool statusOk(int status);
|
||||
string int2String(int n);
|
||||
bool string2Int(const string & s, int & n);
|
||||
|
||||
/* */
|
||||
bool string2UnsignedInt(const string & s, unsigned int & n);
|
||||
string unsignedInt2String(unsigned int n);
|
||||
|
||||
/* Parse a bool to a string and back */
|
||||
string bool2string(const bool b);
|
||||
bool string2bool(const string & s);
|
||||
|
||||
//return modified string s with spaces trimmed from left
|
||||
string triml(const string & s);
|
||||
//return modified string s with spaces trimmed from right
|
||||
string trimr(const string & s);
|
||||
//return modified string s with spaces trimmed from edges
|
||||
string trim(const string & s);
|
||||
|
||||
//excecute a shell command
|
||||
void executeShellCommand(const string & command);
|
||||
|
||||
//
|
||||
void runProgram_AndPrintOutput(Path program, bool searchPath, const Strings & args, const string outputPrefix);
|
||||
|
||||
unsigned int getTimeStamp();
|
||||
|
||||
//string getCallingUserName();
|
||||
|
||||
/* Merges two PathSets into one, removing doubles (union) */
|
||||
PathSet pathSets_union(const PathSet & paths1, const PathSet & paths2);
|
||||
|
||||
/* TODO UNTESTED !!!!!!!!!!!!!! */
|
||||
void pathSets_union_ordered(PathSet & originalPaths, const PathSet & newPaths);
|
||||
|
||||
/* TODO */
|
||||
void pathSets_difference(const PathSet & oldpaths, const PathSet & newpaths, PathSet & addedpaths, PathSet & removedpaths);
|
||||
|
||||
/* TODO */
|
||||
void ensureDirExists(const Path & path);
|
||||
|
||||
/* TODO */
|
||||
void setChown(const Path & pathOrFile, const string & user, const string & group, bool recursive = false);
|
||||
void setChmod(const Path & pathOrFile, const string & chmod);
|
||||
|
||||
string padd(const string & s, char c , unsigned int size, bool front = false);
|
||||
|
||||
/* Symlinks one path to the other */
|
||||
void symlinkPath(const Path & existingDir, const Path & newLinkName);
|
||||
|
||||
void removeSymlink(const string & path);
|
||||
|
||||
void ensureStateDir(const Path & statePath, const string & user, const string & group, const string & chmod);
|
||||
|
||||
/* Exception handling in destructors: print an error message, then
|
||||
ignore the exception. */
|
||||
void ignoreException();
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
#endif /* !__UTIL_H */
|
||||
|
||||
@@ -9,6 +9,7 @@ Operations:
|
||||
--set: create a user environment containing a single derivation
|
||||
--uninstall / -e: remove derivations from the user environment
|
||||
--query / -q: perform a query on an environment or Nix expression
|
||||
--set-flag NAME VALUE: set derivation meta-attribute to given value
|
||||
|
||||
The previous operations take a list of derivation names. The special
|
||||
name `*' may be used to indicate all derivations.
|
||||
@@ -20,8 +21,6 @@ name `*' may be used to indicate all derivations.
|
||||
--delete-generations GENERATIONS...: deleted listed generations,
|
||||
`old' for all non-current generations
|
||||
|
||||
--import / -I FILE: set default Nix expression
|
||||
|
||||
--version: output version information
|
||||
--help: display help
|
||||
|
||||
@@ -55,7 +54,7 @@ Query flags:
|
||||
--xml: show output in XML format
|
||||
--status / -s: print installed/present status
|
||||
--no-name: hide derivation names
|
||||
--attr / -A: shows the unambiguous attribute name of the
|
||||
--attr-path / -P: shows the unambiguous attribute name of the
|
||||
derivation which can be used when installing with -A
|
||||
--system: print the platform type of the derivation
|
||||
--compare-versions / -c: compare version to available or installed
|
||||
@@ -63,8 +62,6 @@ Query flags:
|
||||
--out-path: print path of derivation output
|
||||
--description: print description
|
||||
--meta: print all meta attributes (only with --xml)
|
||||
--prebuilt-only: only show derivations whose prebuilt binaries are
|
||||
available on this machine or are downloadable
|
||||
|
||||
Options:
|
||||
|
||||
@@ -74,3 +71,5 @@ Options:
|
||||
--keep-failed / -K: keep temporary directories of failed builds
|
||||
--preserve-installed: do not replace currently installed versions in `-i'
|
||||
--system-filter SYSTEM: only use derivations for specified platform
|
||||
--prebuilt-only / -b: only use derivations whose prebuilt binaries are
|
||||
available on this machine or are downloadable
|
||||
|
||||
@@ -15,6 +15,8 @@
|
||||
#include "store-api.hh"
|
||||
#include "db.hh"
|
||||
#include "util.hh"
|
||||
#include "local-store.hh"
|
||||
#include "store-state.hh"
|
||||
|
||||
#include <cerrno>
|
||||
#include <ctime>
|
||||
@@ -47,8 +49,9 @@ struct InstallSourceInfo
|
||||
Path nixExprPath; /* for srcNixExprDrvs, srcNixExprs */
|
||||
Path profile; /* for srcProfile */
|
||||
string systemFilter; /* for srcNixExprDrvs */
|
||||
bool prebuiltOnly;
|
||||
ATermMap autoArgs;
|
||||
InstallSourceInfo() : autoArgs() { };
|
||||
InstallSourceInfo() : prebuiltOnly(false) { };
|
||||
};
|
||||
|
||||
|
||||
@@ -94,6 +97,8 @@ static bool parseInstallSourceOptions(Globals & globals,
|
||||
}
|
||||
else if (arg == "--attr" || arg == "-A")
|
||||
globals.instSource.type = srcAttrPath;
|
||||
else if (arg == "--prebuilt-only" || arg == "-b")
|
||||
globals.instSource.prebuiltOnly = true;
|
||||
else return false;
|
||||
return true;
|
||||
}
|
||||
@@ -223,8 +228,7 @@ static void createUserEnv(EvalState & state, const DrvInfos & elems,
|
||||
i != elems.end(); ++i)
|
||||
/* Call to `isDerivation' is for compatibility with Nix <= 0.7
|
||||
user environments. */
|
||||
if (i->queryDrvPath(state) != "" &&
|
||||
isDerivation(i->queryDrvPath(state)))
|
||||
if (i->queryDrvPath(state) != "" && isDerivation(i->queryDrvPath(state)))
|
||||
drvsToBuild.insert(i->queryDrvPath(state));
|
||||
|
||||
debug(format("building user environment dependencies"));
|
||||
@@ -238,6 +242,7 @@ static void createUserEnv(EvalState & state, const DrvInfos & elems,
|
||||
PathSet references;
|
||||
ATermList manifest = ATempty;
|
||||
ATermList inputs = ATempty;
|
||||
ATermList stateIdentifiers = ATempty;
|
||||
for (DrvInfos::const_iterator i = elems.begin();
|
||||
i != elems.end(); ++i)
|
||||
{
|
||||
@@ -246,7 +251,7 @@ static void createUserEnv(EvalState & state, const DrvInfos & elems,
|
||||
the meta attributes. */
|
||||
Path drvPath = keepDerivations ? i->queryDrvPath(state) : "";
|
||||
|
||||
ATermList as = ATmakeList4(
|
||||
ATermList as = ATmakeList5(
|
||||
makeBind(toATerm("type"),
|
||||
makeStr("derivation"), makeNoPos()),
|
||||
makeBind(toATerm("name"),
|
||||
@@ -254,18 +259,21 @@ static void createUserEnv(EvalState & state, const DrvInfos & elems,
|
||||
makeBind(toATerm("system"),
|
||||
makeStr(i->system), makeNoPos()),
|
||||
makeBind(toATerm("outPath"),
|
||||
makeStr(i->queryOutPath(state)), makeNoPos()));
|
||||
makeStr(i->queryOutPath(state)), makeNoPos()),
|
||||
makeBind(toATerm("stateIdentifier"),
|
||||
makeStr(i->queryStateIdentifier(state)), makeNoPos()) );
|
||||
|
||||
if (drvPath != "") as = ATinsert(as,
|
||||
makeBind(toATerm("drvPath"),
|
||||
makeStr(drvPath), makeNoPos()));
|
||||
if (drvPath != "")
|
||||
as = ATinsert(as, makeBind(toATerm("drvPath"), makeStr(drvPath), makeNoPos()));
|
||||
|
||||
if (i->attrs->get(toATerm("meta"))) as = ATinsert(as,
|
||||
makeBind(toATerm("meta"),
|
||||
strictEvalExpr(state, i->attrs->get(toATerm("meta"))),
|
||||
makeNoPos()));
|
||||
if (i->attrs->get(toATerm("meta")))
|
||||
as = ATinsert(as, makeBind(toATerm("meta"),
|
||||
strictEvalExpr(state, i->attrs->get(toATerm("meta"))), makeNoPos()));
|
||||
|
||||
manifest = ATinsert(manifest, makeAttrs(as));
|
||||
manifest = ATinsert(manifest, makeAttrs(as)); //All DrvInfo's are inserted here
|
||||
|
||||
//Insert the new stateIdentifier into the stateIdentifiers Atermlist
|
||||
stateIdentifiers = ATinsert(stateIdentifiers, makeStr(i->queryStateIdentifier(state)));
|
||||
|
||||
inputs = ATinsert(inputs, makeStr(i->queryOutPath(state)));
|
||||
|
||||
@@ -273,31 +281,46 @@ static void createUserEnv(EvalState & state, const DrvInfos & elems,
|
||||
`nix-env -i /nix/store/abcd...-foo'. */
|
||||
store->addTempRoot(i->queryOutPath(state));
|
||||
store->ensurePath(i->queryOutPath(state));
|
||||
|
||||
references.insert(i->queryOutPath(state));
|
||||
if (drvPath != "") references.insert(drvPath);
|
||||
|
||||
if (drvPath != "")
|
||||
references.insert(drvPath);
|
||||
}
|
||||
|
||||
//printMsg(lvlError, format("TEST '%1%'") % atPrint(canonicaliseExpr(makeList(ATreverse(stateIdentifiers)))) );
|
||||
//printMsg(lvlError, format("TEST '%1%'") % atPrint(canonicaliseExpr(makeList(ATreverse(runtimeStateArgs)))) );
|
||||
|
||||
/* Also write a copy of the list of inputs to the store; we need
|
||||
it for future modifications of the environment. */
|
||||
Path manifestFile = store->addTextToStore("env-manifest",
|
||||
atPrint(canonicaliseExpr(makeList(ATreverse(manifest)))), references);
|
||||
Path manifestFile = store->addTextToStore("env-manifest", atPrint(canonicaliseExpr(makeList(ATreverse(manifest)))), references);
|
||||
|
||||
Expr topLevel = makeCall(envBuilder, makeAttrs(ATmakeList3(
|
||||
makeBind(toATerm("system"),
|
||||
makeStr(thisSystem), makeNoPos()),
|
||||
makeBind(toATerm("derivations"),
|
||||
makeList(ATreverse(manifest)), makeNoPos()),
|
||||
makeBind(toATerm("manifest"),
|
||||
makeStr(manifestFile, singleton<PathSet>(manifestFile)), makeNoPos())
|
||||
)));
|
||||
Expr topLevel = makeCall(envBuilder, makeAttrs(
|
||||
|
||||
ATinsert(
|
||||
ATmakeList5(
|
||||
makeBind(toATerm("system"),
|
||||
makeStr(thisSystem), makeNoPos()),
|
||||
makeBind(toATerm("derivations"),
|
||||
makeList(ATreverse(manifest)), makeNoPos()),
|
||||
makeBind(toATerm("stateIdentifiers"),
|
||||
makeList(ATreverse(stateIdentifiers)), makeNoPos()),
|
||||
makeBind(toATerm("manifest"),
|
||||
makeStr(manifestFile, singleton<PathSet>(manifestFile)), makeNoPos()),
|
||||
makeBind(toATerm("nixBinDir"),
|
||||
makeStr(nixBinDir), makeNoPos()) )
|
||||
,
|
||||
makeBind(toATerm("nixStore"),
|
||||
makeStr(nixStore), makeNoPos())
|
||||
)
|
||||
|
||||
));
|
||||
|
||||
/* Instantiate it. */
|
||||
debug(format("evaluating builder expression `%1%'") % topLevel);
|
||||
DrvInfo topLevelDrv;
|
||||
if (!getDerivation(state, topLevel, topLevelDrv))
|
||||
abort();
|
||||
|
||||
|
||||
/* Realise the resulting store expression. */
|
||||
debug(format("building user environment"));
|
||||
store->buildDerivations(singleton<PathSet>(topLevelDrv.queryDrvPath(state)));
|
||||
@@ -319,9 +342,16 @@ static int comparePriorities(EvalState & state,
|
||||
}
|
||||
|
||||
|
||||
static DrvInfos filterBySelector(EvalState & state,
|
||||
const DrvInfos & allElems,
|
||||
const Strings & args, bool newestOnly)
|
||||
static bool isPrebuilt(EvalState & state, const DrvInfo & elem)
|
||||
{
|
||||
return
|
||||
store->isValidPath(elem.queryOutPath(state)) ||
|
||||
store->hasSubstitutes(elem.queryOutPath(state));
|
||||
}
|
||||
|
||||
|
||||
static DrvInfos filterBySelector(EvalState & state, const DrvInfos & allElems,
|
||||
const Strings & args, bool newestOnly, bool prebuiltOnly)
|
||||
{
|
||||
DrvNames selectors = drvNamesFromArgs(args);
|
||||
|
||||
@@ -340,7 +370,8 @@ static DrvInfos filterBySelector(EvalState & state,
|
||||
DrvName drvName(j->name);
|
||||
if (i->matches(drvName)) {
|
||||
i->hits++;
|
||||
matches.push_back(std::pair<DrvInfo, unsigned int>(*j, n));
|
||||
if (!prebuiltOnly || isPrebuilt(state, *j))
|
||||
matches.push_back(std::pair<DrvInfo, unsigned int>(*j, n));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -399,7 +430,7 @@ static DrvInfos filterBySelector(EvalState & state,
|
||||
/* Check that all selectors have been used. */
|
||||
for (DrvNames::iterator i = selectors.begin();
|
||||
i != selectors.end(); ++i)
|
||||
if (i->hits == 0)
|
||||
if (i->hits == 0 && i->fullName != "*")
|
||||
throw Error(format("selector `%1%' matches no derivations")
|
||||
% i->fullName);
|
||||
|
||||
@@ -407,12 +438,18 @@ static DrvInfos filterBySelector(EvalState & state,
|
||||
}
|
||||
|
||||
|
||||
static bool isPath(const string & s)
|
||||
{
|
||||
return s.find('/') != string::npos;
|
||||
}
|
||||
|
||||
|
||||
static void queryInstSources(EvalState & state,
|
||||
const InstallSourceInfo & instSource, const Strings & args,
|
||||
DrvInfos & elems, bool newestOnly)
|
||||
{
|
||||
InstallSourceType type = instSource.type;
|
||||
if (type == srcUnknown && args.size() > 0 && args.front()[0] == '/')
|
||||
if (type == srcUnknown && args.size() > 0 && isPath(args.front()))
|
||||
type = srcStorePaths;
|
||||
|
||||
switch (type) {
|
||||
@@ -429,7 +466,8 @@ static void queryInstSources(EvalState & state,
|
||||
loadDerivations(state, instSource.nixExprPath,
|
||||
instSource.systemFilter, instSource.autoArgs, "", allElems);
|
||||
|
||||
elems = filterBySelector(state, allElems, args, newestOnly);
|
||||
elems = filterBySelector(state, allElems, args,
|
||||
newestOnly, instSource.prebuiltOnly);
|
||||
|
||||
break;
|
||||
}
|
||||
@@ -463,23 +501,24 @@ static void queryInstSources(EvalState & state,
|
||||
for (Strings::const_iterator i = args.begin();
|
||||
i != args.end(); ++i)
|
||||
{
|
||||
assertStorePath(*i);
|
||||
Path path = followLinksToStorePath(*i);
|
||||
|
||||
DrvInfo elem;
|
||||
elem.attrs = boost::shared_ptr<ATermMap>(new ATermMap(0)); /* ugh... */
|
||||
string name = baseNameOf(*i);
|
||||
elem.attrs = boost::shared_ptr<ATermMap>(new ATermMap(0)); /* ugh... */
|
||||
string name = baseNameOf(path);
|
||||
string::size_type dash = name.find('-');
|
||||
if (dash != string::npos)
|
||||
name = string(name, dash + 1);
|
||||
|
||||
if (isDerivation(*i)) {
|
||||
elem.setDrvPath(*i);
|
||||
elem.setOutPath(findOutput(derivationFromPath(*i), "out"));
|
||||
if (isDerivation(path)) {
|
||||
elem.setDrvPath(path);
|
||||
elem.setOutPath(findOutput(derivationFromPath(path), "out"));
|
||||
|
||||
if (name.size() >= drvExtension.size() &&
|
||||
string(name, name.size() - drvExtension.size()) == drvExtension)
|
||||
name = string(name, 0, name.size() - drvExtension.size());
|
||||
}
|
||||
else elem.setOutPath(*i);
|
||||
else elem.setOutPath(path);
|
||||
|
||||
elem.name = name;
|
||||
|
||||
@@ -495,7 +534,7 @@ static void queryInstSources(EvalState & state,
|
||||
case srcProfile: {
|
||||
elems = filterBySelector(state,
|
||||
queryInstalled(state, instSource.profile),
|
||||
args, newestOnly);
|
||||
args, newestOnly, instSource.prebuiltOnly);
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -556,12 +595,36 @@ static void installDerivations(Globals & globals,
|
||||
DrvInfos newElems;
|
||||
queryInstSources(globals.state, globals.instSource, args, newElems, true);
|
||||
|
||||
StringPairs externalStates; //Mapping from statePath --> some path outide the store
|
||||
|
||||
StringSet newNames;
|
||||
for (DrvInfos::iterator i = newElems.begin(); i != newElems.end(); ++i) {
|
||||
/* `forceName' is a hack to get package names right in some
|
||||
one-click installs, namely those where the name used in the
|
||||
path is not the one we want (e.g., `java-front' versus
|
||||
`java-front-0.9pre15899'). */
|
||||
|
||||
//Set the state indentifier if it is a state-component
|
||||
printMsg(lvlError, format("New component to install DRV: '%1%'") % i->queryDrvPath(globals.state));
|
||||
Derivation drv = derivationFromPath(i->queryDrvPath(globals.state));
|
||||
if(isStateDrv(drv))
|
||||
{
|
||||
DerivationStateOutputs stateOutputs = drv.stateOutputs;
|
||||
string stateIdentifier = stateOutputs.find("state")->second.stateIdentifier;
|
||||
printMsg(lvlError, format("Add state component '%2%' with identifier '%1%'") % stateIdentifier % i->queryOutPath(globals.state));
|
||||
if(stateIdentifier == "")
|
||||
i->setStateIdentifier("__EMTPY__");
|
||||
else
|
||||
i->setStateIdentifier(stateIdentifier);
|
||||
|
||||
//Later on we need to link the sharedState path out of the store to the statePath inside the state-store
|
||||
string externalState = stateOutputs.find("state")->second.externalState;
|
||||
Path statePath = stateOutputs.find("state")->second.statepath;
|
||||
if(externalState != ""){
|
||||
externalStates[statePath] = externalState;
|
||||
}
|
||||
}
|
||||
|
||||
if (globals.forceName != "")
|
||||
i->name = globals.forceName;
|
||||
newNames.insert(DrvName(i->name).name);
|
||||
@@ -573,24 +636,72 @@ static void installDerivations(Globals & globals,
|
||||
lockProfile(lock, profile);
|
||||
DrvInfos installedElems = queryInstalled(globals.state, profile);
|
||||
|
||||
|
||||
StringPairs toBeShared; //Mapping from old --> new statePath
|
||||
|
||||
DrvInfos allElems(newElems);
|
||||
for (DrvInfos::iterator i = installedElems.begin();
|
||||
i != installedElems.end(); ++i)
|
||||
for (DrvInfos::iterator i = installedElems.begin(); i != installedElems.end(); ++i)
|
||||
{
|
||||
DrvName drvName(i->name);
|
||||
MetaInfo meta = i->queryMetaInfo(globals.state);
|
||||
if (!globals.preserveInstalled &&
|
||||
newNames.find(drvName.name) != newNames.end() &&
|
||||
meta["keep"] != "true")
|
||||
printMsg(lvlInfo,
|
||||
format("replacing old `%1%'") % i->name);
|
||||
|
||||
//We may need to share state
|
||||
if (!globals.preserveInstalled &&
|
||||
newNames.find(drvName.name) != newNames.end() &&
|
||||
meta["keep"] != "true"
|
||||
){
|
||||
|
||||
//******** We're gonna check if the component and state indentifiers are the same,
|
||||
// since we may need to share state in that case.
|
||||
|
||||
string oldStateIdentifier = i->queryStateIdentifier(globals.state); //get the old stateIdentifier
|
||||
if(oldStateIdentifier != "__NOSTATE__")
|
||||
{
|
||||
//get the new stateIdentifier
|
||||
string newStateIdentifier;
|
||||
string newStatePath;
|
||||
string newSharedState;
|
||||
for (DrvInfos::iterator j = newElems.begin(); j != newElems.end(); ++j) {
|
||||
DrvName newDrvName(j->name);
|
||||
if(newDrvName.name == drvName.name){
|
||||
Derivation newDrv = derivationFromPath(j->queryDrvPath(globals.state));
|
||||
DerivationStateOutputs newStateOutputs = newDrv.stateOutputs;
|
||||
newStateIdentifier = newStateOutputs.find("state")->second.stateIdentifier;
|
||||
newStatePath = newDrv.stateOutputs.find("state")->second.statepath;
|
||||
newSharedState = newDrv.stateOutputs.find("state")->second.sharedState;
|
||||
break;
|
||||
}
|
||||
}
|
||||
//printMsg(lvlError, format("old '%1%' new '%2%'") % oldStateIdentifier % newStateIdentifier);
|
||||
|
||||
//if it doesnt need to share state with some other component
|
||||
//&& the identifiers are equal
|
||||
if( newSharedState == ""
|
||||
&&
|
||||
(oldStateIdentifier == newStateIdentifier || (oldStateIdentifier == "__EMTPY__" && newStateIdentifier == ""))
|
||||
){
|
||||
|
||||
//query old state path
|
||||
Path oldStatePath = store->lookupStatePath(i->queryOutPath(globals.state), newStateIdentifier, queryCurrentUsername());
|
||||
|
||||
//SharePaths
|
||||
printMsg(lvlError, format("Sharing state from old <-- new component '%1%' <-- '%2%'") % oldStatePath % newStatePath);
|
||||
toBeShared[oldStatePath] = newStatePath;
|
||||
}
|
||||
else{ //If not equal, then we do not replace, so we push back (just like the else branch)
|
||||
printMsg(lvlError, format("Installing new state-component component '%1%' with identifier '%2%'") % drvName.name % newStateIdentifier);
|
||||
allElems.push_back(*i);
|
||||
}
|
||||
}
|
||||
else
|
||||
printMsg(lvlInfo, format("replacing old `%1%'") % i->name);
|
||||
}
|
||||
else
|
||||
allElems.push_back(*i);
|
||||
}
|
||||
|
||||
for (DrvInfos::iterator i = newElems.begin(); i != newElems.end(); ++i)
|
||||
printMsg(lvlInfo,
|
||||
format("installing `%1%'") % i->name);
|
||||
printMsg(lvlInfo, format("installing `%1%'") % i->name);
|
||||
|
||||
if (globals.dryRun) {
|
||||
printMissing(globals.state, newElems);
|
||||
@@ -599,6 +710,63 @@ static void installDerivations(Globals & globals,
|
||||
|
||||
createUserEnv(globals.state, allElems,
|
||||
profile, globals.keepDerivations);
|
||||
|
||||
//After all components have been built succesfully, share their state paths with the old ones
|
||||
for (StringPairs::iterator i = toBeShared.begin(); i != toBeShared.end(); ++i){
|
||||
|
||||
printMsg(lvlError, format("Sharing state from old <-- new component '%1%' <-- '%2%'") % i->first % i->second);
|
||||
|
||||
//Share from new --> to existing
|
||||
store->shareState(i->second, i->first, false);
|
||||
}
|
||||
|
||||
//**********************
|
||||
|
||||
//Let the stateDirs in /nix/state point to the solidStateDependencies
|
||||
for (StringPairs::iterator i = externalStates.begin(); i != externalStates.end(); ++i){
|
||||
|
||||
Path statePath = i->first;
|
||||
Path externalState = i->second;
|
||||
|
||||
//1. If dir externalState exists, we move its data into the statePath
|
||||
//2. We ensure that the parent dir of externalState exists so we can create a symlink
|
||||
if(DirectoryExist(externalState)){
|
||||
//We cannot copy into itself so we have to test that
|
||||
if(IsSymlink(externalState)){
|
||||
Path read_statePath = readLink(externalState);
|
||||
assert(store->isValidStatePath(read_statePath));
|
||||
PathSet comparePaths;
|
||||
comparePaths.insert(statePath);
|
||||
comparePaths.insert(read_statePath);
|
||||
if(store->toNonSharedPathSet(comparePaths).size() != 1)
|
||||
rsyncPaths(externalState, statePath, true);
|
||||
}
|
||||
else
|
||||
rsyncPaths(externalState, statePath, true);
|
||||
|
||||
deletePath(externalState);
|
||||
}
|
||||
else{
|
||||
//Ensure parent dir
|
||||
string externalState_p = externalState;
|
||||
if(externalState_p[externalState_p.length() - 1] == '/')
|
||||
externalState_p.erase(externalState_p.length(),1);
|
||||
externalState_p = externalState_p.substr(0,externalState_p.find_last_of('/'));
|
||||
ensureDirExists(externalState_p);
|
||||
//printMsg(lvlError, format("EnsureDir: '%1%'") % externalState_p);
|
||||
}
|
||||
|
||||
//Now we create a symlink externalState --> statePath
|
||||
printMsg(lvlError, format("SYMLINK: '%1%' --> '%2%'") % externalState % statePath); //TODO !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! DOESNT WORK OK >.....
|
||||
executeShellCommand("whoami");
|
||||
symlinkPath(statePath, externalState);
|
||||
}
|
||||
|
||||
//**********************
|
||||
|
||||
|
||||
//TODO !!!!!!!!!!!!!!!!!!!!!!!
|
||||
//Remove the external links when a user uninstalls
|
||||
}
|
||||
|
||||
|
||||
@@ -620,6 +788,7 @@ static void opInstall(Globals & globals,
|
||||
typedef enum { utLt, utLeq, utEq, utAlways } UpgradeType;
|
||||
|
||||
|
||||
//TODO !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
|
||||
static void upgradeDerivations(Globals & globals,
|
||||
const Strings & args, UpgradeType upgradeType)
|
||||
{
|
||||
@@ -799,7 +968,7 @@ static void opSet(Globals & globals,
|
||||
}
|
||||
|
||||
|
||||
static void uninstallDerivations(Globals & globals, DrvNames & selectors,
|
||||
static void uninstallDerivations(Globals & globals, Strings & selectors,
|
||||
Path & profile)
|
||||
{
|
||||
PathLocks lock;
|
||||
@@ -812,11 +981,13 @@ static void uninstallDerivations(Globals & globals, DrvNames & selectors,
|
||||
{
|
||||
DrvName drvName(i->name);
|
||||
bool found = false;
|
||||
for (DrvNames::iterator j = selectors.begin();
|
||||
j != selectors.end(); ++j)
|
||||
if (j->matches(drvName)) {
|
||||
printMsg(lvlInfo,
|
||||
format("uninstalling `%1%'") % i->name);
|
||||
for (Strings::iterator j = selectors.begin(); j != selectors.end(); ++j)
|
||||
/* !!! the repeated calls to followLinksToStorePath() are
|
||||
expensive, should pre-compute them. */
|
||||
if ((isPath(*j) && i->queryOutPath(globals.state) == followLinksToStorePath(*j))
|
||||
|| DrvName(*j).matches(drvName))
|
||||
{
|
||||
printMsg(lvlInfo, format("uninstalling `%1%'") % i->name);
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
@@ -835,11 +1006,7 @@ static void opUninstall(Globals & globals,
|
||||
{
|
||||
if (opFlags.size() > 0)
|
||||
throw UsageError(format("unknown flag `%1%'") % opFlags.front());
|
||||
|
||||
DrvNames drvNames = drvNamesFromArgs(opArgs);
|
||||
|
||||
uninstallDerivations(globals, drvNames,
|
||||
globals.profile);
|
||||
uninstallDerivations(globals, opArgs, globals.profile);
|
||||
}
|
||||
|
||||
|
||||
@@ -999,7 +1166,7 @@ static void opQuery(Globals & globals,
|
||||
|
||||
DrvInfos elems = filterBySelector(globals.state,
|
||||
source == sInstalled ? installedElems : availElems,
|
||||
remaining, false);
|
||||
remaining, false, prebuiltOnly);
|
||||
|
||||
DrvInfos & otherElems(source == sInstalled ? availElems : installedElems);
|
||||
|
||||
@@ -1040,12 +1207,6 @@ static void opQuery(Globals & globals,
|
||||
/* For XML output. */
|
||||
XMLAttrs attrs;
|
||||
|
||||
if (prebuiltOnly) {
|
||||
if (!store->isValidPath(i->queryOutPath(globals.state)) &&
|
||||
!store->hasSubstitutes(i->queryOutPath(globals.state)))
|
||||
continue;
|
||||
}
|
||||
|
||||
if (printStatus) {
|
||||
bool hasSubs = store->hasSubstitutes(i->queryOutPath(globals.state));
|
||||
bool isInstalled = installed.find(i->queryOutPath(globals.state)) != installed.end();
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user