* 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:
Eelco Dolstra 2007-11-29 16:18:24 +00:00
parent 12d0a1eb75
commit 633518628f
7 changed files with 81 additions and 50 deletions

View file

@ -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,13 +503,12 @@ 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();
j != drv.outputs.end(); ++j) j != drv.outputs.end(); ++j)
if (store->isValidPath(j->second.path)) if (store->isValidPath(j->second.path))
computeFSClosure(j->second.path, livePaths); computeFSClosure(j->second.path, livePaths);
} }

View file

@ -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 = "+-._?=";

View file

@ -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);

View file

@ -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);

View file

@ -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);
} }

View file

@ -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);

View file

@ -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