From 9a313469a4bdea2d1e8df24d16289dc2a172a169 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Wed, 7 Dec 2016 17:57:35 +0100 Subject: [PATCH] Add a hook to run diffoscope when non-determinism is detected --- src/libstore/build.cc | 39 +++++++++++++++++++++++++++++++------- src/nix-store/nix-store.cc | 1 + 2 files changed, 33 insertions(+), 7 deletions(-) diff --git a/src/libstore/build.cc b/src/libstore/build.cc index 8442f1f61..bbf3c8bc9 100644 --- a/src/libstore/build.cc +++ b/src/libstore/build.cc @@ -2688,6 +2688,8 @@ void DerivationGoal::registerOutputs() InodesSeen inodesSeen; Path checkSuffix = "-check"; + bool runDiffHook = settings.get("run-diff-hook", false); + bool keepPreviousRound = settings.keepFailed || runDiffHook; /* Check whether the output paths were created, and grep each output path to determine what other paths it references. Also make all @@ -2919,25 +2921,39 @@ void DerivationGoal::registerOutputs() if (!(*i == *j)) { result.isNonDeterministic = true; Path prev = i->path + checkSuffix; - auto msg = pathExists(prev) + bool prevExists = keepPreviousRound && pathExists(prev); + auto msg = prevExists ? 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); + + auto diffHook = settings.get("diff-hook", std::string("")); + if (prevExists && diffHook != "" && runDiffHook) { + try { + auto diff = runProgram(diffHook, true, {prev, i->path}); + if (diff != "") + printError(chomp(diff)); + } catch (Error & error) { + printError("diff hook execution failed: %s", error.what()); + } + } + if (settings.get("enforce-determinism", true)) throw NotDeterministic(msg); + printError(msg); curRound = nrRounds; // we know enough, bail out early } } - if (settings.keepFailed) { + /* If this is the first round of several, then move the output out + of the way. */ + if (nrRounds > 1 && curRound == 1 && curRound < nrRounds && keepPreviousRound) { for (auto & i : drv->outputs) { Path prev = i.second.path + checkSuffix; 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); - } + 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); } } @@ -2946,6 +2962,15 @@ void DerivationGoal::registerOutputs() return; } + /* Remove the -check directories if we're done. FIXME: keep them + if the result was not determistic? */ + if (curRound == nrRounds) { + for (auto & i : drv->outputs) { + Path prev = i.second.path + checkSuffix; + deletePath(prev); + } + } + /* Register each output path as valid, and register the sets of paths referenced by each of them. If there are cycles in the outputs, this will fail. */ diff --git a/src/nix-store/nix-store.cc b/src/nix-store/nix-store.cc index f81cff4ac..c1e6afef0 100644 --- a/src/nix-store/nix-store.cc +++ b/src/nix-store/nix-store.cc @@ -843,6 +843,7 @@ static void opServe(Strings opFlags, Strings opArgs) if (GET_PROTOCOL_MINOR(clientVersion) >= 3) { settings.set("build-repeat", std::to_string(readInt(in))); settings.set("enforce-determinism", readInt(in) != 0 ? "true" : "false"); + settings.set("run-diff-hook", "true"); } settings.printRepeatedBuilds = false; };