* 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.
This commit is contained in:
Eelco Dolstra 2005-02-01 13:48:46 +00:00
parent dcc37c236c
commit 630ae0c9d7
7 changed files with 101 additions and 50 deletions

View file

@ -8,41 +8,52 @@ if test -z "$nixExpr"; then
fi fi
extraArgs= 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 for i in "$@"; do
case "$i" in case "$i" in
--no-link)
noLink=1 --add-drv-link)
addDrvLink=1
;; ;;
--no-link)
addOutLink=0
;;
-*) -*)
extraArgs="$extraArgs $i" extraArgs="$extraArgs $i"
;; ;;
*) *)
# Instantiate the Nix expression.
prefix=
if test "$addDrvLink" = 0; then prefix=.nix-build-tmp-; fi
storeExprs=$(@bindir@/nix-instantiate \ storeExprs=$(@bindir@/nix-instantiate \
--add-root "@localstatedir@/nix/gcroots/nix-build/$userName-drv" \ --add-root ./${prefix}derivation --indirect \
"$i") "$i")
for j in $storeExprs; do for j in $storeExprs; do
echo "store expression is $j" >&2 echo "store expression is $j $(readlink "$j")" >&2
done done
# Build the resulting store derivation.
prefix=
if test "$addOutLink" = 0; then prefix=.nix-build-tmp-; fi
outPaths=$(@bindir@/nix-store \ outPaths=$(@bindir@/nix-store \
--add-root "@localstatedir@/nix/gcroots/nix-build/$userName-out" \ --add-root ./${prefix}result --indirect \
-rv $extraArgs $storeExprs) -rv $extraArgs $storeExprs)
for j in $outPaths; do for j in $outPaths; do
echo "$j" echo "$j $(readlink "$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
done done
;; ;;
esac esac
done done

View file

@ -13,6 +13,8 @@
static string gcLockName = "gc.lock"; 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 /* 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 storePath(canonPath(_storePath));
Path gcRoot(canonPath(_gcRoot)); Path gcRoot(canonPath(_gcRoot));
assertStorePath(storePath);
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);
/* Grab the global GC root. This prevents the set of permanent /* Grab the global GC root. This prevents the set of permanent
roots from increasing while a GC is in progress. */ roots from increasing while a GC is in progress. */
AutoCloseFD fdGCLock = openGCLock(ltRead); AutoCloseFD fdGCLock = openGCLock(ltRead);
/* Create directories up to `gcRoot'. */ if (indirect) {
createDirs(dirOf(gcRoot)); string hash = printHash32(hashString(htSHA1, gcRoot));
Path realRoot = canonPath((format("%1%/%2%/auto/%3%")
% nixStateDir % gcRootsDir % hash).str());
/* Remove the old symlink. */ createSymlink(gcRoot, storePath, true);
unlink(gcRoot.c_str()); createSymlink(realRoot, gcRoot, false);
}
/* And create the new own. */ else {
if (symlink(storePath.c_str(), gcRoot.c_str()) == -1) Path rootsDir = canonPath((format("%1%/%2%") % nixStateDir % gcRootsDir).str());
throw SysError(format("symlinking `%1%' to `%2%'")
% gcRoot % storePath); 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; return gcRoot;
} }
static string tempRootsDir = "temproots";
/* The file to which we write our temporary roots. */ /* The file to which we write our temporary roots. */
static Path fnTempRoots; static Path fnTempRoots;
static AutoCloseFD fdTempRoots; static AutoCloseFD fdTempRoots;

View file

@ -27,7 +27,8 @@ void addTempRoot(const Path & path);
void removeTempRoots(); void removeTempRoots();
/* Register a permanent GC root. */ /* 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 */ #endif /* !__GC_H */

View file

