1
1
Fork 0
mirror of https://github.com/NixOS/nix.git synced 2025-11-26 04:00:59 +01:00

* Maintain the references graph again.

* Only build a derivation if there are no trusted output paths in the
  equivalence classes for that derivation's outputs.
* Set the trust ID to the current user name, or use the value of the
  NIX_USER_ID environment variable.
This commit is contained in:
Eelco Dolstra 2005-05-27 10:54:32 +00:00
parent 75454567f7
commit 89635e16ba
14 changed files with 179 additions and 162 deletions

View file

@ -1,6 +1,7 @@
#include "build.hh" #include "build.hh"
#include "eval.hh" #include "eval.hh"
#include "globals.hh" #include "globals.hh"
#include "misc.hh"
#include "nixexpr-ast.hh" #include "nixexpr-ast.hh"

View file

@ -144,6 +144,17 @@ static void initAndRun(int argc, char * * argv)
improve. */ improve. */
srand(time(0)); srand(time(0));
/* Set the trust ID to the user name. */
currentTrustId = getEnv("NIX_USER_ID"); /* !!! dangerous? */
if (currentTrustId == "") {
SwitchToOriginalUser sw;
uid_t uid = geteuid();
struct passwd * pw = getpwuid(uid);
if (!pw) throw Error(format("unknown user ID %1%, go away") % uid);
currentTrustId = pw->pw_name;
}
printMsg(lvlError, format("trust ID is `%1%'") % currentTrustId);
/* Put the arguments in a vector. */ /* Put the arguments in a vector. */
Strings args, remaining; Strings args, remaining;
while (argc--) args.push_back(*argv++); while (argc--) args.push_back(*argv++);

View file

@ -2,7 +2,7 @@ noinst_LIBRARIES = libstore.a
libstore_a_SOURCES = \ libstore_a_SOURCES = \
store.cc store.hh derivations.cc derivations.hh \ store.cc store.hh derivations.cc derivations.hh \
build.cc misc.cc build.hh \ build.cc build.hh misc.cc misc.hh \
globals.cc globals.hh db.cc db.hh \ globals.cc globals.hh db.cc db.hh \
references.cc references.hh pathlocks.cc pathlocks.hh \ references.cc references.hh pathlocks.cc pathlocks.hh \
gc.cc gc.hh derivations-ast.hh gc.cc gc.hh derivations-ast.hh

View file

