* 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 tempRootsDir = "temproots"; | ||||||
| static string gcRootsDir = "gcroots"; | 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
 | /* 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); |         queryBoolSetting("gc-keep-outputs", false); | ||||||
|     bool gcKeepDerivations = |     bool gcKeepDerivations = | ||||||
|         queryBoolSetting("gc-keep-derivations", true); |         queryBoolSetting("gc-keep-derivations", true); | ||||||
|     unsigned int gcKeepOutputsThreshold =  |     int gcKeepOutputsThreshold =  | ||||||
|         queryIntSetting ("gc-keep-outputs-threshold", defaultGcLevel); |         queryIntSetting ("gc-keep-outputs-threshold", defaultGcLevel); | ||||||
| 
 | 
 | ||||||
|     /* Acquire the global GC root.  This prevents
 |     /* Acquire the global GC root.  This prevents
 | ||||||
|  | @ -503,9 +503,8 @@ void LocalStore::collectGarbage(GCAction action, const PathSet & pathsToDelete, | ||||||
| 
 | 
 | ||||||
| 		string gcLevelStr = drv.env["__gcLevel"]; | 		string gcLevelStr = drv.env["__gcLevel"]; | ||||||
| 		int gcLevel; | 		int gcLevel; | ||||||
| 		if (!string2Int(gcLevelStr,gcLevel)) { | 		if (!string2Int(gcLevelStr, gcLevel)) | ||||||
| 		    gcLevel = defaultGcLevel; | 		    gcLevel = defaultGcLevel; | ||||||
| 		} |  | ||||||
| 		 | 		 | ||||||
| 		if (gcLevel >= gcKeepOutputsThreshold)     | 		if (gcLevel >= gcKeepOutputsThreshold)     | ||||||
| 		    for (DerivationOutputs::iterator j = drv.outputs.begin(); | 		    for (DerivationOutputs::iterator j = drv.outputs.begin(); | ||||||
|  |  | ||||||
|  | @ -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) | void checkStoreName(const string & name) | ||||||
| { | { | ||||||
|     string validChars = "+-._?="; |     string validChars = "+-._?="; | ||||||
|  |  | ||||||
|  | @ -174,11 +174,21 @@ bool isStorePath(const Path & path); | ||||||
| 
 | 
 | ||||||
| void checkStoreName(const string & name); | void checkStoreName(const string & name); | ||||||
| 
 | 
 | ||||||
|  | 
 | ||||||
| /* Chop off the parts after the top-level store name, e.g.,
 | /* Chop off the parts after the top-level store name, e.g.,
 | ||||||
|    /nix/store/abcd-foo/bar => /nix/store/abcd-foo. */ |    /nix/store/abcd-foo/bar => /nix/store/abcd-foo. */ | ||||||
| Path toStorePath(const Path & path); | 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. */ | /* Constructs a unique store path name. */ | ||||||
| Path makeStorePath(const string & type, | Path makeStorePath(const string & type, | ||||||
|     const Hash & hash, const string & suffix); |     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
 |             /* If s points to a symlink, resolve it and restart (since
 | ||||||
|                the symlink target might contain new symlinks). */ |                the symlink target might contain new symlinks). */ | ||||||
|             if (resolveSymlinks && isLink(s)) { |             if (resolveSymlinks && isLink(s)) { | ||||||
|                 followCount++; |                 if (++followCount >= maxFollow) | ||||||
|                 if (followCount >= maxFollow) |  | ||||||
|                     throw Error(format("infinite symlink recursion in path `%1%'") % path); |                     throw Error(format("infinite symlink recursion in path `%1%'") % path); | ||||||
|                 temp = absPath(readLink(s), dirOf(s)) |                 temp = absPath(readLink(s), dirOf(s)) | ||||||
|                     + string(i, end); |                     + string(i, end); | ||||||
|  |  | ||||||
|  | @ -410,7 +410,7 @@ static DrvInfos filterBySelector(EvalState & state, const DrvInfos & allElems, | ||||||
|     /* Check that all selectors have been used. */ |     /* Check that all selectors have been used. */ | ||||||
|     for (DrvNames::iterator i = selectors.begin(); |     for (DrvNames::iterator i = selectors.begin(); | ||||||
|          i != selectors.end(); ++i) |          i != selectors.end(); ++i) | ||||||
|         if (i->hits == 0) |         if (i->hits == 0 && i->fullName != "*") | ||||||
|             throw Error(format("selector `%1%' matches no derivations") |             throw Error(format("selector `%1%' matches no derivations") | ||||||
|                 % i->fullName); |                 % 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, | static void queryInstSources(EvalState & state, | ||||||
|     const InstallSourceInfo & instSource, const Strings & args, |     const InstallSourceInfo & instSource, const Strings & args, | ||||||
|     DrvInfos & elems, bool newestOnly) |     DrvInfos & elems, bool newestOnly) | ||||||
| { | { | ||||||
|     InstallSourceType type = instSource.type; |     InstallSourceType type = instSource.type; | ||||||
|     if (type == srcUnknown && args.size() > 0 && args.front()[0] == '/') |     if (type == srcUnknown && args.size() > 0 && isPath(args.front())) | ||||||
|         type = srcStorePaths; |         type = srcStorePaths; | ||||||
|      |      | ||||||
|     switch (type) { |     switch (type) { | ||||||
|  | @ -475,23 +481,23 @@ static void queryInstSources(EvalState & state, | ||||||
|             for (Strings::const_iterator i = args.begin(); |             for (Strings::const_iterator i = args.begin(); | ||||||
|                  i != args.end(); ++i) |                  i != args.end(); ++i) | ||||||
|             { |             { | ||||||
|                 assertStorePath(*i); |                 Path path = followLinksToStorePath(*i); | ||||||
| 
 | 
 | ||||||
|                 DrvInfo elem; |                 DrvInfo elem; | ||||||
|                 elem.attrs = boost::shared_ptr<ATermMap>(new ATermMap(0)); /* ugh... */ |                 elem.attrs = boost::shared_ptr<ATermMap>(new ATermMap(0)); /* ugh... */ | ||||||
|                 string name = baseNameOf(*i); |                 string name = baseNameOf(path); | ||||||
|                 string::size_type dash = name.find('-'); |                 string::size_type dash = name.find('-'); | ||||||
|                 if (dash != string::npos) |                 if (dash != string::npos) | ||||||
|                     name = string(name, dash + 1); |                     name = string(name, dash + 1); | ||||||
| 
 | 
 | ||||||
|                 if (isDerivation(*i)) { |                 if (isDerivation(path)) { | ||||||
|                     elem.setDrvPath(*i); |                     elem.setDrvPath(path); | ||||||
|                     elem.setOutPath(findOutput(derivationFromPath(*i), "out")); |                     elem.setOutPath(findOutput(derivationFromPath(path), "out")); | ||||||
|                     if (name.size() >= drvExtension.size() && |                     if (name.size() >= drvExtension.size() && | ||||||
|                         string(name, name.size() - drvExtension.size()) == drvExtension) |                         string(name, name.size() - drvExtension.size()) == drvExtension) | ||||||
|                         name = string(name, 0, name.size() - drvExtension.size()); |                         name = string(name, 0, name.size() - drvExtension.size()); | ||||||
|                 } |                 } | ||||||
|                 else elem.setOutPath(*i); |                 else elem.setOutPath(path); | ||||||
| 
 | 
 | ||||||
|                 elem.name = name; |                 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) |     Path & profile) | ||||||
| { | { | ||||||
|     PathLocks lock; |     PathLocks lock; | ||||||
|  | @ -824,11 +830,13 @@ static void uninstallDerivations(Globals & globals, DrvNames & selectors, | ||||||
|     { |     { | ||||||
|         DrvName drvName(i->name); |         DrvName drvName(i->name); | ||||||
|         bool found = false; |         bool found = false; | ||||||
|         for (DrvNames::iterator j = selectors.begin(); |         for (Strings::iterator j = selectors.begin(); j != selectors.end(); ++j) | ||||||
|              j != selectors.end(); ++j) |             /* !!! the repeated calls to followLinksToStorePath() are
 | ||||||
|             if (j->matches(drvName)) { |                expensive, should pre-compute them. */ | ||||||
|                 printMsg(lvlInfo, |             if ((isPath(*j) && i->queryOutPath(globals.state) == followLinksToStorePath(*j)) | ||||||
|                     format("uninstalling `%1%'") % i->name); |                 || DrvName(*j).matches(drvName)) | ||||||
|  |             { | ||||||
|  |                 printMsg(lvlInfo, format("uninstalling `%1%'") % i->name); | ||||||
|                 found = true; |                 found = true; | ||||||
|                 break; |                 break; | ||||||
|             } |             } | ||||||
|  | @ -847,11 +855,7 @@ static void opUninstall(Globals & globals, | ||||||
| { | { | ||||||
|     if (opFlags.size() > 0) |     if (opFlags.size() > 0) | ||||||
|         throw UsageError(format("unknown flag `%1%'") % opFlags.front()); |         throw UsageError(format("unknown flag `%1%'") % opFlags.front()); | ||||||
| 
 |     uninstallDerivations(globals, opArgs, globals.profile); | ||||||
|     DrvNames drvNames = drvNamesFromArgs(opArgs); |  | ||||||
| 
 |  | ||||||
|     uninstallDerivations(globals, drvNames, |  | ||||||
|         globals.profile); |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -31,18 +31,6 @@ static int rootNr = 0; | ||||||
| static bool indirectRoot = false; | 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) | static Path useDeriver(Path path) | ||||||
| {        | {        | ||||||
|     if (!isDerivation(path)) { |     if (!isDerivation(path)) { | ||||||
|  | @ -86,7 +74,7 @@ static void opRealise(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 = fixPath(*i); |         *i = followLinksToStorePath(*i); | ||||||
|              |              | ||||||
|     if (opArgs.size() > 1) { |     if (opArgs.size() > 1) { | ||||||
|         PathSet drvPaths; |         PathSet drvPaths; | ||||||
|  | @ -296,7 +284,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 = fixPath(*i); |                 *i = followLinksToStorePath(*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"); | ||||||
|  | @ -312,7 +300,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) | ||||||
|             { |             { | ||||||
|                 Path path = maybeUseOutput(fixPath(*i), useOutput, forceRealise); |                 Path path = maybeUseOutput(followLinksToStorePath(*i), useOutput, forceRealise); | ||||||
|                 if (query == qRequisites) |                 if (query == qRequisites) | ||||||
|                     storePathRequisites(path, includeOutputs, paths); |                     storePathRequisites(path, includeOutputs, paths); | ||||||
|                 else if (query == qReferences) store->queryReferences(path, 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(); |             for (Strings::iterator i = opArgs.begin(); | ||||||
|                  i != opArgs.end(); ++i) |                  i != opArgs.end(); ++i) | ||||||
|             { |             { | ||||||
|                 Path deriver = store->queryDeriver(fixPath(*i)); |                 Path deriver = store->queryDeriver(followLinksToStorePath(*i)); | ||||||
|                 cout << format("%1%\n") % |                 cout << format("%1%\n") % | ||||||
|                     (deriver == "" ? "unknown-deriver" : deriver); |                     (deriver == "" ? "unknown-deriver" : deriver); | ||||||
|             } |             } | ||||||
|  | @ -340,7 +328,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) | ||||||
|             { |             { | ||||||
|                 Path path = useDeriver(fixPath(*i)); |                 Path path = useDeriver(followLinksToStorePath(*i)); | ||||||
|                 Derivation drv = derivationFromPath(path); |                 Derivation drv = derivationFromPath(path); | ||||||
|                 StringPairs::iterator j = drv.env.find(bindingName); |                 StringPairs::iterator j = drv.env.find(bindingName); | ||||||
|                 if (j == drv.env.end()) |                 if (j == drv.env.end()) | ||||||
|  | @ -354,7 +342,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) | ||||||
|             { |             { | ||||||
|                 Path path = maybeUseOutput(fixPath(*i), useOutput, forceRealise); |                 Path path = maybeUseOutput(followLinksToStorePath(*i), useOutput, forceRealise); | ||||||
|                 Hash hash = store->queryPathHash(path); |                 Hash hash = store->queryPathHash(path); | ||||||
|                 assert(hash.type == htSHA256); |                 assert(hash.type == htSHA256); | ||||||
|                 cout << format("sha256:%1%\n") % printHash32(hash); |                 cout << format("sha256:%1%\n") % printHash32(hash); | ||||||
|  | @ -365,7 +353,7 @@ static void opQuery(Strings opFlags, Strings opArgs) | ||||||
|             PathSet done; |             PathSet done; | ||||||
|             for (Strings::iterator i = opArgs.begin(); |             for (Strings::iterator i = opArgs.begin(); | ||||||
|                  i != opArgs.end(); ++i) |                  i != opArgs.end(); ++i) | ||||||
|                 printTree(fixPath(*i), "", "", done); |                 printTree(followLinksToStorePath(*i), "", "", done); | ||||||
|             break; |             break; | ||||||
|         } |         } | ||||||
|              |              | ||||||
|  | @ -373,7 +361,7 @@ static void opQuery(Strings opFlags, Strings opArgs) | ||||||
|             PathSet roots; |             PathSet roots; | ||||||
|             for (Strings::iterator i = opArgs.begin(); |             for (Strings::iterator i = opArgs.begin(); | ||||||
|                  i != opArgs.end(); ++i) |                  i != opArgs.end(); ++i) | ||||||
|                 roots.insert(maybeUseOutput(fixPath(*i), useOutput, forceRealise)); |                 roots.insert(maybeUseOutput(followLinksToStorePath(*i), useOutput, forceRealise)); | ||||||
| 	    printDotGraph(roots); | 	    printDotGraph(roots); | ||||||
|             break; |             break; | ||||||
|         } |         } | ||||||
|  | @ -381,7 +369,7 @@ static void opQuery(Strings opFlags, Strings opArgs) | ||||||
|         case qResolve: { |         case qResolve: { | ||||||
|             for (Strings::iterator i = opArgs.begin(); |             for (Strings::iterator i = opArgs.begin(); | ||||||
|                  i != opArgs.end(); ++i) |                  i != opArgs.end(); ++i) | ||||||
|                 cout << format("%1%\n") % fixPath(*i); |                 cout << format("%1%\n") % followLinksToStorePath(*i); | ||||||
|             break; |             break; | ||||||
|         } |         } | ||||||
|              |              | ||||||
|  | @ -398,7 +386,7 @@ static void opReadLog(Strings opFlags, Strings opArgs) | ||||||
|     for (Strings::iterator i = opArgs.begin(); |     for (Strings::iterator i = opArgs.begin(); | ||||||
|          i != opArgs.end(); ++i) |          i != opArgs.end(); ++i) | ||||||
|     { |     { | ||||||
|         Path path = useDeriver(fixPath(*i)); |         Path path = useDeriver(followLinksToStorePath(*i)); | ||||||
|          |          | ||||||
|         Path logPath = (format("%1%/%2%/%3%") % |         Path logPath = (format("%1%/%2%/%3%") % | ||||||
|             nixLogDir % drvsLogDir % baseNameOf(path)).str(); |             nixLogDir % drvsLogDir % baseNameOf(path)).str(); | ||||||
|  | @ -456,7 +444,7 @@ static void opCheckValidity(Strings opFlags, Strings opArgs) | ||||||
|     for (Strings::iterator i = opArgs.begin(); |     for (Strings::iterator i = opArgs.begin(); | ||||||
|          i != opArgs.end(); ++i) |          i != opArgs.end(); ++i) | ||||||
|     { |     { | ||||||
|         Path path = fixPath(*i); |         Path path = followLinksToStorePath(*i); | ||||||
|         if (!store->isValidPath(path)) |         if (!store->isValidPath(path)) | ||||||
|             if (printInvalid) |             if (printInvalid) | ||||||
|                 cout << format("%1%\n") % path; |                 cout << format("%1%\n") % path; | ||||||
|  | @ -531,7 +519,7 @@ static void opDelete(Strings opFlags, Strings opArgs) | ||||||
|     PathSet pathsToDelete; |     PathSet pathsToDelete; | ||||||
|     for (Strings::iterator i = opArgs.begin(); |     for (Strings::iterator i = opArgs.begin(); | ||||||
|          i != opArgs.end(); ++i) |          i != opArgs.end(); ++i) | ||||||
|         pathsToDelete.insert(fixPath(*i)); |         pathsToDelete.insert(followLinksToStorePath(*i)); | ||||||
|      |      | ||||||
|     PathSet dummy; |     PathSet dummy; | ||||||
|     PrintFreed freed(true, false); |     PrintFreed freed(true, false); | ||||||
|  |  | ||||||
|  | @ -2,6 +2,8 @@ source common.sh | ||||||
| 
 | 
 | ||||||
| clearProfiles | clearProfiles | ||||||
| 
 | 
 | ||||||
|  | set -x | ||||||
|  | 
 | ||||||
| # Query installed: should be empty. | # Query installed: should be empty. | ||||||
| test "$($nixenv -p $profiles/test -q '*' | wc -l)" -eq 0 | 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 -i "$outPath10" | ||||||
| $nixenv -p $profiles/test -q '*' | grep -q foo-1.0 | $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. | # Delete all old generations. | ||||||
| $nixenv -p $profiles/test --delete-generations old | $nixenv -p $profiles/test --delete-generations old | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue