summary refs log tree commit diff
diff options
context:
space:
mode:
authorEelco Dolstra <e.dolstra@tudelft.nl>2005-02-01 13:48:46 +0000
committerEelco Dolstra <e.dolstra@tudelft.nl>2005-02-01 13:48:46 +0000
commit630ae0c9d7f65a2d6bef85a5194b4d704e54eded (patch)
tree1cbb2dce71e58abb4617239857bbd144b1f355c1
parentdcc37c236c66ba463bd61fec23d046485d8a412f (diff)
downloadguix-630ae0c9d7f65a2d6bef85a5194b4d704e54eded.tar.gz
* nix-build: use an indirection scheme to make it easier for users to
  get rid of GC roots.  Nix-build places a symlink `result' in the
  current directory.  Previously, removing that symlink would not
  remove the store path being linked to as a GC root.  Now, the GC
  root created by nix-build is actually a symlink in
  `/nix/var/nix/gcroots/auto' to `result'.  So if that symlink is
  removed the GC root automatically becomes invalid (since it can no
  longer be resolved).  The root itself is not automatically removed -
  the garbage collector should delete dangling roots.

-rw-r--r--scripts/nix-build.in45
-rw-r--r--src/libstore/gc.cc61
-rw-r--r--src/libstore/gc.hh3
-rw-r--r--src/libutil/util.cc9
-rw-r--r--src/libutil/util.hh2
-rw-r--r--src/nix-instantiate/main.cc8
-rw-r--r--src/nix-store/main.cc21
7 files changed, 100 insertions, 49 deletions
diff --git a/scripts/nix-build.in b/scripts/nix-build.in
index 33fbc61a3d..e11a88dcd0 100644
--- a/scripts/nix-build.in
+++ b/scripts/nix-build.in
@@ -8,41 +8,52 @@ if test -z "$nixExpr"; then
 fi
 
 extraArgs=
-noLink=
+addDrvLink=0
+addOutLink=1
 
-userName=$USER
-if test -z "$username"; then userName="unknown"; fi
 
+trap 'rm -f ./.nix-build-tmp-*' EXIT
+
+
+# Process the arguments.
 for i in "$@"; do
     case "$i" in
+    
+        --add-drv-link)
+            addDrvLink=1
+            ;;
+            
         --no-link)
-            noLink=1
+            addOutLink=0
             ;;
+            
         -*)
             extraArgs="$extraArgs $i"
             ;;
+            
         *)
+            # Instantiate the Nix expression.
+            prefix=
+            if test "$addDrvLink" = 0; then prefix=.nix-build-tmp-; fi
             storeExprs=$(@bindir@/nix-instantiate \
-                --add-root "@localstatedir@/nix/gcroots/nix-build/$userName-drv" \
+                --add-root ./${prefix}derivation --indirect \
                 "$i")
+                
             for j in $storeExprs; do
-                echo "store expression is $j" >&2
+                echo "store expression is $j $(readlink "$j")" >&2
             done
+
+            # Build the resulting store derivation.
+            prefix=
+            if test "$addOutLink" = 0; then prefix=.nix-build-tmp-; fi
             outPaths=$(@bindir@/nix-store \
-                --add-root "@localstatedir@/nix/gcroots/nix-build/$userName-out" \
+                --add-root ./${prefix}result --indirect \
                 -rv $extraArgs $storeExprs)
+                
             for j in $outPaths; do
-                echo "$j"
-                if test -z "$noLink"; then
-                    if test -L result; then
-                        rm result
-                    elif test -e result; then
-                        echo "cannot remove \`result' (not a symlink)"
-                        exit 1
-                    fi
-                    ln -s "$j" result
-                fi
+                echo "$j $(readlink "$j")"
             done
+            
             ;;
     esac
 done
diff --git a/src/libstore/gc.cc b/src/libstore/gc.cc
index ee9a369dc4..8385e31b1c 100644
--- a/src/libstore/gc.cc
+++ b/src/libstore/gc.cc
@@ -13,6 +13,8 @@
 
 
 static string gcLockName = "gc.lock";
