Support building a derivation if some outputs are already valid (non-chroot case)

This uses scary hash rewriting.

Fixes #21.
This commit is contained in:
Eelco Dolstra 2012-09-11 18:39:22 -04:00
parent 9dbda2b3fe
commit 8cf672091e

View file

@ -731,6 +731,26 @@ HookInstance::~HookInstance()
//////////////////////////////////////////////////////////////////////
typedef map<string, string> HashRewrites;
string rewriteHashes(string s, const HashRewrites & rewrites)
{
foreach (HashRewrites::const_iterator, i, rewrites) {
assert(i->first.size() == i->second.size());
size_t j = 0;
while ((j = s.find(i->first, j)) != string::npos) {
debug(format("rewriting @ %1%") % j);
s.replace(j, i->second.size(), i->second);
}
}
return s;
}
//////////////////////////////////////////////////////////////////////
typedef enum {rpAccept, rpDecline, rpPostpone} HookReply;
class SubstitutionGoal;
@ -801,6 +821,10 @@ private:
typedef map<string, string> Environment;
Environment env;
/* Hash rewriting. */
HashRewrites rewritesToTmp, rewritesFromTmp;
PathSet redirectedOutputs;
public:
DerivationGoal(const Path & drvPath, Worker & worker);
~DerivationGoal();
@ -1277,14 +1301,13 @@ void DerivationGoal::buildDone()
foreach (DerivationOutputs::iterator, i, drv.outputs) {
Path path = i->second.path;
if (useChroot && pathExists(chrootRootDir + path)) {
/* Move output paths from the chroot to the Nix store.
If the output was already valid, just skip
(discard) it. */
if (validPaths.find(path) != validPaths.end()) continue;
/* If the output was already valid, just skip (discard) it. */
if (validPaths.find(path) != validPaths.end()) continue;
if (useChroot && pathExists(chrootRootDir + path))
/* Move output paths from the chroot to the Nix store. */
if (rename((chrootRootDir + path).c_str(), path.c_str()) == -1)
throw SysError(format("moving build output `%1%' from the chroot to the Nix store") % path);
}
if (!pathExists(path)) continue;
@ -1302,6 +1325,17 @@ void DerivationGoal::buildDone()
throw BuildError(format("suspicious ownership or permission on `%1%'; rejecting this build output") % path);
#endif
/* Apply hash rewriting if necessary. */
if (!rewritesFromTmp.empty()) {
printMsg(lvlError, format("warning: rewriting hashes in `%1%'; cross fingers") % path);
/* FIXME: this is in-memory. */
StringSink sink;
dumpPath(path, sink);
deletePath(path);
StringSource source(rewriteHashes(sink.s, rewritesFromTmp));
restorePath(path, source);
}
/* Gain ownership of the build result using the setuid
wrapper if we're not root. If we *are* root, then
canonicalisePathMetaData() will take care of this later
@ -1327,6 +1361,10 @@ void DerivationGoal::buildDone()
foreach (PathSet::iterator, i, regularInputPaths)
makeImmutable(*i);
/* Delete redirected outputs (when doing hash rewriting). */
foreach (PathSet::iterator, i, redirectedOutputs)
deletePath(*i);
/* Compute the FS closure of the outputs and register them as
being valid. */
computeClosure();
@ -1746,9 +1784,27 @@ void DerivationGoal::startBuilder()
#else
throw Error("chroot builds are not supported on this platform");
#endif
} else { // !useChroot
if (validPaths.size() > 0)
throw Error(format("derivation `%1%' is blocked by its output path(s) %2%") % drvPath % showPaths(validPaths));
}
/* We're not doing a chroot build, but we have some valid output
paths. Since we can't just overwrite or delete them, we have
to do hash rewriting: i.e. in the environment/arguments passed
to the build, we replace the hashes of the valid outputs with
unique dummy strings; after the build, we discard the
redirected outputs corresponding to the valid outputs, and
rewrite the contents of the new outputs to replace the dummy
strings with the actual hashes. */
else if (validPaths.size() > 0) {
//throw Error(format("derivation `%1%' is blocked by its output path(s) %2%") % drvPath % showPaths(validPaths));
foreach (PathSet::iterator, i, validPaths) {
string h1 = string(*i, settings.nixStore.size() + 1, 32);
string h2 = string(printHash32(hashString(htSHA256, "rewrite:" + drvPath + ":" + *i)), 0, 32);
Path p = settings.nixStore + "/" + h2 + string(*i, settings.nixStore.size() + 33);
assert(i->size() == p.size());
rewritesToTmp[h1] = h2;
rewritesFromTmp[h2] = h1;
redirectedOutputs.insert(p);
}
}
@ -1925,7 +1981,7 @@ void DerivationGoal::initChild()
/* Fill in the environment. */
Strings envStrs;
foreach (Environment::const_iterator, i, env)
envStrs.push_back(i->first + "=" + i->second);
envStrs.push_back(rewriteHashes(i->first + "=" + i->second, rewritesToTmp));
const char * * envArr = strings2CharPtrs(envStrs);
Path program = drv.builder.c_str();
@ -1971,7 +2027,7 @@ void DerivationGoal::initChild()
string builderBasename = baseNameOf(drv.builder);
args.push_back(builderBasename.c_str());
foreach (Strings::iterator, i, drv.args)
args.push_back(i->c_str());
args.push_back(rewriteHashes(*i, rewritesToTmp).c_str());
args.push_back(0);
restoreSIGPIPE();