* 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
4 changed files with 90 additions and 18 deletions
|
@ -454,7 +454,12 @@ struct LocalStore::GCState
|
|||
PathSet busy;
|
||||
bool gcKeepOutputs;
|
||||
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();
|
||||
}
|
||||
|
||||
|
||||
/* 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)
|
||||
{
|
||||
|
@ -519,24 +560,34 @@ bool LocalStore::tryToDelete(GCState & state, const Path & path)
|
|||
if (!pathExists(path)) return true;
|
||||
|
||||
/* If gc-keep-outputs is set, then don't delete this path if
|
||||
its deriver is not garbage. !!! This is somewhat buggy,
|
||||
since there might be multiple derivers, but the database
|
||||
only stores one. */
|
||||
its deriver is not garbage. !!! Nix does not reliably
|
||||
store derivers, so we have to look at all derivations to
|
||||
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) {
|
||||
Path deriver = queryDeriver(path);
|
||||
/* Break an infinite recursion if gc-keep-derivations and
|
||||
gc-keep-outputs are both set by tentatively assuming
|
||||
that this path is garbage. This is a safe assumption
|
||||
because at this point, the only thing that can prevent
|
||||
it from being garbage is the deriver. Since
|
||||
tryToDelete() works "upwards" through the dependency
|
||||
graph, it won't encouter this path except in the call
|
||||
to tryToDelete() in the gc-keep-derivation branch. */
|
||||
state.deleted.insert(path);
|
||||
if (deriver != "" && !tryToDelete(state, deriver)) {
|
||||
state.deleted.erase(path);
|
||||
printMsg(lvlDebug, format("cannot delete `%1%' because its deriver is alive") % path);
|
||||
goto isLive;
|
||||
PathSet derivers = findDerivers(state, path);
|
||||
foreach (PathSet::iterator, deriver, derivers) {
|
||||
/* Break an infinite recursion if gc-keep-derivations
|
||||
and gc-keep-outputs are both set by tentatively
|
||||
assuming that this path is garbage. This is a safe
|
||||
assumption because at this point, the only thing
|
||||
that can prevent it from being garbage is the
|
||||
deriver. Since tryToDelete() works "upwards"
|
||||
through the dependency graph, it won't encouter
|
||||
this path except in the call to tryToDelete() in
|
||||
the gc-keep-derivation branch. */
|
||||
state.deleted.insert(path);
|
||||
if (!tryToDelete(state, *deriver)) {
|
||||
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);
|
||||
|
||||
PathSet findDerivers(GCState & state, const Path & path);
|
||||
|
||||
bool isActiveTempFile(const GCState & state,
|
||||
const Path & path, const string & suffix);
|
||||
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
#include "store-api.hh"
|
||||
#include "globals.hh"
|
||||
#include "util.hh"
|
||||
#include "derivations.hh"
|
||||
|
||||
#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 path = absPath(_path);
|
||||
|
|
|
@ -243,6 +243,12 @@ void checkStoreName(const string & name);
|
|||
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. */
|
||||
Path followLinksToStore(const Path & path);
|
||||
|
||||
|
|
Loading…
Reference in a new issue