+static string tempRootsDir = "temproots";
+static string gcRootsDir = "gcroots";
 
 
 /* Acquire the global GC lock.  This is used to prevent new Nix
@@ -49,40 +51,61 @@ static void createDirs(const Path & path)
 }
 
 
-Path addPermRoot(const Path & _storePath, const Path & _gcRoot)
+void createSymlink(const Path & link, const Path & target, bool careful)
+{
+    /* Create directories up to `gcRoot'. */
+    createDirs(dirOf(link));
+
+    /* Remove the old symlink. */
+    if (pathExists(link)) {
+        if (careful && (!isLink(link) || !isStorePath(readLink(link))))
+            throw Error(format("cannot create symlink `%1%'; already exists") % link);
+        unlink(link.c_str());
+    }
+
+    /* And create the new own. */
+    if (symlink(target.c_str(), link.c_str()) == -1)
+        throw SysError(format("symlinking `%1%' to `%2%'")
+            % link % target);
+}
+
+
+Path addPermRoot(const Path & _storePath, const Path & _gcRoot,
+    bool indirect)
 {
     Path storePath(canonPath(_storePath));
     Path gcRoot(canonPath(_gcRoot));
-
-    Path rootsDir = canonPath((format("%1%/%2%") % nixStateDir % "gcroots").str());
-    
-    if (string(gcRoot, 0, rootsDir.size() + 1) != rootsDir + "/")
-        throw Error(format(
-            "path `%1%' is not a valid garbage collector root; "
-            "it's not in the `%1%' directory")
-            % gcRoot % rootsDir);
+    assertStorePath(storePath);
 
     /* Grab the global GC root.  This prevents the set of permanent
        roots from increasing while a GC is in progress. */
     AutoCloseFD fdGCLock = openGCLock(ltRead);
 
-    /* Create directories up to `gcRoot'. */
-    createDirs(dirOf(gcRoot));
+    if (indirect) {
+        string hash = printHash32(hashString(htSHA1, gcRoot));
+        Path realRoot = canonPath((format("%1%/%2%/auto/%3%")
+            % nixStateDir % gcRootsDir % hash).str());
 
-    /* Remove the old symlink. */
-    unlink(gcRoot.c_str());
+        createSymlink(gcRoot, storePath, true);
+        createSymlink(realRoot, gcRoot, false);
+    }
 
-    /* And create the new own. */
-    if (symlink(storePath.c_str(), gcRoot.c_str()) == -1)
-        throw SysError(format("symlinking `%1%' to `%2%'")
-            % gcRoot % storePath);
+    else {
+        Path rootsDir = canonPath((format("%1%/%2%") % nixStateDir % gcRootsDir).str());
+    
+        if (string(gcRoot, 0, rootsDir.size() + 1) != rootsDir + "/")
+            throw Error(format(
+                "path `%1%' is not a valid garbage collector root; "
+                "it's not in the directory `%2%'")
+                % gcRoot % rootsDir);
+
+        createSymlink(gcRoot, storePath, false);
+    }
 
     return gcRoot;
 }
 
 
-static string tempRootsDir = "temproots";
-
 /* The file to which we write our temporary roots. */
 static Path fnTempRoots;
 static AutoCloseFD fdTempRoots;
diff --git a/src/libstore/gc.hh b/src/libstore/gc.hh
index 03e1d76915..e3da4505d5 100644
--- a/src/libstore/gc.hh
+++ b/src/libstore/gc.hh
@@ -27,7 +27,8 @@ void addTempRoot(const Path & path);
 void removeTempRoots();
 
 /* Register a permanent GC root. */
-Path addPermRoot(const Path & storePath, const Path & gcRoot);
+Path addPermRoot(const Path & storePath, const Path & gcRoot,
+    bool indirect);
 
 
 #endif /* !__GC_H */
diff --git a/src/libutil/util.cc b/src/libutil/util.cc
index 5b6fb62026..108c054b7f 100644
--- a/src/libutil/util.cc
+++ b/src/libutil/util.cc
@@ -135,6 +135,15 @@ Path readLink(const Path & path)
 }
 
 