@ -14,6 +14,7 @@
#include "pathlocks.hh" #include "pathlocks.hh"
#include "globals.hh" #include "globals.hh"
#include "gc.hh" #include "gc.hh"
#include "misc.hh"
/* !!! TODO derivationFromPath shouldn't be used here */ /* !!! TODO derivationFromPath shouldn't be used here */
@ -409,7 +410,8 @@ private:
void writeLog(int fd, const unsigned char * buf, size_t count); void writeLog(int fd, const unsigned char * buf, size_t count);
/* Return the set of (in)valid paths. */ /* Return the set of (in)valid paths. */
PathSet checkPathValidity(bool returnValid); typedef set<OutputEqClass> OutputEqClasses;
OutputEqClasses checkOutputValidity(bool returnValid);
}; };
@ -477,9 +479,11 @@ void DerivationGoal::haveStoreExpr()
for (DerivationOutputs::iterator i = drv.outputs.begin(); for (DerivationOutputs::iterator i = drv.outputs.begin();
i != drv.outputs.end(); ++i) i != drv.outputs.end(); ++i)
addTempRoot(i->second.path); addTempRoot(i->second.path);
#endif
/* Check what outputs paths are not already valid. */ /* Check for what output path equivalence classes we do not
PathSet invalidOutputs = checkPathValidity(false); already have valid, trusted output paths. */
OutputEqClasses invalidOutputs = checkOutputValidity(false);
/* If they are all valid, then we're done. */ /* If they are all valid, then we're done. */
if (invalidOutputs.size() == 0) { if (invalidOutputs.size() == 0) {
@ -487,6 +491,7 @@ void DerivationGoal::haveStoreExpr()
return; return;
} }
#if 0
/* We are first going to try to create the invalid output paths /* We are first going to try to create the invalid output paths
through substitutes. If that doesn't work, we'll build through substitutes. If that doesn't work, we'll build
them. */ them. */
@ -514,12 +519,10 @@ void DerivationGoal::outputsSubstituted()
nrFailed = 0; nrFailed = 0;
#if 0 if (checkOutputValidity(false).size() == 0) {
if (checkPathValidity(false).size() == 0) {
amDone(true); amDone(true);
return; return;
} }
#endif
/* Otherwise, at least one of the output paths could not be /* Otherwise, at least one of the output paths could not be
produced using a substitute. So we have to build instead. */ produced using a substitute. So we have to build instead. */
@ -904,6 +907,9 @@ void DerivationGoal::terminateBuildHook()
bool DerivationGoal::prepareBuild() bool DerivationGoal::prepareBuild()
{ {
/* We direct each output of the derivation to a temporary location
in the Nix store. Afterwards, we move the outputs to their
final, content-addressed location. */
for (DerivationOutputs::iterator i = drv.outputs.begin(); for (DerivationOutputs::iterator i = drv.outputs.begin();
i != drv.outputs.end(); ++i) i != drv.outputs.end(); ++i)
{ {
@ -911,8 +917,20 @@ bool DerivationGoal::prepareBuild()
printMsg(lvlError, format("mapping output id `%1%', class `%2%' to `%3%'") printMsg(lvlError, format("mapping output id `%1%', class `%2%' to `%3%'")
% i->first % i->second.eqClass % tmpPath); % i->first % i->second.eqClass % tmpPath);
assert(i->second.eqClass.size() == tmpPath.size()); assert(i->second.eqClass.size() == tmpPath.size());
rewrites[hashPartOf(i->second.eqClass)] = hashPartOf(tmpPath);
debug(format("building path `%1%'") % tmpPath);
tmpOutputs[i->second.eqClass] = tmpPath; tmpOutputs[i->second.eqClass] = tmpPath;
/* This is a referenceable path. Make a note of that for when
we are scanning for references in the output. */
allPaths.insert(tmpPath);
/* The environment variables and command-line arguments of the
builder refer to the output path equivalence class. Cause
those references to be rewritten to the temporary
locations. */
rewrites[hashPartOf(i->second.eqClass)] = hashPartOf(tmpPath);
} }
/* Obtain locks on all output paths. The locks are automatically /* Obtain locks on all output paths. The locks are automatically
@ -948,19 +966,6 @@ bool DerivationGoal::prepareBuild()
} }
#endif #endif
/* Gather information necessary for computing the closure and/or
running the build hook. */
#if 0
/* The outputs are referenceable paths. */
for (DerivationOutputs::iterator i = drv.outputs.begin();
i != drv.outputs.end(); ++i)
{
debug(format("building path `%1%'") % i->second.path);
allPaths.insert(i->second.path);
}
#endif
/* Determine the full set of input paths. */ /* Determine the full set of input paths. */
/* First, the input derivations. */ /* First, the input derivations. */
@ -972,30 +977,19 @@ bool DerivationGoal::prepareBuild()
that are specified as inputs. */ that are specified as inputs. */
assert(isValidPath(i->first)); assert(isValidPath(i->first));
Derivation inDrv = derivationFromPath(i->first); Derivation inDrv = derivationFromPath(i->first);
for (StringSet::iterator j = i->second.begin(); for (StringSet::iterator j = i->second.begin();
j != i->second.end(); ++j) j != i->second.end(); ++j)
{
if (inDrv.outputs.find(*j) != inDrv.outputs.end()) { OutputEqClass eqClass = findOutputEqClass(inDrv, *j);
Path input = findTrustedEqClassMember(eqClass, currentTrustId);
OutputEqClass eqClass = inDrv.outputs[*j].eqClass; if (input == "")
OutputEqMembers members; throw Error(format("output `%1%' of derivation `%2%' is missing!")
queryOutputEqMembers(noTxn, eqClass, members); % *j % i->first);
if (members.size() == 0)
throw Error(format("output equivalence class `%1%' has no members!")
% eqClass);
Path input = members.front().path;
rewrites[hashPartOf(eqClass)] = hashPartOf(input); rewrites[hashPartOf(eqClass)] = hashPartOf(input);
#if 0 computeFSClosure(input, inputPaths);
computeFSClosure(inDrv.outputs[*j].path, inputPaths); }
#endif
} else
throw Error(
format("derivation `%1%' requires non-existent output `%2%' from input derivation `%3%'")
% drvPath % *j % i->first);
} }
/* Second, the input sources. */ /* Second, the input sources. */
@ -1003,7 +997,7 @@ bool DerivationGoal::prepareBuild()
i != drv.inputSrcs.end(); ++i) i != drv.inputSrcs.end(); ++i)
computeFSClosure(*i, inputPaths); computeFSClosure(*i, inputPaths);
debug(format("added input paths %1%") % showPaths(inputPaths)); printMsg(lvlError, format("added input paths %1%") % showPaths(inputPaths)); /* !!! */
allPaths.insert(inputPaths.begin(), inputPaths.end()); allPaths.insert(inputPaths.begin(), inputPaths.end());
@ -1014,7 +1008,7 @@ bool DerivationGoal::prepareBuild()
void DerivationGoal::startBuilder() void DerivationGoal::startBuilder()
{ {
startNest(nest, lvlInfo, startNest(nest, lvlInfo,
format("building path(s) XXX") /* % showPaths(outputPaths(drv.outputs)) */) format("building derivation `%1%'") % drvPath)
/* Right platform? */ /* Right platform? */
if (drv.platform != thisSystem) if (drv.platform != thisSystem)
@ -1151,43 +1145,16 @@ void DerivationGoal::computeClosure()
map<Path, PathSet> allReferences; map<Path, PathSet> allReferences;
map<Path, Hash> contentHashes; map<Path, Hash> contentHashes;
for (OutputMap::iterator i = tmpOutputs.begin();
i != tmpOutputs.end(); ++i)
{
/* Rewrite each output to a name matching its content hash.
I.e., enforce the hash invariant: the hash part of a store
path matches the contents at that path. */
Path finalPath = addToStore(i->second, hashPartOf(i->second),
namePartOf(i->second));
printMsg(lvlError, format("produced final path `%1%'") % finalPath);
/* Register the fact that this output path is a member of some
output path equivalence class (for a certain user, at
least). This is how subsequent derivations will be able to
find it. */
Transaction txn;
createStoreTransaction(txn);
addOutputEqMember(txn, i->first, "root", finalPath);
txn.commit();
}
#if 0
/* Check whether the output paths were created, and grep each
output path to determine what other paths it references. Also make all
output paths read-only. */
for (DerivationOutputs::iterator i = drv.outputs.begin(); for (DerivationOutputs::iterator i = drv.outputs.begin();
i != drv.outputs.end(); ++i) i != drv.outputs.end(); ++i)
{ {
Path path = i->second.path; Path path = tmpOutputs[i->second.eqClass];
if (!pathExists(path)) { if (!pathExists(path)) {
throw BuildError( throw BuildError(
format("builder for `%1%' failed to produce output path `%2%'") format("builder for `%1%' failed to produce output path `%2%'")
% drvPath % path); % drvPath % path);
} }
startNest(nest, lvlTalkative,
format("scanning for references inside `%1%'") % path);
/* Check that fixed-output derivations produced the right /* Check that fixed-output derivations produced the right
outputs (i.e., the content hash should match the specified outputs (i.e., the content hash should match the specified
hash). */ hash). */
@ -1225,64 +1192,50 @@ void DerivationGoal::computeClosure()
% path % algo % printHash(h) % printHash(h2)); % path % algo % printHash(h) % printHash(h2));
} }
canonicalisePathMetaData(path); /* For this output path, find the references to other paths
contained in it. */
/* For this output path, find the references to other paths contained PathSet referenced;
in it. */
PathSet references;
if (!pathExists(path + "/nix-support/no-scan")) { if (!pathExists(path + "/nix-support/no-scan")) {
Paths references2; Paths referenced2 = filterReferences(path,
references2 = filterReferences(path,
Paths(allPaths.begin(), allPaths.end())); Paths(allPaths.begin(), allPaths.end()));
references = PathSet(references2.begin(), references2.end()); referenced = PathSet(referenced2.begin(), referenced2.end());
/* For debugging, print out the referenced and /* For debugging, print out the referenced and
unreferenced paths. */ unreferenced paths. */
for (PathSet::iterator i = inputPaths.begin(); PathSet unreferenced;
i != inputPaths.end(); ++i) insert_iterator<PathSet> ins(unreferenced, unreferenced.begin());
{ set_difference(
PathSet::iterator j = references.find(*i); inputPaths.begin(), inputPaths.end(),
if (j == references.end()) referenced.begin(), referenced.end(), ins);
debug(format("unreferenced input: `%1%'") % *i); printMsg(lvlError, format("unreferenced inputs: %1%") % showPaths(unreferenced));
else printMsg(lvlError, format("referenced inputs: %1%") % showPaths(referenced));
debug(format("referenced input: `%1%'") % *i);
}
} }
allReferences[path] = references; /* Rewrite each output to a name matching its content hash.
I.e., enforce the hash invariant: the hash part of a store
path matches the contents at that path.
/* Hash the contents of the path. The hash is stored in the This also registers the final output path as valid, and
database so that we can verify later on whether nobody has sets it references. */
messed with the store. !!! inefficient: it would be nice Path finalPath = addToStore(path,
if we could combine this with filterReferences(). */ hashPartOf(path), namePartOf(path),
contentHashes[path] = hashPath(htSHA256, path); referenced);
} printMsg(lvlError, format("produced final path `%1%'") % finalPath);
#endif
#if 0 /* Register the fact that this output path is a member of some
/* Register each output path as valid, and register the sets of output path equivalence class (for a certain user, at
paths referenced by each of them. This is wrapped in one least). This is how subsequent derivations will be able to
database transaction to ensure that if we crash, either find it. */
everything is registered or nothing is. This is for
recoverability: unregistered paths in the store can be deleted
arbitrarily, while registered paths can only be deleted by
running the garbage collector.
The reason that we do the transaction here and not on the fly
while we are scanning (above) is so that we don't hold database
locks for too long. */
Transaction txn; Transaction txn;
createStoreTransaction(txn); createStoreTransaction(txn);
for (DerivationOutputs::iterator i = drv.outputs.begin(); addOutputEqMember(txn, i->second.eqClass, currentTrustId, finalPath);
i != drv.outputs.end(); ++i)
{
registerValidPath(txn, i->second.path,
contentHashes[i->second.path],
allReferences[i->second.path],
drvPath);
}
txn.commit(); txn.commit();
#endif
/* Get rid of the temporary output. !!! optimise all this by
*moving* the temporary output to the new location and
applying rewrites in situ. */
deletePath(path);
}
/* It is now safe to delete the lock files, since all future /* It is now safe to delete the lock files, since all future
lockers will see that the output paths are valid; they will not lockers will see that the output paths are valid; they will not
@ -1363,19 +1316,21 @@ void DerivationGoal::writeLog(int fd,
} }
PathSet DerivationGoal::checkPathValidity(bool returnValid) DerivationGoal::OutputEqClasses DerivationGoal::checkOutputValidity(bool returnValid)
{ {
#if 0 OutputEqClasses result;
PathSet result;
for (DerivationOutputs::iterator i = drv.outputs.begin(); for (DerivationOutputs::iterator i = drv.outputs.begin();
i != drv.outputs.end(); ++i) i != drv.outputs.end(); ++i)
if (isValidPath(i->second.path)) { {
if (returnValid) result.insert(i->second.path); Path path = findTrustedEqClassMember(i->second.eqClass, currentTrustId);
if (path != "") {
assert(isValidPath(path));
if (returnValid) result.insert(i->second.eqClass);
} else { } else {
if (!returnValid) result.insert(i->second.path); if (!returnValid) result.insert(i->second.eqClass);
}
} }
return result; return result;
#endif
} }

View file

@ -3,6 +3,7 @@
#include "derivations.hh" #include "derivations.hh"
/* Ensure that the output paths of the derivation are valid. If they /* Ensure that the output paths of the derivation are valid. If they
are already valid, this is a no-op. Otherwise, validity can are already valid, this is a no-op. Otherwise, validity can
be reached in two ways. First, if the output paths have be reached in two ways. First, if the output paths have
@ -11,26 +12,10 @@
sub-derivations. */ sub-derivations. */
void buildDerivations(const PathSet & drvPaths); void buildDerivations(const PathSet & drvPaths);
/* Ensure that a path is valid. If it is not currently valid, it may /* Ensure that a path is valid. If it is not currently valid, it may
be made valid by running a substitute (if defined for the path). */ be made valid by running a substitute (if defined for the path). */
void ensurePath(const Path & storePath); void ensurePath(const Path & storePath);
/* Read a derivation, after ensuring its existence through
ensurePath(). */
Derivation derivationFromPath(const Path & drvPath);
/* Place in `paths' the set of all store paths in the file system
closure of `storePath'; that is, all paths than can be directly or
indirectly reached from it. `paths' is not cleared. If
`flipDirection' is true, the set of paths that can reach
`storePath' is returned; that is, the closures under the `referers'
relation instead of the `references' relation is returned. */
void computeFSClosure(const Path & storePath,
PathSet & paths, bool flipDirection = false);
/* Return the path corresponding to the output identifier `id' in the
given derivation. */
Path findOutput(const Derivation & drv, string id);
#endif /* !__BUILD_H */ #endif /* !__BUILD_H */

