forked from lix-project/lix
* 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
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
|
@ -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…
Reference in a new issue