Print only one error message if a build fails

E.g. instead of

  error: --- BuildError ----------------------------------------------- nix
  builder for '/nix/store/03nk0a3n8h2948k4lqfgnnmym7knkcma-foo.drv' failed with exit code 1
  error: --- Error ---------------------------------------------------- nix
  build of '/nix/store/03nk0a3n8h2948k4lqfgnnmym7knkcma-foo.drv' failed

we now get

  error: --- Error ---------------------------------------------------- nix
  builder for '/nix/store/03nk0a3n8h2948k4lqfgnnmym7knkcma-foo.drv' failed with exit code 1
This commit is contained in:
Eelco Dolstra 2020-06-15 19:25:35 +02:00
parent 24a3208247
commit a588b6b19d

View file

@ -104,13 +104,10 @@ typedef std::map<StorePath, WeakGoalPtr> WeakGoalMap;
class Goal : public std::enable_shared_from_this<Goal> struct Goal : public std::enable_shared_from_this<Goal>
{ {
public:
typedef enum {ecBusy, ecSuccess, ecFailed, ecNoSubstituters, ecIncompleteClosure} ExitCode; typedef enum {ecBusy, ecSuccess, ecFailed, ecNoSubstituters, ecIncompleteClosure} ExitCode;
protected:
/* Backlink to the worker. */ /* Backlink to the worker. */
Worker & worker; Worker & worker;
@ -138,6 +135,9 @@ protected:
/* Whether the goal is finished. */ /* Whether the goal is finished. */
ExitCode exitCode; ExitCode exitCode;
/* Exception containing an error message, if any. */
std::optional<Error> ex;
Goal(Worker & worker) : worker(worker) Goal(Worker & worker) : worker(worker)
{ {
nrFailed = nrNoSubstituters = nrIncompleteClosure = 0; nrFailed = nrNoSubstituters = nrIncompleteClosure = 0;
@ -149,7 +149,6 @@ protected:
trace("goal destroyed"); trace("goal destroyed");
} }
public:
virtual void work() = 0; virtual void work() = 0;
void addWaitee(GoalPtr waitee); void addWaitee(GoalPtr waitee);
@ -173,21 +172,14 @@ public:
return name; return name;
} }
ExitCode getExitCode()
{
return exitCode;
}
/* Callback in case of a timeout. It should wake up its waiters, /* Callback in case of a timeout. It should wake up its waiters,
get rid of any running child processes that are being monitored get rid of any running child processes that are being monitored
by the worker (important!), etc. */ by the worker (important!), etc. */
virtual void timedOut() = 0; virtual void timedOut(Error && ex) = 0;
virtual string key() = 0; virtual string key() = 0;
protected: void amDone(ExitCode result, std::optional<Error> ex = {});
void amDone(ExitCode result);
}; };
@ -392,8 +384,7 @@ void Goal::waiteeDone(GoalPtr waitee, ExitCode result)
assert(waitees.find(waitee) != waitees.end()); assert(waitees.find(waitee) != waitees.end());
waitees.erase(waitee); waitees.erase(waitee);
trace(format("waitee '%1%' done; %2% left") % trace(fmt("waitee '%s' done; %d left", waitee->name, waitees.size()));
waitee->name % waitees.size());
if (result == ecFailed || result == ecNoSubstituters || result == ecIncompleteClosure) ++nrFailed; if (result == ecFailed || result == ecNoSubstituters || result == ecIncompleteClosure) ++nrFailed;
@ -418,12 +409,20 @@ void Goal::waiteeDone(GoalPtr waitee, ExitCode result)
} }
void Goal::amDone(ExitCode result) void Goal::amDone(ExitCode result, std::optional<Error> ex)
{ {
trace("done"); trace("done");
assert(exitCode == ecBusy); assert(exitCode == ecBusy);
assert(result == ecSuccess || result == ecFailed || result == ecNoSubstituters || result == ecIncompleteClosure); assert(result == ecSuccess || result == ecFailed || result == ecNoSubstituters || result == ecIncompleteClosure);
exitCode = result; exitCode = result;
if (ex) {
if (!waiters.empty())
logError(ex->info());
else
this->ex = std::move(*ex);
}
for (auto & i : waiters) { for (auto & i : waiters) {
GoalPtr goal = i.lock(); GoalPtr goal = i.lock();
if (goal) goal->waiteeDone(shared_from_this(), result); if (goal) goal->waiteeDone(shared_from_this(), result);
@ -910,7 +909,7 @@ public:
/* Whether we need to perform hash rewriting if there are valid output paths. */ /* Whether we need to perform hash rewriting if there are valid output paths. */
bool needsHashRewrite(); bool needsHashRewrite();
void timedOut() override; void timedOut(Error && ex) override;
string key() override string key() override
{ {
@ -1011,7 +1010,9 @@ private:
void started(); void started();
void done(BuildResult::Status status, const string & msg = ""); void done(
BuildResult::Status status,
std::optional<Error> ex = {});
StorePathSet exportReferences(const StorePathSet & storePaths); StorePathSet exportReferences(const StorePathSet & storePaths);
}; };
@ -1105,10 +1106,10 @@ void DerivationGoal::killChild()
} }
void DerivationGoal::timedOut() void DerivationGoal::timedOut(Error && ex)
{ {
killChild(); killChild();
done(BuildResult::TimedOut); done(BuildResult::TimedOut, ex);
} }
@ -1156,11 +1157,7 @@ void DerivationGoal::loadDerivation()
trace("loading derivation"); trace("loading derivation");
if (nrFailed != 0) { if (nrFailed != 0) {
logError({ done(BuildResult::MiscFailure, Error("cannot build missing derivation '%s'", worker.store.printStorePath(drvPath)));
.name = "missing derivation during build",
.hint = hintfmt("cannot build missing derivation '%s'", worker.store.printStorePath(drvPath))
});
done(BuildResult::MiscFailure);
return; return;
} }
@ -1349,13 +1346,9 @@ void DerivationGoal::inputsRealised()
if (nrFailed != 0) { if (nrFailed != 0) {
if (!useDerivation) if (!useDerivation)
throw Error("some dependencies of '%s' are missing", worker.store.printStorePath(drvPath)); throw Error("some dependencies of '%s' are missing", worker.store.printStorePath(drvPath));
logError({ done(BuildResult::DependencyFailed, Error(
.name = "Dependencies could not be built", "%s dependencies of derivation '%s' failed to build",
.hint = hintfmt( nrFailed, worker.store.printStorePath(drvPath)));
"cannot build derivation '%s': %s dependencies couldn't be built",
worker.store.printStorePath(drvPath), nrFailed)
});
done(BuildResult::DependencyFailed);
return; return;
} }
@ -1554,11 +1547,10 @@ void DerivationGoal::tryLocalBuild() {
startBuilder(); startBuilder();
} catch (BuildError & e) { } catch (BuildError & e) {
logError(e.info());
outputLocks.unlock(); outputLocks.unlock();
buildUser.reset(); buildUser.reset();
worker.permanentFailure = true; worker.permanentFailure = true;
done(BuildResult::InputRejected, e.msg()); done(BuildResult::InputRejected, e);
return; return;
} }
@ -1670,7 +1662,7 @@ void DerivationGoal::buildDone()
} }
auto msg = fmt("builder for '%s' %s", auto msg = fmt("builder for '%s' %s",
worker.store.printStorePath(drvPath), yellowtxt(worker.store.printStorePath(drvPath)),
statusToString(status)); statusToString(status));
if (!logger->isVerbose() && !logTail.empty()) { if (!logger->isVerbose() && !logTail.empty()) {
@ -1771,8 +1763,6 @@ void DerivationGoal::buildDone()
outputLocks.unlock(); outputLocks.unlock();
} catch (BuildError & e) { } catch (BuildError & e) {
logError(e.info());
outputLocks.unlock(); outputLocks.unlock();
BuildResult::Status st = BuildResult::MiscFailure; BuildResult::Status st = BuildResult::MiscFailure;
@ -1791,7 +1781,7 @@ void DerivationGoal::buildDone()
BuildResult::PermanentFailure; BuildResult::PermanentFailure;
} }
done(st, e.msg()); done(st, e);
return; return;
} }
@ -3881,7 +3871,6 @@ void DerivationGoal::registerOutputs()
.hint = hint .hint = hint
}); });
curRound = nrRounds; // we know enough, bail out early curRound = nrRounds; // we know enough, bail out early
} }
} }
@ -4145,14 +4134,11 @@ void DerivationGoal::handleChildOutput(int fd, const string & data)
{ {
logSize += data.size(); logSize += data.size();
if (settings.maxLogSize && logSize > settings.maxLogSize) { if (settings.maxLogSize && logSize > settings.maxLogSize) {
logError({
.name = "Max log size exceeded",
.hint = hintfmt(
"%1% killed after writing more than %2% bytes of log output",
getName(), settings.maxLogSize)
});
killChild(); killChild();
done(BuildResult::LogLimitExceeded); done(
BuildResult::LogLimitExceeded,
Error("%s killed after writing more than %d bytes of log output",
getName(), settings.maxLogSize));
return; return;
} }
@ -4233,11 +4219,12 @@ void DerivationGoal::addHashRewrite(const StorePath & path)
} }
void DerivationGoal::done(BuildResult::Status status, const string & msg) void DerivationGoal::done(BuildResult::Status status, std::optional<Error> ex)
{ {
result.status = status; result.status = status;
result.errorMsg = msg; if (ex)
amDone(result.success() ? ecSuccess : ecFailed); result.errorMsg = ex->what();
amDone(result.success() ? ecSuccess : ecFailed, ex);
if (result.status == BuildResult::TimedOut) if (result.status == BuildResult::TimedOut)
worker.timedOut = true; worker.timedOut = true;
if (result.status == BuildResult::PermanentFailure) if (result.status == BuildResult::PermanentFailure)
@ -4306,7 +4293,7 @@ public:
SubstitutionGoal(StorePath && storePath, Worker & worker, RepairFlag repair = NoRepair); SubstitutionGoal(StorePath && storePath, Worker & worker, RepairFlag repair = NoRepair);
~SubstitutionGoal(); ~SubstitutionGoal();
void timedOut() override { abort(); }; void timedOut(Error && ex) override { abort(); };
string key() override string key() override
{ {
@ -4693,7 +4680,7 @@ void Worker::removeGoal(GoalPtr goal)
topGoals.erase(goal); topGoals.erase(goal);
/* If a top-level goal failed, then kill all other goals /* If a top-level goal failed, then kill all other goals
(unless keepGoing was set). */ (unless keepGoing was set). */
if (goal->getExitCode() == Goal::ecFailed && !settings.keepGoing) if (goal->exitCode == Goal::ecFailed && !settings.keepGoing)
topGoals.clear(); topGoals.clear();
} }
@ -4935,32 +4922,24 @@ void Worker::waitForInput()
} }
} }
if (goal->getExitCode() == Goal::ecBusy && if (goal->exitCode == Goal::ecBusy &&
0 != settings.maxSilentTime && 0 != settings.maxSilentTime &&
j->respectTimeouts && j->respectTimeouts &&
after - j->lastOutput >= std::chrono::seconds(settings.maxSilentTime)) after - j->lastOutput >= std::chrono::seconds(settings.maxSilentTime))
{ {
logError({ goal->timedOut(Error(
.name = "Silent build timeout",
.hint = hintfmt(
"%1% timed out after %2% seconds of silence", "%1% timed out after %2% seconds of silence",
goal->getName(), settings.maxSilentTime) goal->getName(), settings.maxSilentTime));
});
goal->timedOut();
} }
else if (goal->getExitCode() == Goal::ecBusy && else if (goal->exitCode == Goal::ecBusy &&
0 != settings.buildTimeout && 0 != settings.buildTimeout &&
j->respectTimeouts && j->respectTimeouts &&
after - j->timeStarted >= std::chrono::seconds(settings.buildTimeout)) after - j->timeStarted >= std::chrono::seconds(settings.buildTimeout))
{ {
logError({ goal->timedOut(Error(
.name = "Build timeout",
.hint = hintfmt(
"%1% timed out after %2% seconds", "%1% timed out after %2% seconds",
goal->getName(), settings.buildTimeout) goal->getName(), settings.buildTimeout));
});
goal->timedOut();
} }
} }
@ -5066,16 +5045,28 @@ void LocalStore::buildPaths(const std::vector<StorePathWithOutputs> & drvPaths,
worker.run(goals); worker.run(goals);
StorePathSet failed; StorePathSet failed;
std::optional<Error> ex;
for (auto & i : goals) { for (auto & i : goals) {
if (i->getExitCode() != Goal::ecSuccess) { if (i->ex) {
if (ex)
logError(i->ex->info());
else
ex = i->ex;
}
if (i->exitCode != Goal::ecSuccess) {
DerivationGoal * i2 = dynamic_cast<DerivationGoal *>(i.get()); DerivationGoal * i2 = dynamic_cast<DerivationGoal *>(i.get());
if (i2) failed.insert(i2->getDrvPath()); if (i2) failed.insert(i2->getDrvPath());
else failed.insert(dynamic_cast<SubstitutionGoal *>(i.get())->getStorePath()); else failed.insert(dynamic_cast<SubstitutionGoal *>(i.get())->getStorePath());
} }
} }
if (!failed.empty()) if (failed.size() == 1 && ex) {
ex->status = worker.exitStatus();
throw *ex;
} else if (!failed.empty()) {
if (ex) logError(ex->info());
throw Error(worker.exitStatus(), "build of %s failed", showPaths(failed)); throw Error(worker.exitStatus(), "build of %s failed", showPaths(failed));
}
} }
BuildResult LocalStore::buildDerivation(const StorePath & drvPath, const BasicDerivation & drv, BuildResult LocalStore::buildDerivation(const StorePath & drvPath, const BasicDerivation & drv,
@ -5111,8 +5102,13 @@ void LocalStore::ensurePath(const StorePath & path)
worker.run(goals); worker.run(goals);
if (goal->getExitCode() != Goal::ecSuccess) if (goal->exitCode != Goal::ecSuccess) {
if (goal->ex) {
goal->ex->status = worker.exitStatus();
throw *goal->ex;
} else
throw Error(worker.exitStatus(), "path '%s' does not exist and cannot be created", printStorePath(path)); throw Error(worker.exitStatus(), "path '%s' does not exist and cannot be created", printStorePath(path));
}
} }
@ -5124,7 +5120,7 @@ void LocalStore::repairPath(const StorePath & path)
worker.run(goals); worker.run(goals);
if (goal->getExitCode() != Goal::ecSuccess) { if (goal->exitCode != Goal::ecSuccess) {
/* Since substituting the path didn't work, if we have a valid /* Since substituting the path didn't work, if we have a valid
deriver, then rebuild the deriver. */ deriver, then rebuild the deriver. */
auto info = queryPathInfo(path); auto info = queryPathInfo(path);