about summary refs log tree commit diff
diff options
context:
space:
mode:
authorEelco Dolstra <e.dolstra@tudelft.nl>2007-11-29T16·18+0000
committerEelco Dolstra <e.dolstra@tudelft.nl>2007-11-29T16·18+0000
commit633518628f48fb9c06bfd570eeca6f62696aba05 (patch)
tree33313707305a011265e30d4670264a9b7979d298
parent12d0a1eb753567bb2083aadb4ee3d325d3f29c70 (diff)
* 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.

-rw-r--r--src/libstore/gc.cc9
-rw-r--r--src/libstore/store-api.cc20
-rw-r--r--src/libstore/store-api.hh10
-rw-r--r--src/libutil/util.cc3
-rw-r--r--src/nix-env/nix-env.cc42
-rw-r--r--src/nix-store/nix-store.cc36
-rw-r--r--tests/user-envs.sh11
7 files changed, 81 insertions, 50 deletions
diff --git a/src/libstore/gc.cc b/src/libstore/gc.cc
index c0acbab38a43..dab2b80aad06 100644
--- a/src/libstore/gc.cc
+++ b/src/libstore/gc.cc
@@ -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);
             }
diff --git a/src/libstore/store-api.cc b/src/libstore/store-api.cc
index 9eb313da01ff..22a66ccabdb2 100644
--- a/src/libstore/store-api.cc
+++ b/src/libstore/store-api.cc
@@ -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 = "+-._?=";
diff --git a/src/libstore/store-api.hh b/src/libstore/store-api.hh
index f133302b2f84..e44259ddaf09 100644
--- a/src/libstore/store-api.hh
+++ b/src/libstore/store-api.hh
@@ -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);
diff --git a/src/libutil/util.cc b/src/libutil/util.cc
index ed095717e262..37e158e4aff0 100644
--- a/src/libutil/util.cc
+++ b/src/libutil/util.cc
@@ -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);
diff --git a/src/nix-env/nix-env.cc b/src/nix-env/nix-env.cc
index 3396b191fd62..1af1a2f536d1 100644
--- a/src/nix-env/nix-env.cc
+++ b/src/nix-env/nix-env.cc
@@ -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);
 }
 
 
diff --git a/src/nix-store/nix-store.cc b/src/nix-store/nix-store.cc
index c919b0f25df6..f0e36463d48c 100644
--- a/src/nix-store/nix-store.cc
+++ b/src/nix-store/nix-store.cc
@@ -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);
diff --git a/tests/user-envs.sh b/tests/user-envs.sh
index 614c30ceb34d..edb6da0bfcb2 100644
--- a/tests/user-envs.sh
+++ b/tests/user-envs.sh
@@ -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