+bool isLink(const Path & path)
+{
+    struct stat st;
+    if (lstat(path.c_str(), &st))
+        throw SysError(format("getting status of `%1%'") % path);
+    return S_ISLNK(st.st_mode);
+}
+
+
 Strings readDirectory(const Path & path)
 {
     Strings names;
diff --git a/src/libutil/util.hh b/src/libutil/util.hh
index 104e3f2651..d9d5a7cdfc 100644
--- a/src/libutil/util.hh
+++ b/src/libutil/util.hh
@@ -84,6 +84,8 @@ bool pathExists(const Path & path);
    in any way canonicalised. */
 Path readLink(const Path & path);
 
+bool isLink(const Path & path);
+
 /* Read the contents of a directory.  The entries `.' and `..' are
    removed. */
 Strings readDirectory(const Path & path);
diff --git a/src/nix-instantiate/main.cc b/src/nix-instantiate/main.cc
index 7d12c201fb..3e9fad4ae4 100644
--- a/src/nix-instantiate/main.cc
+++ b/src/nix-instantiate/main.cc
@@ -29,6 +29,7 @@ static Expr evalStdin(EvalState & state, bool parseOnly)
 
 static Path gcRoot;
 static int rootNr = 0;
+static bool indirectRoot = false;
 
 
 /* Print out the paths of the resulting derivation(s).  If the user
@@ -51,7 +52,8 @@ static void printDrvPaths(EvalState & state, Expr e)
                     printGCWarning();
                 else
                     drvPath = addPermRoot(drvPath,
-                        makeRootName(gcRoot, rootNr));
+                        makeRootName(gcRoot, rootNr),
+                        indirectRoot);
                 cout << format("%1%\n") % drvPath;
                 return;
             }
@@ -110,8 +112,10 @@ void run(Strings args)
         else if (arg == "--add-root") {
             if (i == args.end())
                 throw UsageError("`--add-root requires an argument");
-            gcRoot = *i++;
+            gcRoot = absPath(*i++);
         }
+        else if (arg == "--indirect")
+            indirectRoot = true;
         else if (arg[0] == '-')
             throw UsageError(format("unknown flag `%1%`") % arg);
         else
diff --git a/src/nix-store/main.cc b/src/nix-store/main.cc
index c1fedaf48c..3edcff7eee 100644
--- a/src/nix-store/main.cc
+++ b/src/nix-store/main.cc
@@ -1,9 +1,5 @@
 #include <iostream>
 
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <unistd.h>
-
 #include "globals.hh"
 #include "build.hh"
 #include "gc.hh"
@@ -24,6 +20,7 @@ void printHelp()
 
 static Path gcRoot;
 static int rootNr = 0;
+static bool indirectRoot = false;
 
 
 static Path findOutput(const Derivation & drv, string id)
@@ -37,11 +34,9 @@ static Path findOutput(const Derivation & drv, string id)
 
 static Path followSymlinks(Path & path)
 {
+    path = absPath(path);
     while (!isStorePath(path)) {
-        struct stat st;
-        if (lstat(path.c_str(), &st))
-            throw SysError(format("getting status of `%1%'") % path);
-        if (!S_ISLNK(st.st_mode)) return path;
+        if (!isLink(path)) return path;
         string target = readLink(path);
         path = canonPath(string(target, 0, 1) == "/"
             ? target
@@ -64,7 +59,9 @@ static Path realisePath(const Path & path)
         if (gcRoot == "")
             printGCWarning();
         else
-            outPath = addPermRoot(outPath, makeRootName(gcRoot, rootNr));
+            outPath = addPermRoot(outPath,
+                makeRootName(gcRoot, rootNr),
+                indirectRoot);
         
         return outPath;
     } else {
@@ -191,6 +188,7 @@ static void opQuery(Strings opFlags, Strings opArgs)
             for (Strings::iterator i = opArgs.begin();
                  i != opArgs.end(); i++)
             {
+                *i = followSymlinks(*i);
                 if (forceRealise) realisePath(*i);
                 Derivation drv = derivationFromPath(*i);
                 cout << format("%1%\n") % findOutput(drv, "out");
@@ -206,6 +204,7 @@ static void opQuery(Strings opFlags, Strings opArgs)
             for (Strings::iterator i = opArgs.begin();
                  i != opArgs.end(); i++)
             {
+                *i = followSymlinks(*i);
                 Path path = maybeUseOutput(*i, useOutput, forceRealise);
                 if (query == qRequisites)
                     storePathRequisites(path, includeOutputs, paths);
@@ -441,8 +440,10 @@ void run(Strings args)
         else if (arg == "--add-root") {
             if (i == args.end())
                 throw UsageError("`--add-root requires an argument");
-            gcRoot = *i++;
+            gcRoot = absPath(*i++);
         }
+        else if (arg == "--indirect")
+            indirectRoot = true;
         else if (arg[0] == '-')
             opFlags.push_back(arg);
         else