/* A pointer to a goal. */
class Goal;
+class DerivationGoal;
typedef std::shared_ptr<Goal> GoalPtr;
typedef std::weak_ptr<Goal> WeakGoalPtr;
return exitCode;
}
- /* Cancel the goal. It should wake up its waiters, get rid of any
- running child processes that are being monitored by the worker
- (important!), etc. */
- virtual void cancel(bool timeout) = 0;
+ /* Callback in case of a timeout. It should wake up its waiters,
+ get rid of any running child processes that are being monitored
+ by the worker (important!), etc. */
+ virtual void timedOut() = 0;
virtual string key() = 0;
result. */
ValidPathInfos prevInfos;
+ BuildResult result;
+
public:
DerivationGoal(const Path & drvPath, const StringSet & wantedOutputs, Worker & worker, BuildMode buildMode = bmNormal);
~DerivationGoal();
- void cancel(bool timeout);
+ void timedOut() override;
string key()
{
/* Add wanted outputs to an already existing derivation goal. */
void addWantedOutputs(const StringSet & outputs);
+ BuildResult getResult() { return result; }
+
private:
/* The states. */
void init();
Path addHashRewrite(const Path & path);
void repairClosure();
+
+ void done(BuildResult::Status status, const string & msg = "");
};
}
-void DerivationGoal::cancel(bool timeout)
+void DerivationGoal::timedOut()
{
- if (settings.printBuildTrace && timeout)
+ if (settings.printBuildTrace)
printMsg(lvlError, format("@ build-failed %1% - timeout") % drvPath);
killChild();
- amDone(ecFailed);
+ done(BuildResult::TimedOut);
}
trace("loading derivation");
if (nrFailed != 0) {
- printMsg(lvlError, format("cannot build missing derivation `%1%'") % drvPath);
- amDone(ecFailed);
+ printMsg(lvlError, format("cannot build missing derivation ‘%1%’") % drvPath);
+ done(BuildResult::MiscFailure);
return;
}
/* If they are all valid, then we're done. */
if (invalidOutputs.size() == 0 && buildMode == bmNormal) {
- amDone(ecSuccess);
+ done(BuildResult::AlreadyValid);
return;
}
unsigned int nrInvalid = checkPathValidity(false, buildMode == bmRepair).size();
if (buildMode == bmNormal && nrInvalid == 0) {
- amDone(ecSuccess);
+ done(BuildResult::Substituted);
return;
}
if (buildMode == bmRepair && nrInvalid == 0) {
}
if (waitees.empty()) {
- amDone(ecSuccess);
+ done(BuildResult::AlreadyValid);
return;
}
{
trace("closure repaired");
if (nrFailed > 0)
- throw Error(format("some paths in the output closure of derivation `%1%' could not be repaired") % drvPath);
- amDone(ecSuccess);
+ throw Error(format("some paths in the output closure of derivation ‘%1%’ could not be repaired") % drvPath);
+ done(BuildResult::AlreadyValid);
}
printMsg(lvlError,
format("cannot build derivation `%1%': %2% dependencies couldn't be built")
% drvPath % nrFailed);
- amDone(ecFailed);
+ done(BuildResult::DependencyFailed);
return;
}
if (buildMode != bmCheck && validPaths.size() == drv.outputs.size()) {
debug(format("skipping build of derivation `%1%', someone beat us to it") % drvPath);
outputLocks.setDeletion(true);
- amDone(ecSuccess);
+ done(BuildResult::AlreadyValid);
return;
}
printMsg(lvlError, format("@ build-failed %1% - %2% %3%")
% drvPath % 0 % e.msg());
worker.permanentFailure = true;
- amDone(ecFailed);
+ done(BuildResult::InputRejected, e.msg());
return;
}
registerOutputs();
if (buildMode == bmCheck) {
- amDone(ecSuccess);
+ done(BuildResult::Built);
return;
}
outputLocks.unlock();
buildUser.release();
+ BuildResult::Status st = BuildResult::MiscFailure;
+
if (hook && WIFEXITED(status) && WEXITSTATUS(status) == 101) {
if (settings.printBuildTrace)
printMsg(lvlError, format("@ build-failed %1% - timeout") % drvPath);
- worker.timedOut = true;
+ st = BuildResult::TimedOut;
}
else if (hook && (!WIFEXITED(status) || WEXITSTATUS(status) != 100)) {
if (settings.printBuildTrace)
printMsg(lvlError, format("@ build-failed %1% - %2% %3%")
% drvPath % 1 % e.msg());
- worker.permanentFailure = !fixedOutput && !diskFull;
+
+ st =
+ statusOk(status) ? BuildResult::OutputRejected :
+ fixedOutput || diskFull ? BuildResult::TransientFailure :
+ BuildResult::PermanentFailure;
/* Register the outputs of this build as "failed" so we
won't try to build them again (negative caching).
worker.store.registerFailedPath(i->second.path);
}
- amDone(ecFailed);
+ done(st, e.msg());
return;
}
if (settings.printBuildTrace)
printMsg(lvlError, format("@ build-succeeded %1% -") % drvPath);
- amDone(ecSuccess);
+ done(BuildResult::Built);
}
printMsg(lvlError,
format("%1% killed after writing more than %2% bytes of log output")
% getName() % settings.maxLogSize);
- cancel(true); // not really a timeout, but close enough
+ timedOut(); // not really a timeout, but close enough
return;
}
if (verbosity >= settings.buildVerbosity)
if (settings.printBuildTrace)
printMsg(lvlError, format("@ build-failed %1% - cached") % drvPath);
- worker.permanentFailure = true;
- amDone(ecFailed);
+ done(BuildResult::CachedFailure);
return true;
}
}
+void DerivationGoal::done(BuildResult::Status status, const string & msg)
+{
+ result.status = status;
+ result.errorMsg = msg;
+ amDone(result.success() ? ecSuccess : ecFailed);
+ if (result.status == BuildResult::TimedOut)
+ worker.timedOut = true;
+ if (result.status == BuildResult::PermanentFailure || result.status == BuildResult::CachedFailure)
+ worker.permanentFailure = true;
+}
+
+
//////////////////////////////////////////////////////////////////////
SubstitutionGoal(const Path & storePath, Worker & worker, bool repair = false);
~SubstitutionGoal();
- void cancel(bool timeout);
+ void timedOut();
string key()
{
}
-void SubstitutionGoal::cancel(bool timeout)
+void SubstitutionGoal::timedOut()
{
- if (settings.printBuildTrace && timeout)
+ if (settings.printBuildTrace)
printMsg(lvlError, format("@ substituter-failed %1% timeout") % storePath);
if (pid != -1) {
pid_t savedPid = pid;
}
-GoalPtr Worker::makeDerivationGoal(const Path & path, const StringSet & wantedOutputs, BuildMode buildMode)
+GoalPtr Worker::makeDerivationGoal(const Path & path,
+ const StringSet & wantedOutputs, BuildMode buildMode)
{
GoalPtr goal = derivationGoals[path].lock();
if (!goal) {
/* Since goals may be canceled from inside the loop below (causing
them go be erased from the `children' map), we have to be
careful that we don't keep iterators alive across calls to
- cancel(). */
+ timedOut(). */
set<pid_t> pids;
foreach (Children::iterator, i, children) pids.insert(i->first);
printMsg(lvlError,
format("%1% timed out after %2% seconds of silence")
% goal->getName() % settings.maxSilentTime);
- goal->cancel(true);
- timedOut = true;
+ goal->timedOut();
}
else if (goal->getExitCode() == Goal::ecBusy &&
printMsg(lvlError,
format("%1% timed out after %2% seconds")
% goal->getName() % settings.buildTimeout);
- goal->cancel(true);
- timedOut = true;
+ goal->timedOut();
}
}