summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--doc/guix.texi4
-rw-r--r--nix/libstore/build.cc45
2 files changed, 42 insertions, 7 deletions
diff --git a/doc/guix.texi b/doc/guix.texi
index cd4e550ef3..4222e011e5 100644
--- a/doc/guix.texi
+++ b/doc/guix.texi
@@ -998,6 +998,10 @@ consecutive build results are not bit-for-bit identical.  Note that this
 setting can be overridden by clients such as @command{guix build}
 (@pxref{Invoking guix build}).
 
+When used in conjunction with @option{--keep-failed}, the differing
+output is kept in the store, under @file{/gnu/store/@dots{}-check}.
+This makes it easy to look for differences between the two results.
+
 @item --debug
 Produce debugging output.
 
diff --git a/nix/libstore/build.cc b/nix/libstore/build.cc
index d51705b48f..2d3960bf89 100644
--- a/nix/libstore/build.cc
+++ b/nix/libstore/build.cc
@@ -2320,6 +2320,8 @@ void DerivationGoal::registerOutputs()
        outputs to allow hard links between outputs. */
     InodesSeen inodesSeen;
 
+    Path checkSuffix = "-check";
+
     /* 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. */
@@ -2433,7 +2435,7 @@ void DerivationGoal::registerOutputs()
             ValidPathInfo info = worker.store.queryPathInfo(path);
             if (hash.first != info.hash) {
                 if (settings.keepFailed) {
-                    Path dst = path + "-check";
+                    Path dst = path + checkSuffix;
                     if (pathExists(dst)) deletePath(dst);
                     if (rename(actualPath.c_str(), dst.c_str()))
                         throw SysError(format("renaming `%1%' to `%2%'") % actualPath % dst);
@@ -2487,9 +2489,11 @@ void DerivationGoal::registerOutputs()
         checkRefs("disallowedReferences", false, false);
         checkRefs("disallowedRequisites", false, true);
 
-        worker.store.optimisePath(path); // FIXME: combine with scanForReferences()
+        if (curRound == nrRounds) {
+            worker.store.optimisePath(path); // FIXME: combine with scanForReferences()
 
-        worker.store.markContentsGood(path);
+            worker.store.markContentsGood(path);
+        }
 
         ValidPathInfo info;
         info.path = path;
@@ -2502,10 +2506,37 @@ void DerivationGoal::registerOutputs()
 
     if (buildMode == bmCheck) return;
 
-    if (curRound > 1 && prevInfos != infos)
-        throw NotDeterministic(
-            format("result of ‘%1%’ differs from previous round; rejecting as non-deterministic")
-            % drvPath);
+    /* Compare the result with the previous round, and report which
+       path is different, if any.*/
+    if (curRound > 1 && prevInfos != infos) {
+        assert(prevInfos.size() == infos.size());
+        for (auto i = prevInfos.begin(), j = infos.begin(); i != prevInfos.end(); ++i, ++j)
+            if (!(*i == *j)) {
+                Path prev = i->path + checkSuffix;
+                if (pathExists(prev))
+                    throw NotDeterministic(
+                        format("output ‘%1%’ of ‘%2%’ differs from ‘%3%’ from previous round")
+                        % i->path % drvPath % prev);
+                else
+                    throw NotDeterministic(
+                        format("output ‘%1%’ of ‘%2%’ differs from previous round")
+                        % i->path % drvPath);
+            }
+        assert(false); // shouldn't happen
+    }
+
+    if (settings.keepFailed) {
+        for (auto & i : drv.outputs) {
+            Path prev = i.second.path + checkSuffix;
+            if (pathExists(prev)) deletePath(prev);
+            if (curRound < nrRounds) {
+                Path dst = i.second.path + checkSuffix;
+                if (rename(i.second.path.c_str(), dst.c_str()))
+                    throw SysError(format("renaming ‘%1%’ to ‘%2%’") % i.second.path % dst);
+            }
+        }
+
+    }
 
     if (curRound < nrRounds) {
         prevInfos = infos;