View file

@ -2,6 +2,7 @@
#include "gc.hh" #include "gc.hh"
#include "build.hh" #include "build.hh"
#include "pathlocks.hh" #include "pathlocks.hh"
#include "misc.hh"
#include <boost/shared_ptr.hpp> #include <boost/shared_ptr.hpp>

View file

@ -22,6 +22,8 @@ unsigned int maxBuildJobs = 1;
bool readOnlyMode = false; bool readOnlyMode = false;
string currentTrustId;
static bool settingsRead = false; static bool settingsRead = false;

View file

@ -52,6 +52,10 @@ extern unsigned int maxBuildJobs;
database. */ database. */
extern bool readOnlyMode; extern bool readOnlyMode;
/* Current trust ID. !!! Of course, this shouldn't be a global
variable. */
extern string currentTrustId;
string querySetting(const string & name, const string & def); string querySetting(const string & name, const string & def);

View file

@ -29,13 +29,23 @@ void computeFSClosure(const Path & storePath,
} }
Path findOutput(const Derivation & drv, string id) OutputEqClass findOutputEqClass(const Derivation & drv, const string & id)
{ {
assert(0); DerivationOutputs::const_iterator i = drv.outputs.find(id);
#if 0 if (i == drv.outputs.end())
for (DerivationOutputs::const_iterator i = drv.outputs.begin();
i != drv.outputs.end(); ++i)
if (i->first == id) return i->second.path;
throw Error(format("derivation has no output `%1%'") % id); throw Error(format("derivation has no output `%1%'") % id);
#endif return i->second.eqClass;
}
Path findTrustedEqClassMember(const OutputEqClass & eqClass,
const TrustId & trustId)
{
OutputEqMembers members;
queryOutputEqMembers(noTxn, eqClass, members);
for (OutputEqMembers::iterator j = members.begin(); j != members.end(); ++j)
if (j->trustId == trustId || j->trustId == "root") return j->path;
return "";
} }

