From e4d4969ae929682aea936e035cc24d56949a82ba Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Thu, 12 Jan 2006 15:17:51 +0000 Subject: [PATCH] * New tools nix-pack-closure and nix-unpack-closure. These provide a useful way to transfer the closure of a store path to another machine. These commands provide functionality previously possible through `nix-push --copy'. However, they are much more convenient in many situations (though possibly less efficient). Example: $ nix-pack-closure /nix/store/hj232g1r...-subversion-1.3.0 > svn.closure (on another machine:) $ nix-unpack-closure < svn.closure Note that Subversion is added to the store, but not installed into a user environment. One should do `nix-env -i /nix/store/hj232g1r...-subversion-1.3.0' for that. Another example: copy the application Azureus to the machine `scratchy' through ssh: $ nix-pack-closure $(which azureus) | ssh scratchy nix-unpack-closure --- scripts/Makefile.am | 6 ++- scripts/nix-pack-closure.in | 67 +++++++++++++++++++++++++++++ scripts/nix-unpack-closure.in | 81 +++++++++++++++++++++++++++++++++++ 3 files changed, 152 insertions(+), 2 deletions(-) create mode 100644 scripts/nix-pack-closure.in create mode 100644 scripts/nix-unpack-closure.in diff --git a/scripts/Makefile.am b/scripts/Makefile.am index 740110d4..428d3fa6 100644 --- a/scripts/Makefile.am +++ b/scripts/Makefile.am @@ -1,6 +1,7 @@ bin_SCRIPTS = nix-collect-garbage \ nix-pull nix-push nix-prefetch-url \ - nix-install-package nix-channel nix-build + nix-install-package nix-channel nix-build \ + nix-pack-closure nix-unpack-closure noinst_SCRIPTS = nix-profile.sh generate-patches.pl @@ -23,4 +24,5 @@ EXTRA_DIST = nix-collect-garbage.in \ readmanifest.pm.in \ nix-build.in \ download-using-manifests.pl.in \ - generate-patches.pl.in + generate-patches.pl.in \ + nix-pack-closure.in nix-unpack-closure.in diff --git a/scripts/nix-pack-closure.in b/scripts/nix-pack-closure.in new file mode 100644 index 00000000..8523c61d --- /dev/null +++ b/scripts/nix-pack-closure.in @@ -0,0 +1,67 @@ +#! @perl@ -w + +# This tool computes the closure of a path (using "nix-store --query +# --requisites") and puts the contents of each path in the closure in +# a big NAR archive that can be installed on another Nix installation +# using "nix-unpack-closure". + +# TODO: make this program "streamy", i.e., don't use a temporary +# directory. + +use strict; +use POSIX qw(tmpnam); + +my $binDir = $ENV{"NIX_BIN_DIR"}; +$binDir = "@bindir@" unless defined $binDir; + +my $tmpDir; +do { $tmpDir = tmpnam(); } +until mkdir $tmpDir, 0777; +END { system "rm -rf '$tmpDir'"; } +mkdir "$tmpDir/contents", 0777 or die; +mkdir "$tmpDir/references", 0777 or die; +mkdir "$tmpDir/derivers", 0777 or die; + + + +my %storePaths; + + +while (@ARGV) { + my $storePath = shift @ARGV; + + # Get the closure of this path. + my $pid = open(READ, + "$binDir/nix-store --query --requisites " . + "--force-realise '$storePath'|") or die; + + while () { + chomp; + die "bad: $_" unless /^\//; + $storePaths{$_} = ""; + } + + close READ or die "nix-store failed: $?"; +} + + +foreach my $storePath (sort(keys %storePaths)) { + print STDERR "packing `$storePath'...\n"; + + $storePath =~ /\/([^\/]+)$/; + my $name = $1; + + system("$binDir/nix-store --dump '$storePath' > $tmpDir/contents/$name") == 0 + or die "nix-store --dump failed on `$storePath': $?"; + + system("$binDir/nix-store --query --references '$storePath' > $tmpDir/references/$name") == 0 + or die "nix-store --query --references failed on `$storePath': $?"; + + system("$binDir/nix-store --query --deriver '$storePath' > $tmpDir/derivers/$name") == 0 + or die "nix-store --query --deriver failed on `$storePath': $?"; +} + + +# Write a NAR archive of everything to standard output. +system("nix-store --dump '$tmpDir'") == 0 + or die "nix-store --dump failed"; diff --git a/scripts/nix-unpack-closure.in b/scripts/nix-unpack-closure.in new file mode 100644 index 00000000..98c7d84d --- /dev/null +++ b/scripts/nix-unpack-closure.in @@ -0,0 +1,81 @@ +#! @perl@ -w + +# This tool unpacks the closures created by "nix-pack-closure" and +# adds them to the Nix store. + +# TODO: make this program "streamy", i.e., don't use a temporary +# directory. + +use strict; +use POSIX qw(tmpnam); + +my $binDir = $ENV{"NIX_BIN_DIR"}; +$binDir = "@bindir@" unless defined $binDir; + +my $tmpDir; +do { $tmpDir = tmpnam(); } +until mkdir $tmpDir, 0777; +END { system "rm -rf '$tmpDir'"; } + + +# Unpack the NAR archive on standard input. +system("nix-store --restore '$tmpDir/unpacked'") == 0 + or die "nix-store --restore failed"; + + +open VALID, ">$tmpDir/validity" or die; + + +# For each path in the closure that is not yet valid, add it to the +# store. TODO: use proper locking. Or even better, let nix-store do +# this. +opendir(DIR, "$tmpDir/unpacked/contents") or die "cannot open directory: $!"; + +foreach my $name (sort(readdir DIR)) { + next if $name eq "." or $name eq ".."; + + my $storePath = "/nix/store/$name"; # !!! + + # !!! this really isn't a good validity check! + system "/nix/bin/nix-store --check-validity '$storePath' 2> /dev/null"; + if ($? != 0) { + print STDERR "unpacking `$storePath'...\n"; + + # !!! race + system("rm -rf '$storePath'") == 0 + or die "cannot remove `$storePath': $?"; + + system("$binDir/nix-store --restore '$storePath' < '$tmpDir/unpacked/contents/$name'") == 0 + or die "nix-store --dump failed on `$storePath': $?"; + + print VALID "$storePath\n"; + + open DRV, "<$tmpDir/unpacked/derivers/$name" or die; + my $deriver = ; + chomp $deriver; + $deriver = "" if $deriver eq "unknown-deriver"; + close DRV; + + my @refs; + open REFS, "<$tmpDir/unpacked/references/$name" or die; + while () { + chomp; + push @refs, $_; + } + close REFS; + + print VALID "$deriver\n"; + + print VALID (scalar @refs), "\n"; + foreach my $ref (@refs) { + print VALID "$ref\n"; + } + } +} + +closedir(DIR) or die; + + +# Register the invalid paths as valid. +system("nix-store --register-validity <'$tmpDir/validity'") == 0 + or die "nix-store --register-validity failed";