aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorEelco Dolstra <eelco.dolstra@logicblox.com>2012-09-11 18:39:22 -0400
committerEelco Dolstra <eelco.dolstra@logicblox.com>2012-09-11 18:39:22 -0400
commit8cf672091eb93fea5496aca2655436413f4d3c66 (patch)
tree747e1cd89cd6fe45344534cd713796453ac34e29
parent9dbda2b3fe98014687ae5cbbf16866ca6cf34f64 (diff)
Support building a derivation if some outputs are already valid (non-chroot case)
This uses scary hash rewriting. Fixes #21.
-rw-r--r--src/libstore/build.cc78
1 files changed, 67 insertions, 11 deletions
diff --git a/src/libstore/build.cc b/src/libstore/build.cc
index 4be46cfd3..93dfd21cc 100644
--- a/src/libstore/build.cc
+++ b/src/libstore/build.cc
@@ -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();