* 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:
parent
dcc37c236c
commit
630ae0c9d7
7 changed files with 101 additions and 50 deletions
|
@ -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
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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 */
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
Loading…
Reference in a new issue