* Make the garbage collector do the right thing when `gc-keep-outputs'
is enabled by not depending on the deriver.
This commit is contained in:
parent
f0c0277970
commit
5388944e8d
|
@ -454,7 +454,12 @@ struct LocalStore::GCState
|
||||||
PathSet busy;
|
PathSet busy;
|
||||||
bool gcKeepOutputs;
|
bool gcKeepOutputs;
|
||||||
bool gcKeepDerivations;
|
bool gcKeepDerivations;
|
||||||
GCState(GCResults & results_) : results(results_)
|
|
||||||
|
bool drvsIndexed;
|
||||||
|
typedef std::multimap<string, Path> DrvsByName;
|
||||||
|
DrvsByName drvsByName; // derivation paths hashed by name attribute
|
||||||
|
|
||||||
|
GCState(GCResults & results_) : results(results_), drvsIndexed(false)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -474,6 +479,42 @@ bool LocalStore::isActiveTempFile(const GCState & state,
|
||||||
&& state.tempRoots.find(string(path, 0, path.size() - suffix.size())) != state.tempRoots.end();
|
&& state.tempRoots.find(string(path, 0, path.size() - suffix.size())) != state.tempRoots.end();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* Return all the derivations in the Nix store that have `path' as an
|
||||||
|
output. This function assumes that derivations have the same name
|
||||||
|
as their outputs. */
|
||||||
|
PathSet LocalStore::findDerivers(GCState & state, const Path & path)
|
||||||
|
{
|
||||||
|
PathSet derivers;
|
||||||
|
|
||||||
|
Path deriver = queryDeriver(path);
|
||||||
|
if (deriver != "") derivers.insert(deriver);
|
||||||
|
|
||||||
|
if (!state.drvsIndexed) {
|
||||||
|
Paths entries = readDirectory(nixStore);
|
||||||
|
foreach (Paths::iterator, i, entries)
|
||||||
|
if (isDerivation(*i))
|
||||||
|
state.drvsByName.insert(std::pair<string, Path>(
|
||||||
|
getNameOfStorePath(*i), nixStore + "/" + *i));
|
||||||
|
state.drvsIndexed = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
string name = getNameOfStorePath(path);
|
||||||
|
|
||||||
|
// Urgh, I should have used Haskell...
|
||||||
|
std::pair<GCState::DrvsByName::iterator, GCState::DrvsByName::iterator> range =
|
||||||
|
state.drvsByName.equal_range(name);
|
||||||
|
|
||||||
|
for (GCState::DrvsByName::iterator i = range.first; i != range.second; ++i)
|
||||||
|
if (isValidPath(i->second)) {
|
||||||
|
Derivation drv = derivationFromPath(i->second);
|
||||||
|
foreach (DerivationOutputs::iterator, j, drv.outputs)
|
||||||
|
if (j->second.path == path) derivers.insert(i->second);
|
||||||
|
}
|
||||||
|
|
||||||
|
return derivers;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
bool LocalStore::tryToDelete(GCState & state, const Path & path)
|
bool LocalStore::tryToDelete(GCState & state, const Path & path)
|
||||||
{
|
{
|
||||||
|
@ -519,24 +560,34 @@ bool LocalStore::tryToDelete(GCState & state, const Path & path)
|
||||||
if (!pathExists(path)) return true;
|
if (!pathExists(path)) return true;
|
||||||
|
|
||||||
/* If gc-keep-outputs is set, then don't delete this path if
|
/* If gc-keep-outputs is set, then don't delete this path if
|
||||||
its deriver is not garbage. !!! This is somewhat buggy,
|
its deriver is not garbage. !!! Nix does not reliably
|
||||||
since there might be multiple derivers, but the database
|
store derivers, so we have to look at all derivations to
|
||||||
only stores one. */
|
determine which of them derive `path'. Since this makes
|
||||||
|
the garbage collector very slow to start on large Nix
|
||||||
|
stores, here we just look for all derivations that have the
|
||||||
|
same name as `path' (where the name is the part of the
|
||||||
|
filename after the hash, i.e. the `name' attribute of the
|
||||||
|
derivation). This is somewhat hacky: currently, the
|
||||||
|
deriver of a path always has the same name as the output,
|
||||||
|
but this might change in the future. */
|
||||||
if (state.gcKeepOutputs) {
|
if (state.gcKeepOutputs) {
|
||||||
Path deriver = queryDeriver(path);
|
PathSet derivers = findDerivers(state, path);
|
||||||
/* Break an infinite recursion if gc-keep-derivations and
|
foreach (PathSet::iterator, deriver, derivers) {
|
||||||
gc-keep-outputs are both set by tentatively assuming
|
/* Break an infinite recursion if gc-keep-derivations
|
||||||
that this path is garbage. This is a safe assumption
|
and gc-keep-outputs are both set by tentatively
|
||||||
because at this point, the only thing that can prevent
|
assuming that this path is garbage. This is a safe
|
||||||
it from being garbage is the deriver. Since
|
assumption because at this point, the only thing
|
||||||
tryToDelete() works "upwards" through the dependency
|
that can prevent it from being garbage is the
|
||||||
graph, it won't encouter this path except in the call
|
deriver. Since tryToDelete() works "upwards"
|
||||||
to tryToDelete() in the gc-keep-derivation branch. */
|
through the dependency graph, it won't encouter
|
||||||
state.deleted.insert(path);
|
this path except in the call to tryToDelete() in
|
||||||
if (deriver != "" && !tryToDelete(state, deriver)) {
|
the gc-keep-derivation branch. */
|
||||||
state.deleted.erase(path);
|
state.deleted.insert(path);
|
||||||
printMsg(lvlDebug, format("cannot delete `%1%' because its deriver is alive") % path);
|
if (!tryToDelete(state, *deriver)) {
|
||||||
goto isLive;
|
state.deleted.erase(path);
|
||||||
|
printMsg(lvlDebug, format("cannot delete `%1%' because its deriver `%2%' is alive") % path % *deriver);
|
||||||
|
goto isLive;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -178,6 +178,8 @@ private:
|
||||||
|
|
||||||
bool tryToDelete(GCState & state, const Path & path);
|
bool tryToDelete(GCState & state, const Path & path);
|
||||||
|
|
||||||
|
PathSet findDerivers(GCState & state, const Path & path);
|
||||||
|
|
||||||
bool isActiveTempFile(const GCState & state,
|
bool isActiveTempFile(const GCState & state,
|
||||||
const Path & path, const string & suffix);
|
const Path & path, const string & suffix);
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
#include "store-api.hh"
|
#include "store-api.hh"
|
||||||
#include "globals.hh"
|
#include "globals.hh"
|
||||||
#include "util.hh"
|
#include "util.hh"
|
||||||
|
#include "derivations.hh"
|
||||||
|
|
||||||
#include <limits.h>
|
#include <limits.h>
|
||||||
|
|
||||||
|
@ -52,6 +53,18 @@ Path toStorePath(const Path & path)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
string getNameOfStorePath(const Path & path)
|
||||||
|
{
|
||||||
|
Path::size_type slash = path.rfind('/');
|
||||||
|
string p = slash == Path::npos ? path : string(path, slash + 1);
|
||||||
|
Path::size_type dash = p.find('-');
|
||||||
|
assert(dash != Path::npos);
|
||||||
|
string p2 = string(p, dash + 1);
|
||||||
|
if (isDerivation(p2)) p2 = string(p2, 0, p2.size() - 4);
|
||||||
|
return p2;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
Path followLinksToStore(const Path & _path)
|
Path followLinksToStore(const Path & _path)
|
||||||
{
|
{
|
||||||
Path path = absPath(_path);
|
Path path = absPath(_path);
|
||||||
|
|
|
@ -243,6 +243,12 @@ void checkStoreName(const string & name);
|
||||||
Path toStorePath(const Path & path);
|
Path toStorePath(const Path & path);
|
||||||
|
|
||||||
|
|
||||||
|
/* Get the "name" part of a store path, that is, the part after the
|
||||||
|
hash and the dash, and with any ".drv" suffix removed
|
||||||
|
(e.g. /nix/store/<hash>-foo-1.2.3.drv => foo-1.2.3). */
|
||||||
|
string getNameOfStorePath(const Path & path);
|
||||||
|
|
||||||
|
|
||||||
/* Follow symlinks until we end up with a path in the Nix store. */
|
/* Follow symlinks until we end up with a path in the Nix store. */
|
||||||
Path followLinksToStore(const Path & path);
|
Path followLinksToStore(const Path & path);
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue