* nix-env -e: support uninstalling by path, so that one can say
$ nix-env -e $(which firefox)
  or
    $ nix-env -e /nix/store/nywzlygrkfcgz7dfmhm5xixlx1l0m60v-pan-0.132
* nix-env -i: if an argument contains a slash anywhere, treat it as a
  path and follow it through symlinks into the Nix store.  This allows
  things like
    $ nix-build -A firefox
    $ nix-env -i ./result
* nix-env -q/-i/-e: don't complain when the `*' selector doesn't match
  anything.  In particular, `nix-env -q \*' doesn't fail anymore on an
  empty profile.
			
			
This commit is contained in:
		
							parent
							
								
									12d0a1eb75
								
							
						
					
					
						commit
						633518628f
					
				
					 7 changed files with 81 additions and 50 deletions
				
			
		|  | @ -26,7 +26,7 @@ static string gcLockName = "gc.lock"; | |||
| static string tempRootsDir = "temproots"; | ||||
| static string gcRootsDir = "gcroots"; | ||||
| 
 | ||||
| const unsigned int defaultGcLevel = 1000; | ||||
| static const int defaultGcLevel = 1000; | ||||
| 
 | ||||
| 
 | ||||
| /* Acquire the global GC lock.  This is used to prevent new Nix
 | ||||
|  | @ -447,7 +447,7 @@ void LocalStore::collectGarbage(GCAction action, const PathSet & pathsToDelete, | |||
|         queryBoolSetting("gc-keep-outputs", false); | ||||
|     bool gcKeepDerivations = | ||||
|         queryBoolSetting("gc-keep-derivations", true); | ||||
|     unsigned int gcKeepOutputsThreshold =  | ||||
|     int gcKeepOutputsThreshold =  | ||||
|         queryIntSetting ("gc-keep-outputs-threshold", defaultGcLevel); | ||||
| 
 | ||||
|     /* Acquire the global GC root.  This prevents
 | ||||
|  | @ -503,13 +503,12 @@ void LocalStore::collectGarbage(GCAction action, const PathSet & pathsToDelete, | |||
| 
 | ||||
| 		string gcLevelStr = drv.env["__gcLevel"]; | ||||
| 		int gcLevel; | ||||
| 		if (!string2Int(gcLevelStr,gcLevel)) { | ||||
| 		if (!string2Int(gcLevelStr, gcLevel)) | ||||
| 		    gcLevel = defaultGcLevel; | ||||
| 		} | ||||
| 		 | ||||
| 		if (gcLevel >= gcKeepOutputsThreshold)     | ||||
| 		    for (DerivationOutputs::iterator j = drv.outputs.begin(); | ||||
| 		            j != drv.outputs.end(); ++j) | ||||
|                          j != drv.outputs.end(); ++j) | ||||
| 			if (store->isValidPath(j->second.path)) | ||||
| 			    computeFSClosure(j->second.path, livePaths); | ||||
|             } | ||||
|  |  | |||
|  | @ -48,6 +48,26 @@ Path toStorePath(const Path & path) | |||
| } | ||||
| 
 | ||||
| 
 | ||||
| Path followLinksToStore(const Path & _path) | ||||
| { | ||||
|     Path path = absPath(_path); | ||||
|     while (!isInStore(path)) { | ||||
|         if (!isLink(path)) break; | ||||
|         string target = readLink(path); | ||||
|         path = absPath(target, dirOf(path)); | ||||
|     } | ||||
|     if (!isInStore(path)) | ||||
|         throw Error(format("path `%1%' is not in the Nix store") % path); | ||||
|     return path; | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| Path followLinksToStorePath(const Path & path) | ||||
| { | ||||
|     return toStorePath(followLinksToStore(path)); | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| void checkStoreName(const string & name) | ||||
| { | ||||
|     string validChars = "+-._?="; | ||||
|  |  | |||
|  | @ -174,11 +174,21 @@ bool isStorePath(const Path & path); | |||
| 
 | ||||
| void checkStoreName(const string & name); | ||||
| 
 | ||||
| 
 | ||||
| /* Chop off the parts after the top-level store name, e.g.,
 | ||||
|    /nix/store/abcd-foo/bar => /nix/store/abcd-foo. */ | ||||
| Path toStorePath(const Path & path); | ||||
| 
 | ||||
| 
 | ||||
| /* Follow symlinks until we end up with a path in the Nix store. */ | ||||
| Path followLinksToStore(const Path & path); | ||||
| 
 | ||||
| 
 | ||||
| /* Same as followLinksToStore(), but apply toStorePath() to the
 | ||||
|    result. */ | ||||
| Path followLinksToStorePath(const Path & path); | ||||
| 
 | ||||
| 
 | ||||
| /* Constructs a unique store path name. */ | ||||
| Path makeStorePath(const string & type, | ||||
|     const Hash & hash, const string & suffix); | ||||
|  |  | |||
|  | @ -105,8 +105,7 @@ Path canonPath(const Path & path, bool resolveSymlinks) | |||
|             /* If s points to a symlink, resolve it and restart (since
 | ||||
|                the symlink target might contain new symlinks). */ | ||||
|             if (resolveSymlinks && isLink(s)) { | ||||
|                 followCount++; | ||||
|                 if (followCount >= maxFollow) | ||||
|                 if (++followCount >= maxFollow) | ||||
|                     throw Error(format("infinite symlink recursion in path `%1%'") % path); | ||||
|                 temp = absPath(readLink(s), dirOf(s)) | ||||
|                     + string(i, end); | ||||
|  |  | |||
|  | @ -410,7 +410,7 @@ static DrvInfos filterBySelector(EvalState & state, const DrvInfos & allElems, | |||
|     /* Check that all selectors have been used. */ | ||||
|     for (DrvNames::iterator i = selectors.begin(); | ||||
|          i != selectors.end(); ++i) | ||||
|         if (i->hits == 0) | ||||
|         if (i->hits == 0 && i->fullName != "*") | ||||
|             throw Error(format("selector `%1%' matches no derivations") | ||||
|                 % i->fullName); | ||||
| 
 | ||||
|  | @ -418,12 +418,18 @@ static DrvInfos filterBySelector(EvalState & state, const DrvInfos & allElems, | |||
| } | ||||
| 
 | ||||
| 
 | ||||
| static bool isPath(const string & s) | ||||
| { | ||||
|     return s.find('/') != string::npos; | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| static void queryInstSources(EvalState & state, | ||||
|     const InstallSourceInfo & instSource, const Strings & args, | ||||
|     DrvInfos & elems, bool newestOnly) | ||||
| { | ||||
|     InstallSourceType type = instSource.type; | ||||
|     if (type == srcUnknown && args.size() > 0 && args.front()[0] == '/') | ||||
|     if (type == srcUnknown && args.size() > 0 && isPath(args.front())) | ||||
|         type = srcStorePaths; | ||||
|      | ||||
|     switch (type) { | ||||
|  | @ -475,23 +481,23 @@ static void queryInstSources(EvalState & state, | |||
|             for (Strings::const_iterator i = args.begin(); | ||||
|                  i != args.end(); ++i) | ||||
|             { | ||||
|                 assertStorePath(*i); | ||||
|                 Path path = followLinksToStorePath(*i); | ||||
| 
 | ||||
|                 DrvInfo elem; | ||||
|                 elem.attrs = boost::shared_ptr<ATermMap>(new ATermMap(0)); /* ugh... */ | ||||
|                 string name = baseNameOf(*i); | ||||
|                 string name = baseNameOf(path); | ||||
|                 string::size_type dash = name.find('-'); | ||||
|                 if (dash != string::npos) | ||||
|                     name = string(name, dash + 1); | ||||
| 
 | ||||
|                 if (isDerivation(*i)) { | ||||
|                     elem.setDrvPath(*i); | ||||
|                     elem.setOutPath(findOutput(derivationFromPath(*i), "out")); | ||||
|                 if (isDerivation(path)) { | ||||
|                     elem.setDrvPath(path); | ||||
|                     elem.setOutPath(findOutput(derivationFromPath(path), "out")); | ||||
|                     if (name.size() >= drvExtension.size() && | ||||
|                         string(name, name.size() - drvExtension.size()) == drvExtension) | ||||
|                         name = string(name, 0, name.size() - drvExtension.size()); | ||||
|                 } | ||||
|                 else elem.setOutPath(*i); | ||||
|                 else elem.setOutPath(path); | ||||
| 
 | ||||
|                 elem.name = name; | ||||
| 
 | ||||
|  | @ -811,7 +817,7 @@ static void opSet(Globals & globals, | |||
| } | ||||
| 
 | ||||
| 
 | ||||
| static void uninstallDerivations(Globals & globals, DrvNames & selectors, | ||||
| static void uninstallDerivations(Globals & globals, Strings & selectors, | ||||
|     Path & profile) | ||||
| { | ||||
|     PathLocks lock; | ||||
|  | @ -824,11 +830,13 @@ static void uninstallDerivations(Globals & globals, DrvNames & selectors, | |||
|     { | ||||
|         DrvName drvName(i->name); | ||||
|         bool found = false; | ||||
|         for (DrvNames::iterator j = selectors.begin(); | ||||
|              j != selectors.end(); ++j) | ||||
|             if (j->matches(drvName)) { | ||||
|                 printMsg(lvlInfo, | ||||
|                     format("uninstalling `%1%'") % i->name); | ||||
|         for (Strings::iterator j = selectors.begin(); j != selectors.end(); ++j) | ||||
|             /* !!! the repeated calls to followLinksToStorePath() are
 | ||||
|                expensive, should pre-compute them. */ | ||||
|             if ((isPath(*j) && i->queryOutPath(globals.state) == followLinksToStorePath(*j)) | ||||
|                 || DrvName(*j).matches(drvName)) | ||||
|             { | ||||
|                 printMsg(lvlInfo, format("uninstalling `%1%'") % i->name); | ||||
|                 found = true; | ||||
|                 break; | ||||
|             } | ||||
|  | @ -847,11 +855,7 @@ static void opUninstall(Globals & globals, | |||
| { | ||||
|     if (opFlags.size() > 0) | ||||
|         throw UsageError(format("unknown flag `%1%'") % opFlags.front()); | ||||
| 
 | ||||
|     DrvNames drvNames = drvNamesFromArgs(opArgs); | ||||
| 
 | ||||
|     uninstallDerivations(globals, drvNames, | ||||
|         globals.profile); | ||||
|     uninstallDerivations(globals, opArgs, globals.profile); | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
|  |  | |||
|  | @ -31,18 +31,6 @@ static int rootNr = 0; | |||
| static bool indirectRoot = false; | ||||
| 
 | ||||
| 
 | ||||
| static Path fixPath(Path path) | ||||
| { | ||||
|     path = absPath(path); | ||||
|     while (!isInStore(path)) { | ||||
|         if (!isLink(path)) break; | ||||
|         string target = readLink(path); | ||||
|         path = absPath(target, dirOf(path)); | ||||
|     } | ||||
|     return toStorePath(path); | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| static Path useDeriver(Path path) | ||||
| {        | ||||
|     if (!isDerivation(path)) { | ||||
|  | @ -86,7 +74,7 @@ static void opRealise(Strings opFlags, Strings opArgs) | |||
| 
 | ||||
|     for (Strings::iterator i = opArgs.begin(); | ||||
|          i != opArgs.end(); ++i) | ||||
|         *i = fixPath(*i); | ||||
|         *i = followLinksToStorePath(*i); | ||||
|              | ||||
|     if (opArgs.size() > 1) { | ||||
|         PathSet drvPaths; | ||||
|  | @ -296,7 +284,7 @@ static void opQuery(Strings opFlags, Strings opArgs) | |||
|             for (Strings::iterator i = opArgs.begin(); | ||||
|                  i != opArgs.end(); ++i) | ||||
|             { | ||||
|                 *i = fixPath(*i); | ||||
|                 *i = followLinksToStorePath(*i); | ||||
|                 if (forceRealise) realisePath(*i); | ||||
|                 Derivation drv = derivationFromPath(*i); | ||||
|                 cout << format("%1%\n") % findOutput(drv, "out"); | ||||
|  | @ -312,7 +300,7 @@ static void opQuery(Strings opFlags, Strings opArgs) | |||
|             for (Strings::iterator i = opArgs.begin(); | ||||
|                  i != opArgs.end(); ++i) | ||||
|             { | ||||
|                 Path path = maybeUseOutput(fixPath(*i), useOutput, forceRealise); | ||||
|                 Path path = maybeUseOutput(followLinksToStorePath(*i), useOutput, forceRealise); | ||||
|                 if (query == qRequisites) | ||||
|                     storePathRequisites(path, includeOutputs, paths); | ||||
|                 else if (query == qReferences) store->queryReferences(path, paths); | ||||
|  | @ -330,7 +318,7 @@ static void opQuery(Strings opFlags, Strings opArgs) | |||
|             for (Strings::iterator i = opArgs.begin(); | ||||
|                  i != opArgs.end(); ++i) | ||||
|             { | ||||
|                 Path deriver = store->queryDeriver(fixPath(*i)); | ||||
|                 Path deriver = store->queryDeriver(followLinksToStorePath(*i)); | ||||
|                 cout << format("%1%\n") % | ||||
|                     (deriver == "" ? "unknown-deriver" : deriver); | ||||
|             } | ||||
|  | @ -340,7 +328,7 @@ static void opQuery(Strings opFlags, Strings opArgs) | |||
|             for (Strings::iterator i = opArgs.begin(); | ||||
|                  i != opArgs.end(); ++i) | ||||
|             { | ||||
|                 Path path = useDeriver(fixPath(*i)); | ||||
|                 Path path = useDeriver(followLinksToStorePath(*i)); | ||||
|                 Derivation drv = derivationFromPath(path); | ||||
|                 StringPairs::iterator j = drv.env.find(bindingName); | ||||
|                 if (j == drv.env.end()) | ||||
|  | @ -354,7 +342,7 @@ static void opQuery(Strings opFlags, Strings opArgs) | |||
|             for (Strings::iterator i = opArgs.begin(); | ||||
|                  i != opArgs.end(); ++i) | ||||
|             { | ||||
|                 Path path = maybeUseOutput(fixPath(*i), useOutput, forceRealise); | ||||
|                 Path path = maybeUseOutput(followLinksToStorePath(*i), useOutput, forceRealise); | ||||
|                 Hash hash = store->queryPathHash(path); | ||||
|                 assert(hash.type == htSHA256); | ||||
|                 cout << format("sha256:%1%\n") % printHash32(hash); | ||||
|  | @ -365,7 +353,7 @@ static void opQuery(Strings opFlags, Strings opArgs) | |||
|             PathSet done; | ||||
|             for (Strings::iterator i = opArgs.begin(); | ||||
|                  i != opArgs.end(); ++i) | ||||
|                 printTree(fixPath(*i), "", "", done); | ||||
|                 printTree(followLinksToStorePath(*i), "", "", done); | ||||
|             break; | ||||
|         } | ||||
|              | ||||
|  | @ -373,7 +361,7 @@ static void opQuery(Strings opFlags, Strings opArgs) | |||
|             PathSet roots; | ||||
|             for (Strings::iterator i = opArgs.begin(); | ||||
|                  i != opArgs.end(); ++i) | ||||
|                 roots.insert(maybeUseOutput(fixPath(*i), useOutput, forceRealise)); | ||||
|                 roots.insert(maybeUseOutput(followLinksToStorePath(*i), useOutput, forceRealise)); | ||||
| 	    printDotGraph(roots); | ||||
|             break; | ||||
|         } | ||||
|  | @ -381,7 +369,7 @@ static void opQuery(Strings opFlags, Strings opArgs) | |||
|         case qResolve: { | ||||
|             for (Strings::iterator i = opArgs.begin(); | ||||
|                  i != opArgs.end(); ++i) | ||||
|                 cout << format("%1%\n") % fixPath(*i); | ||||
|                 cout << format("%1%\n") % followLinksToStorePath(*i); | ||||
|             break; | ||||
|         } | ||||
|              | ||||
|  | @ -398,7 +386,7 @@ static void opReadLog(Strings opFlags, Strings opArgs) | |||
|     for (Strings::iterator i = opArgs.begin(); | ||||
|          i != opArgs.end(); ++i) | ||||
|     { | ||||
|         Path path = useDeriver(fixPath(*i)); | ||||
|         Path path = useDeriver(followLinksToStorePath(*i)); | ||||
|          | ||||
|         Path logPath = (format("%1%/%2%/%3%") % | ||||
|             nixLogDir % drvsLogDir % baseNameOf(path)).str(); | ||||
|  | @ -456,7 +444,7 @@ static void opCheckValidity(Strings opFlags, Strings opArgs) | |||
|     for (Strings::iterator i = opArgs.begin(); | ||||
|          i != opArgs.end(); ++i) | ||||
|     { | ||||
|         Path path = fixPath(*i); | ||||
|         Path path = followLinksToStorePath(*i); | ||||
|         if (!store->isValidPath(path)) | ||||
|             if (printInvalid) | ||||
|                 cout << format("%1%\n") % path; | ||||
|  | @ -531,7 +519,7 @@ static void opDelete(Strings opFlags, Strings opArgs) | |||
|     PathSet pathsToDelete; | ||||
|     for (Strings::iterator i = opArgs.begin(); | ||||
|          i != opArgs.end(); ++i) | ||||
|         pathsToDelete.insert(fixPath(*i)); | ||||
|         pathsToDelete.insert(followLinksToStorePath(*i)); | ||||
|      | ||||
|     PathSet dummy; | ||||
|     PrintFreed freed(true, false); | ||||
|  |  | |||
|  | @ -2,6 +2,8 @@ source common.sh | |||
| 
 | ||||
| clearProfiles | ||||
| 
 | ||||
| set -x | ||||
| 
 | ||||
| # Query installed: should be empty. | ||||
| test "$($nixenv -p $profiles/test -q '*' | wc -l)" -eq 0 | ||||
| 
 | ||||
|  | @ -71,6 +73,15 @@ echo $outPath10 | |||
| $nixenv -p $profiles/test -i "$outPath10" | ||||
| $nixenv -p $profiles/test -q '*' | grep -q foo-1.0 | ||||
| 
 | ||||
| # Uninstall foo-1.0, using a symlink to its store path. | ||||
| ln -sfn $outPath10/bin/foo $TEST_ROOT/symlink | ||||
| $nixenv -p $profiles/test -e $TEST_ROOT/symlink | ||||
| if $nixenv -p $profiles/test -q '*' | grep -q foo; then false; fi | ||||
| 
 | ||||
| # Install foo-1.0, now using a symlink to its store path. | ||||
| $nixenv -p $profiles/test -i $TEST_ROOT/symlink | ||||
| $nixenv -p $profiles/test -q '*' | grep -q foo | ||||
| 
 | ||||
| # Delete all old generations. | ||||
| $nixenv -p $profiles/test --delete-generations old | ||||
| 
 | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue