* 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 | ||||
| 
 | ||||
| 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 | ||||
|         --no-link) | ||||
|             noLink=1 | ||||
|      | ||||
|         --add-drv-link) | ||||
|             addDrvLink=1 | ||||
|             ;; | ||||
|              | ||||
|         --no-link) | ||||
|             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 | ||||
|  |  | |||
|  | @ -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; | ||||
|  |  | |||
|  | @ -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 */ | ||||
|  |  | |||
|  | @ -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; | ||||
|  |  | |||
|  | @ -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); | ||||
|  |  | |||
|  | @ -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 | ||||
|  |  | |||
|  | @ -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 | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue