about summary refs log tree commit diff
diff options
context:
space:
mode:
authorEelco Dolstra <e.dolstra@tudelft.nl>2003-07-10T15·11+0000
committerEelco Dolstra <e.dolstra@tudelft.nl>2003-07-10T15·11+0000
commit1d1c3691d2fdf5aad0baceadd8596f23c1e0e1fa (patch)
treec89123e33796e3408b613e1a824bcc66ad30395e
parentd072485d2895d01dbbab1d899418726e3349343f (diff)
* The policy-free derivate sharing now *almost* works. :-) For any
  hash for which no local expansion is available, Nix can execute a
  `substitute' which should produce a path with such a hash.

  This is policy-free since Nix does not in any way specify how the
  substitute should work, i.e., it's an arbitrary (unnormalised)
  fstate expression.  For example, `nix-pull' registers substitutes
  that fetch Nix archives from the network (through `wget') and unpack
  them, but any other method is possible as well.  This is an
  improvement over the old Nix sharing scheme, which had a policy
  (fetching through `wget') built in.

  The sharing scheme doesn't work completely yet because successors
  from fstate rewriting have to be registered on the receiving side.
  Probably the whole successor stuff can be folded up into the
  substitute mechanism; this would be a nice simplification.

-rw-r--r--corepkgs/Makefile.am2
-rw-r--r--corepkgs/nar/unnar.fix8
-rw-r--r--corepkgs/nar/unnar.sh3
-rw-r--r--scripts/Makefile.am4
-rw-r--r--scripts/nix-pull65
-rwxr-xr-xscripts/nix-pull-prebuilts83
-rwxr-xr-xscripts/nix-push-prebuilts44
-rw-r--r--scripts/prebuilts.conf6
-rw-r--r--src/fstate.cc6
-rw-r--r--src/fstate.hh2
-rw-r--r--src/nix.cc25
-rw-r--r--src/store.cc45
-rw-r--r--src/store.hh4
13 files changed, 155 insertions, 142 deletions
diff --git a/corepkgs/Makefile.am b/corepkgs/Makefile.am
index 9ce9c8c790..9298865bff 100644
--- a/corepkgs/Makefile.am
+++ b/corepkgs/Makefile.am
@@ -6,3 +6,5 @@ install-data-local:
 	$(INSTALL) -d $(datadir)/fix/nar
 	$(INSTALL_DATA) nar/nar.fix $(datadir)/fix/nar
 	$(INSTALL_DATA) nar/nar.sh $(datadir)/fix/nar
+	$(INSTALL_DATA) nar/unnar.fix $(datadir)/fix/nar
+	$(INSTALL_DATA) nar/unnar.sh $(datadir)/fix/nar
diff --git a/corepkgs/nar/unnar.fix b/corepkgs/nar/unnar.fix
new file mode 100644
index 0000000000..db97750aaa
--- /dev/null
+++ b/corepkgs/nar/unnar.fix
@@ -0,0 +1,8 @@
+Function(["nar", "name"],
+  Package(
+    [ ("name", Var("name"))
+    , ("build", Relative("nar/unnar.sh"))
+    , ("nar", Var("nar"))
+    ]
+  )
+)
\ No newline at end of file
diff --git a/corepkgs/nar/unnar.sh b/corepkgs/nar/unnar.sh
new file mode 100644
index 0000000000..e6a3f3c1fe
--- /dev/null
+++ b/corepkgs/nar/unnar.sh
@@ -0,0 +1,3 @@
+#! /bin/sh
+
+/tmp/nix/bin/nix --restore "$out" < $nar || exit 1
diff --git a/scripts/Makefile.am b/scripts/Makefile.am
index e4602f2a1f..2f4dbacc93 100644
--- a/scripts/Makefile.am
+++ b/scripts/Makefile.am
@@ -5,5 +5,5 @@ install-exec-local:
 	$(INSTALL) -d $(sysconfdir)/profile.d
 	$(INSTALL_PROGRAM) nix-profile.sh $(sysconfdir)/profile.d/nix.sh
 	$(INSTALL) -d $(sysconfdir)/nix
-    # !!! don't overwrite local modifications
-	$(INSTALL_PROGRAM) prebuilts.conf $(sysconfdir)/nix/prebuilts.conf
+# !!! don't overwrite local modifications
+	$(INSTALL_DATA) prebuilts.conf $(sysconfdir)/nix/prebuilts.conf
diff --git a/scripts/nix-pull b/scripts/nix-pull
index 0b09c8e00a..59773a2bad 100644
--- a/scripts/nix-pull
+++ b/scripts/nix-pull
@@ -1,2 +1,67 @@
 #! /usr/bin/perl -w
 
+my $prefix = $ENV{"NIX"} || "/tmp/nix"; # !!! use prefix
+my $etcdir = "$prefix/etc/nix";
+my $tmpfile = "$prefix/var/nix/pull.tmp";
+
+my $conffile = "$etcdir/prebuilts.conf";
+
+open CONFFILE, "<$conffile";
+
+while (<CONFFILE>) {
+
+    chomp;
+    if (/^\s*(\S+)\s*(\#.*)?$/) {
+        my $url = $1;
+
+        print "obtaining list of Nix archives at $url...\n";
+
+	system "wget '$url' -O '$tmpfile' 2> /dev/null"; # !!! escape
+	if ($?) { die "`wget' failed"; }
+	
+	open INDEX, "<$tmpfile";
+
+	while (<INDEX>) {
+	    # Get all links to prebuilts, that is, file names of the
+	    # form foo-HASH-HASH.tar.bz2.
+	    next unless (/HREF=\"([^\"]*)\"/);
+	    my $fn = $1;
+	    next if $fn =~ /\.\./;
+	    next if $fn =~ /\//;
+	    next unless $fn =~ /([0-9a-z]{32})-([0-9a-z]{32})\.nar/;
+	    my $hash = $2;
+
+	    print "registering $hash -> $url/$fn\n";
+
+	    # Construct a Fix expression that fetches and unpacks a
+	    # Nix archive from the network.
+	    my $fetch =
+              "App(IncludeFix(\"fetchurl/fetchurl.fix\"), " .
+              "[(\"url\", \"$url/$fn\"), (\"hash\", \"\")])";
+	    my $fixexpr = 
+		"App(IncludeFix(\"nar/unnar.fix\"), " .
+		"[ (\"nar\", $fetch)" .
+		", (\"name\", \"fetched-$hash\")" .
+		"])";
+	    
+	    my $fixfile = "/tmp/nix-pull-tmp.fix";
+	    open FIX, ">$fixfile";
+	    print FIX $fixexpr;
+	    close FIX;
+
+	    # Instantiate a Nix expression from the Fix expression.
+	    my $nhash = `fix $fixfile`;
+	    $? and die "instantiating Nix archive expression";
+	    chomp $nhash;
+	    die unless $nhash =~ /^([0-9a-z]{32})$/;
+
+	    system "nix --substitute $hash $nhash";
+	    if ($?) { die "`nix --substitute' failed"; }
+	}
+
+	close INDEX;
+
+	unlink $tmpfile;
+    }
+
+}
diff --git a/scripts/nix-pull-prebuilts b/scripts/nix-pull-prebuilts
deleted file mode 100755
index 3d045b4630..0000000000
--- a/scripts/nix-pull-prebuilts
+++ /dev/null
@@ -1,83 +0,0 @@
-#! /usr/bin/perl -w
-
-my $prefix = $ENV{"NIX"} || "/nix"; # !!! use prefix
-my $etcdir = "$prefix/etc/nix";
-my $knowns = "$prefix/var/nix/known-prebuilts";
-my $tmpfile = "$prefix/var/nix/prebuilts.tmp";
-
-my $conffile = "$etcdir/prebuilts.conf";
-
-umask 0022;
-
-sub register {
-    my $fn = shift;
-    my $url = shift;
-    return unless $fn =~ /([^\/]*)-([0-9a-z]{32})-([0-9a-z]{32})\.tar\.bz2/;
-    my $id = $1;
-    my $pkghash = $2;
-    my $prebuilthash = $3;
-
-    print "$pkghash => $prebuilthash ($id)\n";
-
-    system "nix regprebuilt $pkghash $prebuilthash";
-    if ($?) { die "`nix regprebuilt' failed"; }
-
-    if ($url =~ /^\//) {
-	system "nix regfile $url";
-	if ($?) { die "`nix regfile' failed"; }
-    } else {
-	system "nix regurl $prebuilthash $url";
-	if ($?) { die "`nix regurl' failed"; }
-    }
-
-    print KNOWNS "$pkghash\n";
-}
-
-open KNOWNS, ">$knowns";
-
-open CONFFILE, "<$conffile";
-
-while (<CONFFILE>) {
-    chomp;
-    if (/^\s*(\S+)\s*(\#.*)?$/) {
-        my $url = $1;
-
-        print "obtaining prebuilt list from $url...\n";
-
-        if ($url =~ /^\//) {
-
-            # It's a local path.
-
-            foreach my $fn (glob "$url/*") {
-                register($fn, $fn);
-            }
-
-        } else {
-
-            # It's a URL.
-
-            system "wget '$url' -O '$tmpfile' 2> /dev/null"; # !!! escape
-            if ($?) { die "`wget' failed"; }
-
-            open INDEX, "<$tmpfile";
-
-            while (<INDEX>) {
-                # Get all links to prebuilts, that is, file names of the
-                # form foo-HASH-HASH.tar.bz2.
-                next unless (/HREF=\"([^\"]*)\"/);
-                my $fn = $1;
-                next if $fn =~ /\.\./;
-                next if $fn =~ /\//;
-                register($fn, "$url/$fn");
-            }
-
-            close INDEX;
-
-            unlink $tmpfile;
-        }
-    }
-}
-
-close CONFFILE;
-
-close KNOWNS;
diff --git a/scripts/nix-push-prebuilts b/scripts/nix-push-prebuilts
deleted file mode 100755
index 2d44e7cda0..0000000000
--- a/scripts/nix-push-prebuilts
+++ /dev/null
@@ -1,44 +0,0 @@
-#! /usr/bin/perl -w
-
-my $prefix = $ENV{"NIX"} || "/nix"; # !!! use prefix
-my $etcdir = "$prefix/etc/nix";
-my $exportdir = "$prefix/var/nix/prebuilts/exports";
-my $knowns = "$prefix/var/nix/known-prebuilts";
-
-umask 0022;
-
-# For performance, put the known hashes in an associative array.
-my %knowns = ();
-open KNOWNS, "<$knowns";
-while (<KNOWNS>) {
-    next unless /([0-9a-z]{32})/;
-    $knowns{$1} = 1;
-}
-close KNOWNS;
-
-# For each installed package, check whether a prebuilt is known.
-
-open PKGS, "nix listinst|";
-
-while (<PKGS>) {
-    chomp;
-    next unless /([0-9a-z]{32})/;
-    my $pkghash = $1;
-    if (!defined $knowns{$1}) {
-        # No known prebuilt exists for this package; so export it.
-        print "exporting $pkghash...\n";
-        system "nix export '$exportdir' $pkghash";
-        if ($?) { die "`nix export' failed"; }
-    }
-}
-
-close PKGS;
-
-# Push the prebuilts to the server. !!! FIXME
-
-system "rsync -av -e ssh '$exportdir'/ eelco\@losser.st-lab.cs.uu.nl:/home/eelco/public_html/nix-prebuilts/";
-
-# Rerun `nix-pull-prebuilts' to rescan the prebuilt source locations.
-
-print "running nix-pull-prebuilts...";
-system "nix-pull-prebuilts";
diff --git a/scripts/prebuilts.conf b/scripts/prebuilts.conf
index 9b950cad4d..c7bc89c61c 100644
--- a/scripts/prebuilts.conf
+++ b/scripts/prebuilts.conf
@@ -1,4 +1,2 @@
-# A list of URLs or local paths from where we obtain prebuilts.
-/nix/var/nix/prebuilts/imports
-/nix/var/nix/prebuilts/exports
-http://losser.st-lab.cs.uu.nl/~eelco/nix-prebuilts/
+# A list of URLs from where we obtain Nix archives.
+http://losser.st-lab.cs.uu.nl/~eelco/nix-dist/
diff --git a/src/fstate.cc b/src/fstate.cc
index fdd43d1b13..97532c162c 100644
--- a/src/fstate.cc
+++ b/src/fstate.cc
@@ -147,6 +147,12 @@ Hash hashTerm(ATerm t)
 }
 
 
+FState hash2fstate(Hash hash)
+{
+    return ATmake("Include(<str>)", ((string) hash).c_str());
+}
+
+
 ATerm termFromHash(const Hash & hash, string * p)
 {
     string path = expandHash(hash);
diff --git a/src/fstate.hh b/src/fstate.hh
index 159c7ba463..8a873a5acd 100644
--- a/src/fstate.hh
+++ b/src/fstate.hh
@@ -85,6 +85,8 @@ Error badTerm(const format & f, ATerm t);
 /* Hash an aterm. */
 Hash hashTerm(ATerm t);
 
+FState hash2fstate(Hash hash);
+
 /* Read an aterm from disk, given its hash. */
 ATerm termFromHash(const Hash & hash, string * p = 0);
 
diff --git a/src/nix.cc b/src/nix.cc
index 4721563fdf..53057328dd 100644
--- a/src/nix.cc
+++ b/src/nix.cc
@@ -26,6 +26,8 @@ static ArgType argType = atpUnknown;
      --add / -A: copy a path to the Nix store
      --query / -q: query information
 
+     --substitute: register a substitute expression
+
      --dump: dump a path as a Nix archive
      --restore: restore a path from a Nix archive
 
@@ -87,12 +89,6 @@ static Hash argToHash(const string & arg)
 }
 
 
-static FState hash2fstate(Hash hash)
-{
-    return ATmake("Include(<str>)", ((string) hash).c_str());
-}
-
-
 /* Realise (or install) paths from the given Nix fstate
    expressions. */
 static void opInstall(Strings opFlags, Strings opArgs)
@@ -187,6 +183,21 @@ static void opQuery(Strings opFlags, Strings opArgs)
 }
 
 
+static void opSubstitute(Strings opFlags, Strings opArgs)
+{
+    if (!opFlags.empty()) throw UsageError("unknown flag");
+    if (opArgs.size() % 2) throw UsageError("expecting even number of arguments");
+    
+    for (Strings::iterator i = opArgs.begin();
+         i != opArgs.end(); )
+    {
+        Hash srcHash = parseHash(*i++);
+        Hash subHash = parseHash(*i++);
+        registerSubstitute(srcHash, subHash);
+    }
+}
+
+
 /* A sink that writes dump output to stdout. */
 struct StdoutSink : DumpSink
 {
@@ -277,6 +288,8 @@ void run(Strings args)
             op = opAdd;
         else if (arg == "--query" || arg == "-q")
             op = opQuery;
+        else if (arg == "--substitute")
+            op = opSubstitute;
         else if (arg == "--dump")
             op = opDump;
         else if (arg == "--restore")
diff --git a/src/store.cc b/src/store.cc
index 5a3a4e0678..435ac5cc69 100644
--- a/src/store.cc
+++ b/src/store.cc
@@ -7,6 +7,7 @@
 #include "globals.hh"
 #include "db.hh"
 #include "archive.hh"
+#include "fstate.hh"
 
 
 struct CopySink : DumpSink
@@ -83,6 +84,20 @@ void copyPath(string src, string dst)
 }
 
 
+void registerSubstitute(const Hash & srcHash, const Hash & subHash)
+{
+    Strings subs;
+    queryListDB(nixDB, dbSubstitutes, srcHash, subs); /* non-existence = ok */
+
+    for (Strings::iterator it = subs.begin(); it != subs.end(); it++)
+        if (parseHash(*it) == subHash) return;
+    
+    subs.push_back(subHash);
+    
+    setListDB(nixDB, dbSubstitutes, srcHash, subs);
+}
+
+
 Hash registerPath(const string & _path, Hash hash)
 {
     string path(canonPath(_path));
@@ -139,8 +154,7 @@ string expandHash(const Hash & hash, const string & target,
     if (!target.empty() && !isInPrefix(target, prefix))
         abort();
 
-    if (!queryListDB(nixDB, dbHash2Paths, hash, paths))
-        throw Error(format("no paths known with hash `%1%'") % (string) hash);
+    queryListDB(nixDB, dbHash2Paths, hash, paths);
 
     /* !!! we shouldn't check for staleness by default --- too slow */
 
@@ -181,8 +195,32 @@ string expandHash(const Hash & hash, const string & target,
             /* try next one */
         }
     }
+
+    /* Try to realise the substitutes. */
+
+    Strings subs;
+    queryListDB(nixDB, dbSubstitutes, hash, subs); /* non-existence = ok */
+
+    for (Strings::iterator it = subs.begin(); it != subs.end(); it++) {
+        StringSet dummy;
+        FState nf = realiseFState(hash2fstate(parseHash(*it)), dummy);
+        string path = fstatePath(nf);
+
+        if (hashPath(path) != hash)
+            throw Error(format("bad substitute in `%1%'") % (string) path);
+
+        if (target.empty())
+            return path; /* !!! prefix */
+        else {
+            if (path != target) {
+                copyPath(path, target);
+                registerPath(target, hash);
+            }
+            return target;
+        }
+    }
     
-    throw Error(format("all paths with hash `%1%' are stale") % (string) hash);
+    throw Error(format("cannot expand hash `%1%'") % (string) hash);
 }
 
     
@@ -193,6 +231,7 @@ void addToStore(string srcPath, string & dstPath, Hash & hash)
     hash = hashPath(srcPath);
 
     try {
+        /* !!! should not use the substitutes! */
         dstPath = expandHash(hash, "", nixStore);
         return;
     } catch (...) {
diff --git a/src/store.hh b/src/store.hh
index 8b02cba996..8b41478a24 100644
--- a/src/store.hh
+++ b/src/store.hh
@@ -8,8 +8,12 @@
 using namespace std;
 
 
+/* Copy a path recursively. */
 void copyPath(string src, string dst);
 
+/* Register a substitute. */
+void registerSubstitute(const Hash & srcHash, const Hash & subHash);
+
 /* Register a path keyed on its hash. */
 Hash registerPath(const string & path, Hash hash = Hash());