about summary refs log tree commit diff
path: root/src
diff options
context:
space:
mode:
authorEelco Dolstra <edolstra@gmail.com>2016-12-07T12·16+0100
committerEelco Dolstra <edolstra@gmail.com>2016-12-07T12·16+0100
commit8bdf83f936adae6f2c907a6d2541e80d4120f051 (patch)
tree17b49383bec8578c307ed3e47fe27db24c3f4171 /src
parentceeedb58d23950f0ae3944484bca331e5cbb8053 (diff)
Add an option to make non-determinism non-fatal
That is, when build-repeat > 0, and the output of two rounds differ,
then print a warning rather than fail the build. This is primarily to
let Hydra check reproducibility of all packages.
Diffstat (limited to 'src')
-rw-r--r--src/libstore/build.cc24
-rw-r--r--src/libstore/store-api.hh11
2 files changed, 24 insertions, 11 deletions
diff --git a/src/libstore/build.cc b/src/libstore/build.cc
index 300f6ac00fcb..84fbf1c18dab 100644
--- a/src/libstore/build.cc
+++ b/src/libstore/build.cc
@@ -1272,6 +1272,8 @@ void DerivationGoal::inputsRealised()
        build hook. */
     state = &DerivationGoal::tryToBuild;
     worker.wakeUp(shared_from_this());
+
+    result = BuildResult();
 }
 
 
@@ -1421,6 +1423,8 @@ void DerivationGoal::buildDone()
 
     debug(format("builder process for ‘%1%’ finished") % drvPath);
 
+    result.timesBuilt++;
+
     /* So the child is gone now. */
     worker.childTerminated(this);
 
@@ -2909,17 +2913,15 @@ void DerivationGoal::registerOutputs()
         assert(prevInfos.size() == infos.size());
         for (auto i = prevInfos.begin(), j = infos.begin(); i != prevInfos.end(); ++i, ++j)
             if (!(*i == *j)) {
+                result.isNonDeterministic = true;
                 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);
+                auto msg = pathExists(prev)
+                    ? fmt("output ‘%1%’ of ‘%2%’ differs from ‘%3%’ from previous round", i->path, drvPath, prev)
+                    : fmt("output ‘%1%’ of ‘%2%’ differs from previous round", i->path, drvPath);
+                if (settings.get("enforce-determinism", true))
+                    throw NotDeterministic(msg);
+                printError(msg);
             }
-        abort(); // shouldn't happen
     }
 
     if (settings.keepFailed) {
@@ -2932,7 +2934,6 @@ void DerivationGoal::registerOutputs()
                     throw SysError(format("renaming ‘%1%’ to ‘%2%’") % i.second.path % dst);
             }
         }
-
     }
 
     if (curRound < nrRounds) {
@@ -3792,12 +3793,13 @@ void LocalStore::buildPaths(const PathSet & drvPaths, BuildMode buildMode)
     worker.run(goals);
 
     PathSet failed;
-    for (auto & i : goals)
+    for (auto & i : goals) {
         if (i->getExitCode() != Goal::ecSuccess) {
             DerivationGoal * i2 = dynamic_cast<DerivationGoal *>(i.get());
             if (i2) failed.insert(i2->getDrvPath());
             else failed.insert(dynamic_cast<SubstitutionGoal *>(i.get())->getStorePath());
         }
+    }
 
     if (!failed.empty())
         throw Error(worker.exitStatus(), "build of %s failed", showPaths(failed));
diff --git a/src/libstore/store-api.hh b/src/libstore/store-api.hh
index 2ea74d90e78e..ee2b6b2862b6 100644
--- a/src/libstore/store-api.hh
+++ b/src/libstore/store-api.hh
@@ -208,7 +208,18 @@ struct BuildResult
         NotDeterministic,
     } status = MiscFailure;
     std::string errorMsg;
+
+    /* How many times this build was performed. */
+    unsigned int timesBuilt = 0;
+
+    /* If timesBuilt > 1, whether some builds did not produce the same
+       result. (Note that 'isNonDeterministic = false' does not mean
+       the build is deterministic, just that we don't have evidence of
+       non-determinism.) */
+    bool isNonDeterministic = false;
+
     //time_t startTime = 0, stopTime = 0;
+
     bool success() {
         return status == Built || status == Substituted || status == AlreadyValid;
     }