* 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…
	
	Add table
		Add a link
		
	
		Reference in a new issue