36
src/libstore/misc.hh Normal file
View file

@ -0,0 +1,36 @@
#ifndef __MISC_H
#define __MISC_H
#include "derivations.hh"
#include "store.hh"
/* Read a derivation, after ensuring its existence through
ensurePath(). */
Derivation derivationFromPath(const Path & drvPath);
/* Place in `paths' the set of all store paths in the file system
closure of `storePath'; that is, all paths than can be directly or
indirectly reached from it. `paths' is not cleared. If
`flipDirection' is true, the set of paths that can reach
`storePath' is returned; that is, the closures under the `referers'
relation instead of the `references' relation is returned. */
void computeFSClosure(const Path & storePath,
PathSet & paths, bool flipDirection = false);
/* Return the output equivalence class denoted by `id' in the
derivation `drv'. */
OutputEqClass findOutputEqClass(const Derivation & drv,
const string & id);
/* Return any trusted path (wrt to the given trust ID) in the given
output path equivalence class, or "" if no such path currently
exists. */
Path findTrustedEqClassMember(const OutputEqClass & eqClass,
const TrustId & trustId);
#endif /* !__MISC_H */

View file

@ -842,8 +842,8 @@ static Path _addToStore(const string & suffix, string dump,
/* If the contents had a previous hash reference, rewrite those /* If the contents had a previous hash reference, rewrite those
references to the new hash. */ references to the new hash. */
if (!selfHash.isNull()) {
HashRewrites rewrites; HashRewrites rewrites;
if (!selfHash.isNull()) {
rewrites[selfHash] = pathHash; rewrites[selfHash] = pathHash;
vector<int> positions; vector<int> positions;
dump = rewriteHashes(dump, rewrites, positions); dump = rewriteHashes(dump, rewrites, positions);
@ -872,8 +872,15 @@ static Path _addToStore(const string & suffix, string dump,
canonicalisePathMetaData(dstPath); canonicalisePathMetaData(dstPath);
/* Set the references for the new path. Of course, any
hash rewrites have to be applied to the references,
too. */
PathSet references2;
for (PathSet::iterator i = references.begin(); i != references.end(); ++i)
references2.insert(rewriteHashes(*i, rewrites));
Transaction txn(nixDB); Transaction txn(nixDB);
registerValidPath(txn, dstPath, contentHash, references, ""); registerValidPath(txn, dstPath, contentHash, references2, "");
txn.commit(); txn.commit();
} }
@ -885,7 +892,7 @@ static Path _addToStore(const string & suffix, string dump,
Path addToStore(const Path & _srcPath, const PathHash & selfHash, Path addToStore(const Path & _srcPath, const PathHash & selfHash,
const string & suffix) const string & suffix, const PathSet & references)
{ {
Path srcPath(absPath(_srcPath)); Path srcPath(absPath(_srcPath));
debug(format("adding `%1%' to the store") % srcPath); debug(format("adding `%1%' to the store") % srcPath);
@ -897,7 +904,7 @@ Path addToStore(const Path & _srcPath, const PathHash & selfHash,
} }
return _addToStore(suffix == "" ? baseNameOf(srcPath) : suffix, return _addToStore(suffix == "" ? baseNameOf(srcPath) : suffix,
sink.s, selfHash, PathSet()); sink.s, selfHash, references);
} }

View file

@ -226,7 +226,7 @@ string rewriteHashes(const string & s, const HashRewrites & rewrites);
/* Copy the contents of a path to the store and register the validity /* Copy the contents of a path to the store and register the validity
the resulting path. The resulting path is returned. */ the resulting path. The resulting path is returned. */
Path addToStore(const Path & srcPath, const PathHash & selfHash = PathHash(), Path addToStore(const Path & srcPath, const PathHash & selfHash = PathHash(),
const string & suffix = ""); const string & suffix = "", const PathSet & references = PathSet());
#if 0 #if 0
/* Like addToStore(), but for pre-adding the outputs of fixed-output /* Like addToStore(), but for pre-adding the outputs of fixed-output

View file

@ -3,6 +3,7 @@
#include "globals.hh" #include "globals.hh"
#include "build.hh" #include "build.hh"
#include "gc.hh" #include "gc.hh"
#include "misc.hh"
#include "shared.hh" #include "shared.hh"
#include "parser.hh" #include "parser.hh"
#include "eval.hh" #include "eval.hh"
@ -383,7 +384,6 @@ static void queryInstSources(EvalState & state,
(import ./foo.nix)' = `(import ./foo.nix).bar'. */ (import ./foo.nix)' = `(import ./foo.nix).bar'. */
case srcNixExprs: { case srcNixExprs: {
Expr e1 = parseExprFromFile(state, Expr e1 = parseExprFromFile(state,
absPath(instSource.nixExprPath)); absPath(instSource.nixExprPath));
@ -416,7 +416,10 @@ static void queryInstSources(EvalState & state,
if (isDerivation(*i)) { if (isDerivation(*i)) {
elem.setDrvPath(*i); elem.setDrvPath(*i);
elem.setOutPath(findOutput(derivationFromPath(*i), "out")); elem.setOutPath(
/* XXX check this; may not give a result */
findTrustedEqClassMember(
findOutputEqClass(derivationFromPath(*i), "out"), currentTrustId));
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());

View file

@ -4,6 +4,7 @@
#include "globals.hh" #include "globals.hh"
#include "build.hh" #include "build.hh"
#include "gc.hh" #include "gc.hh"
#include "misc.hh"
#include "archive.hh" #include "archive.hh"
#include "shared.hh" #include "shared.hh"
#include "dotgraph.hh" #include "dotgraph.hh"
@ -45,7 +46,8 @@ static Path realisePath(const Path & path)
PathSet paths; PathSet paths;
paths.insert(path); paths.insert(path);
buildDerivations(paths); buildDerivations(paths);
Path outPath = findOutput(derivationFromPath(path), "out"); Path outPath = findTrustedEqClassMember(
findOutputEqClass(derivationFromPath(path), "out"), currentTrustId);
if (gcRoot == "") if (gcRoot == "")
printGCWarning(); printGCWarning();