@ -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 readDirectory(const Path & path)
{ {
Strings names; Strings names;

View file

@ -84,6 +84,8 @@ bool pathExists(const Path & path);
in any way canonicalised. */ in any way canonicalised. */
Path readLink(const Path & path); Path readLink(const Path & path);
bool isLink(const Path & path);
/* Read the contents of a directory. The entries `.' and `..' are /* Read the contents of a directory. The entries `.' and `..' are
removed. */ removed. */
Strings readDirectory(const Path & path); Strings readDirectory(const Path & path);

View file

@ -29,6 +29,7 @@ static Expr evalStdin(EvalState & state, bool parseOnly)
static Path gcRoot; static Path gcRoot;
static int rootNr = 0; static int rootNr = 0;
static bool indirectRoot = false;
/* Print out the paths of the resulting derivation(s). If the user /* Print out the paths of the resulting derivation(s). If the user
@ -51,7 +52,8 @@ static void printDrvPaths(EvalState & state, Expr e)
printGCWarning(); printGCWarning();
else else
drvPath = addPermRoot(drvPath, drvPath = addPermRoot(drvPath,
makeRootName(gcRoot, rootNr)); makeRootName(gcRoot, rootNr),
indirectRoot);
cout << format("%1%\n") % drvPath; cout << format("%1%\n") % drvPath;
return; return;
} }
@ -110,8 +112,10 @@ void run(Strings args)
else if (arg == "--add-root") { else if (arg == "--add-root") {
if (i == args.end()) if (i == args.end())
throw UsageError("`--add-root requires an argument"); throw UsageError("`--add-root requires an argument");
gcRoot = *i++; gcRoot = absPath(*i++);
} }
else if (arg == "--indirect")
indirectRoot = true;
else if (arg[0] == '-') else if (arg[0] == '-')
throw UsageError(format("unknown flag `%1%`") % arg); throw UsageError(format("unknown flag `%1%`") % arg);
else else

View file

@ -1,9 +1,5 @@
#include <iostream> #include <iostream>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include "globals.hh" #include "globals.hh"
#include "build.hh" #include "build.hh"
#include "gc.hh" #include "gc.hh"
@ -24,6 +20,7 @@ void printHelp()
static Path gcRoot; static Path gcRoot;
static int rootNr = 0; static int rootNr = 0;
static bool indirectRoot = false;
static Path findOutput(const Derivation & drv, string id) 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) static Path followSymlinks(Path & path)
{ {
path = absPath(path);
while (!isStorePath(path)) { while (!isStorePath(path)) {
struct stat st; if (!isLink(path)) return path;
if (lstat(path.c_str(), &st))
throw SysError(format("getting status of `%1%'") % path);
if (!S_ISLNK(st.st_mode)) return path;
string target = readLink(path); string target = readLink(path);
path = canonPath(string(target, 0, 1) == "/" path = canonPath(string(target, 0, 1) == "/"
? target ? target
@ -64,7 +59,9 @@ static Path realisePath(const Path & path)
if (gcRoot == "") if (gcRoot == "")
printGCWarning(); printGCWarning();
else else
outPath = addPermRoot(outPath, makeRootName(gcRoot, rootNr)); outPath = addPermRoot(outPath,
makeRootName(gcRoot, rootNr),
indirectRoot);
return outPath; return outPath;
} else { } else {
@ -191,6 +188,7 @@ static void opQuery(Strings opFlags, Strings opArgs)
for (Strings::iterator i = opArgs.begin(); for (Strings::iterator i = opArgs.begin();
i != opArgs.end(); i++) i != opArgs.end(); i++)
{ {
*i = followSymlinks(*i);
if (forceRealise) realisePath(*i); if (forceRealise) realisePath(*i);
Derivation drv = derivationFromPath(*i); Derivation drv = derivationFromPath(*i);
cout << format("%1%\n") % findOutput(drv, "out"); cout << format("%1%\n") % findOutput(drv, "out");
@ -206,6 +204,7 @@ static void opQuery(Strings opFlags, Strings opArgs)
for (Strings::iterator i = opArgs.begin(); for (Strings::iterator i = opArgs.begin();
i != opArgs.end(); i++) i != opArgs.end(); i++)
{ {
*i = followSymlinks(*i);
Path path = maybeUseOutput(*i, useOutput, forceRealise); Path path = maybeUseOutput(*i, useOutput, forceRealise);
if (query == qRequisites) if (query == qRequisites)
storePathRequisites(path, includeOutputs, paths); storePathRequisites(path, includeOutputs, paths);
@ -441,8 +440,10 @@ void run(Strings args)
else if (arg == "--add-root") { else if (arg == "--add-root") {
if (i == args.end()) if (i == args.end())
throw UsageError("`--add-root requires an argument"); throw UsageError("`--add-root requires an argument");
gcRoot = *i++; gcRoot = absPath(*i++);
} }
else if (arg == "--indirect")
indirectRoot = true;
else if (arg[0] == '-') else if (arg[0] == '-')
opFlags.push_back(arg); opFlags.push_back(arg);
else else