* Don't throw an exception when a build fails. Just terminate the
goal and allow the problem to be handled elsewhere (e.g., at top-level).
This commit is contained in:
parent
795d9f8b08
commit
e4883211f9
1 changed files with 215 additions and 59 deletions
|
@ -49,10 +49,16 @@ protected:
|
||||||
/* Number of goals we are waiting for. */
|
/* Number of goals we are waiting for. */
|
||||||
unsigned int nrWaitees;
|
unsigned int nrWaitees;
|
||||||
|
|
||||||
|
/* Number of goals we were waiting for that have failed. */
|
||||||
|
unsigned int nrFailed;
|
||||||
|
|
||||||
|
/* Whether amDone() has been called. */
|
||||||
|
bool done;
|
||||||
|
|
||||||
|
|
||||||
Goal(Worker & _worker) : worker(_worker)
|
Goal(Worker & _worker) : worker(_worker)
|
||||||
{
|
{
|
||||||
nrWaitees = 0;
|
done = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual ~Goal()
|
virtual ~Goal()
|
||||||
|
@ -60,6 +66,12 @@ protected:
|
||||||
printMsg(lvlVomit, "goal destroyed");
|
printMsg(lvlVomit, "goal destroyed");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void resetWaitees(int nrWaitees)
|
||||||
|
{
|
||||||
|
this->nrWaitees = nrWaitees;
|
||||||
|
nrFailed = 0;
|
||||||
|
}
|
||||||
|
|
||||||
public:
|
public:
|
||||||
virtual void work() = 0;
|
virtual void work() = 0;
|
||||||
|
|
||||||
|
@ -70,9 +82,14 @@ public:
|
||||||
|
|
||||||
void addWaiter(GoalPtr waiter);
|
void addWaiter(GoalPtr waiter);
|
||||||
|
|
||||||
void waiteeDone();
|
virtual void waiteeDone(bool success);
|
||||||
|
|
||||||
void amDone();
|
protected:
|
||||||
|
virtual void waiteeFailed()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void amDone(bool success = true);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
@ -160,6 +177,13 @@ public:
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
class BuildError : public Error
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
BuildError(const format & f) : Error(f) { };
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//////////////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
@ -170,18 +194,25 @@ void Goal::addWaiter(GoalPtr waiter)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void Goal::waiteeDone()
|
void Goal::waiteeDone(bool success)
|
||||||
{
|
{
|
||||||
assert(nrWaitees > 0);
|
assert(nrWaitees > 0);
|
||||||
|
/* Note: waiteeFailed should never call amDone()! */
|
||||||
|
if (!success) {
|
||||||
|
++nrFailed;
|
||||||
|
waiteeFailed();
|
||||||
|
}
|
||||||
if (!--nrWaitees) worker.wakeUp(shared_from_this());
|
if (!--nrWaitees) worker.wakeUp(shared_from_this());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void Goal::amDone()
|
void Goal::amDone(bool success)
|
||||||
{
|
{
|
||||||
printMsg(lvlVomit, "done");
|
printMsg(lvlVomit, "done");
|
||||||
|
assert(!done);
|
||||||
|
done = true;
|
||||||
for (Goals::iterator i = waiters.begin(); i != waiters.end(); ++i)
|
for (Goals::iterator i = waiters.begin(); i != waiters.end(); ++i)
|
||||||
(*i)->waiteeDone();
|
(*i)->waiteeDone(success);
|
||||||
worker.removeGoal(shared_from_this());
|
worker.removeGoal(shared_from_this());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -375,7 +406,7 @@ void NormalisationGoal::init()
|
||||||
/* The first thing to do is to make sure that the store expression
|
/* The first thing to do is to make sure that the store expression
|
||||||
exists. If it doesn't, it may be created through a
|
exists. If it doesn't, it may be created through a
|
||||||
substitute. */
|
substitute. */
|
||||||
nrWaitees = 1;
|
resetWaitees(1);
|
||||||
worker.addSubstitutionGoal(nePath, shared_from_this());
|
worker.addSubstitutionGoal(nePath, shared_from_this());
|
||||||
|
|
||||||
state = &NormalisationGoal::haveStoreExpr;
|
state = &NormalisationGoal::haveStoreExpr;
|
||||||
|
@ -386,6 +417,14 @@ void NormalisationGoal::haveStoreExpr()
|
||||||
{
|
{
|
||||||
trace("loading store expression");
|
trace("loading store expression");
|
||||||
|
|
||||||
|
if (nrFailed != 0) {
|
||||||
|
printMsg(lvlError,
|
||||||
|
format("cannot normalise missing store expression `%1%'")
|
||||||
|
% nePath);
|
||||||
|
amDone(false);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
assert(isValidPath(nePath));
|
assert(isValidPath(nePath));
|
||||||
|
|
||||||
/* Get the store expression. */
|
/* Get the store expression. */
|
||||||
|
@ -403,7 +442,7 @@ void NormalisationGoal::haveStoreExpr()
|
||||||
i != expr.derivation.inputs.end(); ++i)
|
i != expr.derivation.inputs.end(); ++i)
|
||||||
worker.addNormalisationGoal(*i, shared_from_this());
|
worker.addNormalisationGoal(*i, shared_from_this());
|
||||||
|
|
||||||
nrWaitees = expr.derivation.inputs.size();
|
resetWaitees(expr.derivation.inputs.size());
|
||||||
|
|
||||||
state = &NormalisationGoal::inputNormalised;
|
state = &NormalisationGoal::inputNormalised;
|
||||||
}
|
}
|
||||||
|
@ -413,6 +452,15 @@ void NormalisationGoal::inputNormalised()
|
||||||
{
|
{
|
||||||
trace("all inputs normalised");
|
trace("all inputs normalised");
|
||||||
|
|
||||||
|
if (nrFailed != 0) {
|
||||||
|
printMsg(lvlError,
|
||||||
|
format("cannot normalise derivation `%1%': "
|
||||||
|
"%2% closure element(s) could not be normalised")
|
||||||
|
% nePath % nrFailed);
|
||||||
|
amDone(false);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
/* Inputs must also be realised before we can build this goal. */
|
/* Inputs must also be realised before we can build this goal. */
|
||||||
for (PathSet::iterator i = expr.derivation.inputs.begin();
|
for (PathSet::iterator i = expr.derivation.inputs.begin();
|
||||||
i != expr.derivation.inputs.end(); ++i)
|
i != expr.derivation.inputs.end(); ++i)
|
||||||
|
@ -424,7 +472,7 @@ void NormalisationGoal::inputNormalised()
|
||||||
worker.addRealisationGoal(neInput, shared_from_this());
|
worker.addRealisationGoal(neInput, shared_from_this());
|
||||||
}
|
}
|
||||||
|
|
||||||
nrWaitees = expr.derivation.inputs.size();
|
resetWaitees(expr.derivation.inputs.size());
|
||||||
|
|
||||||
state = &NormalisationGoal::inputRealised;
|
state = &NormalisationGoal::inputRealised;
|
||||||
}
|
}
|
||||||
|
@ -434,6 +482,15 @@ void NormalisationGoal::inputRealised()
|
||||||
{
|
{
|
||||||
trace("all inputs realised");
|
trace("all inputs realised");
|
||||||
|
|
||||||
|
if (nrFailed != 0) {
|
||||||
|
printMsg(lvlError,
|
||||||
|
format("cannot normalise derivation `%1%': "
|
||||||
|
"%2% closure element(s) could not be realised")
|
||||||
|
% nePath % nrFailed);
|
||||||
|
amDone(false);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
/* Okay, try to build. Note that here we don't wait for a build
|
/* Okay, try to build. Note that here we don't wait for a build
|
||||||
slot to become available, since we don't need one if there is a
|
slot to become available, since we don't need one if there is a
|
||||||
build hook. */
|
build hook. */
|
||||||
|
@ -446,6 +503,8 @@ void NormalisationGoal::tryToBuild()
|
||||||
{
|
{
|
||||||
trace("trying to build");
|
trace("trying to build");
|
||||||
|
|
||||||
|
try {
|
||||||
|
|
||||||
/* Is the build hook willing to accept this job? */
|
/* Is the build hook willing to accept this job? */
|
||||||
switch (tryBuildHook()) {
|
switch (tryBuildHook()) {
|
||||||
case rpAccept:
|
case rpAccept:
|
||||||
|
@ -482,6 +541,12 @@ void NormalisationGoal::tryToBuild()
|
||||||
/* Okay, we have to build. */
|
/* Okay, we have to build. */
|
||||||
startBuilder();
|
startBuilder();
|
||||||
|
|
||||||
|
} catch (BuildError & e) {
|
||||||
|
printMsg(lvlError, e.msg());
|
||||||
|
amDone(false);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
/* This state will be reached when we get EOF on the child's
|
/* This state will be reached when we get EOF on the child's
|
||||||
log pipe. */
|
log pipe. */
|
||||||
state = &NormalisationGoal::buildDone;
|
state = &NormalisationGoal::buildDone;
|
||||||
|
@ -514,15 +579,23 @@ void NormalisationGoal::buildDone()
|
||||||
/* Check the exit status. */
|
/* Check the exit status. */
|
||||||
if (!statusOk(status)) {
|
if (!statusOk(status)) {
|
||||||
deleteTmpDir(false);
|
deleteTmpDir(false);
|
||||||
throw Error(format("builder for `%1%' %2%")
|
printMsg(lvlError, format("builder for `%1%' %2%")
|
||||||
% nePath % statusToString(status));
|
% nePath % statusToString(status));
|
||||||
|
amDone(false);
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
deleteTmpDir(true);
|
deleteTmpDir(true);
|
||||||
|
|
||||||
/* Compute a closure store expression, and register it as our
|
/* Compute a closure store expression, and register it as our
|
||||||
successor. */
|
successor. */
|
||||||
|
try {
|
||||||
createClosure();
|
createClosure();
|
||||||
|
} catch (BuildError & e) {
|
||||||
|
printMsg(lvlError, e.msg());
|
||||||
|
amDone(false);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
amDone();
|
amDone();
|
||||||
}
|
}
|
||||||
|
@ -791,8 +864,9 @@ void NormalisationGoal::startBuilder()
|
||||||
|
|
||||||
/* Right platform? */
|
/* Right platform? */
|
||||||
if (expr.derivation.platform != thisSystem)
|
if (expr.derivation.platform != thisSystem)
|
||||||
throw Error(format("a `%1%' is required, but I am a `%2%'")
|
throw BuildError(
|
||||||
% expr.derivation.platform % thisSystem);
|
format("a `%1%' is required to build `%3%', but I am a `%2%'")
|
||||||
|
% expr.derivation.platform % thisSystem % nePath);
|
||||||
|
|
||||||
/* If any of the outputs already exist but are not registered,
|
/* If any of the outputs already exist but are not registered,
|
||||||
delete them. */
|
delete them. */
|
||||||
|
@ -923,8 +997,11 @@ void NormalisationGoal::createClosure()
|
||||||
i != expr.derivation.outputs.end(); ++i)
|
i != expr.derivation.outputs.end(); ++i)
|
||||||
{
|
{
|
||||||
Path path = *i;
|
Path path = *i;
|
||||||
if (!pathExists(path))
|
if (!pathExists(path)) {
|
||||||
throw Error(format("output path `%1%' does not exist") % path);
|
throw BuildError(
|
||||||
|
format("builder for `%1%' failed to produce output path `%1%'")
|
||||||
|
% nePath % path);
|
||||||
|
}
|
||||||
nf.closure.roots.insert(path);
|
nf.closure.roots.insert(path);
|
||||||
|
|
||||||
makePathReadOnly(path);
|
makePathReadOnly(path);
|
||||||
|
@ -1038,18 +1115,18 @@ void NormalisationGoal::initChild()
|
||||||
commonChildInit(logPipe);
|
commonChildInit(logPipe);
|
||||||
|
|
||||||
if (chdir(tmpDir.c_str()) == -1)
|
if (chdir(tmpDir.c_str()) == -1)
|
||||||
throw SysError(format("changing into to `%1%'") % tmpDir);
|
throw SysError(format("changing into `%1%'") % tmpDir);
|
||||||
|
|
||||||
/* When running a hook, dup the communication pipes. */
|
/* When running a hook, dup the communication pipes. */
|
||||||
bool inHook = fromHook.writeSide.isOpen();
|
bool inHook = fromHook.writeSide.isOpen();
|
||||||
if (inHook) {
|
if (inHook) {
|
||||||
fromHook.readSide.close();
|
fromHook.readSide.close();
|
||||||
if (dup2(fromHook.writeSide, 3) == -1)
|
if (dup2(fromHook.writeSide, 3) == -1)
|
||||||
throw SysError("dup1");
|
throw SysError("dupping from-hook write side");
|
||||||
|
|
||||||
toHook.writeSide.close();
|
toHook.writeSide.close();
|
||||||
if (dup2(toHook.readSide, 4) == -1)
|
if (dup2(toHook.readSide, 4) == -1)
|
||||||
throw SysError("dup2");
|
throw SysError("dupping to-hook read side");
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Close all other file descriptors. */
|
/* Close all other file descriptors. */
|
||||||
|
@ -1139,7 +1216,7 @@ void RealisationGoal::init()
|
||||||
/* The first thing to do is to make sure that the store expression
|
/* The first thing to do is to make sure that the store expression
|
||||||
exists. If it doesn't, it may be created through a
|
exists. If it doesn't, it may be created through a
|
||||||
substitute. */
|
substitute. */
|
||||||
nrWaitees = 1;
|
resetWaitees(1);
|
||||||
worker.addSubstitutionGoal(nePath, shared_from_this());
|
worker.addSubstitutionGoal(nePath, shared_from_this());
|
||||||
|
|
||||||
state = &RealisationGoal::haveStoreExpr;
|
state = &RealisationGoal::haveStoreExpr;
|
||||||
|
@ -1150,6 +1227,14 @@ void RealisationGoal::haveStoreExpr()
|
||||||
{
|
{
|
||||||
trace("loading store expression");
|
trace("loading store expression");
|
||||||
|
|
||||||
|
if (nrFailed != 0) {
|
||||||
|
printMsg(lvlError,
|
||||||
|
format("cannot realise missing store expression `%1%'")
|
||||||
|
% nePath);
|
||||||
|
amDone(false);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
assert(isValidPath(nePath));
|
assert(isValidPath(nePath));
|
||||||
|
|
||||||
/* Get the store expression. */
|
/* Get the store expression. */
|
||||||
|
@ -1165,7 +1250,7 @@ void RealisationGoal::haveStoreExpr()
|
||||||
i != expr.closure.elems.end(); ++i)
|
i != expr.closure.elems.end(); ++i)
|
||||||
worker.addSubstitutionGoal(i->first, shared_from_this());
|
worker.addSubstitutionGoal(i->first, shared_from_this());
|
||||||
|
|
||||||
nrWaitees = expr.closure.elems.size();
|
resetWaitees(expr.closure.elems.size());
|
||||||
|
|
||||||
state = &RealisationGoal::elemFinished;
|
state = &RealisationGoal::elemFinished;
|
||||||
}
|
}
|
||||||
|
@ -1175,6 +1260,16 @@ void RealisationGoal::elemFinished()
|
||||||
{
|
{
|
||||||
trace("all closure elements present");
|
trace("all closure elements present");
|
||||||
|
|
||||||
|
if (nrFailed != 0) {
|
||||||
|
printMsg(lvlError,
|
||||||
|
format("cannot realise closure `%1%': "
|
||||||
|
"%2% closure element(s) are not present "
|
||||||
|
"and could not be substituted")
|
||||||
|
% nePath % nrFailed);
|
||||||
|
amDone(false);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
amDone();
|
amDone();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1269,15 +1364,21 @@ void SubstitutionGoal::tryNext()
|
||||||
{
|
{
|
||||||
trace("trying next substitute");
|
trace("trying next substitute");
|
||||||
|
|
||||||
if (subs.size() == 0) throw Error(
|
if (subs.size() == 0) {
|
||||||
|
/* None left. Terminate this goal and let someone else deal
|
||||||
|
with it. */
|
||||||
|
printMsg(lvlError,
|
||||||
format("path `%1%' is required, but it has no (remaining) substitutes")
|
format("path `%1%' is required, but it has no (remaining) substitutes")
|
||||||
% storePath);
|
% storePath);
|
||||||
|
amDone(false);
|
||||||
|
return;
|
||||||
|
}
|
||||||
sub = subs.front();
|
sub = subs.front();
|
||||||
subs.pop_front();
|
subs.pop_front();
|
||||||
|
|
||||||
/* Normalise the substitute store expression. */
|
/* Normalise the substitute store expression. */
|
||||||
worker.addNormalisationGoal(sub.storeExpr, shared_from_this());
|
worker.addNormalisationGoal(sub.storeExpr, shared_from_this());
|
||||||
nrWaitees = 1;
|
resetWaitees(1);
|
||||||
|
|
||||||
state = &SubstitutionGoal::exprNormalised;
|
state = &SubstitutionGoal::exprNormalised;
|
||||||
}
|
}
|
||||||
|
@ -1287,11 +1388,17 @@ void SubstitutionGoal::exprNormalised()
|
||||||
{
|
{
|
||||||
trace("substitute store expression normalised");
|
trace("substitute store expression normalised");
|
||||||
|
|
||||||
|
if (nrFailed != 0) {
|
||||||
|
tryNext();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
/* Realise the substitute store expression. */
|
/* Realise the substitute store expression. */
|
||||||
if (!querySuccessor(sub.storeExpr, nfSub))
|
if (!querySuccessor(sub.storeExpr, nfSub))
|
||||||
nfSub = sub.storeExpr;
|
nfSub = sub.storeExpr;
|
||||||
worker.addRealisationGoal(nfSub, shared_from_this());
|
worker.addRealisationGoal(nfSub, shared_from_this());
|
||||||
nrWaitees = 1;
|
|
||||||
|
resetWaitees(1);
|
||||||
|
|
||||||
state = &SubstitutionGoal::exprRealised;
|
state = &SubstitutionGoal::exprRealised;
|
||||||
}
|
}
|
||||||
|
@ -1301,6 +1408,11 @@ void SubstitutionGoal::exprRealised()
|
||||||
{
|
{
|
||||||
trace("substitute store expression realised");
|
trace("substitute store expression realised");
|
||||||
|
|
||||||
|
if (nrFailed != 0) {
|
||||||
|
tryNext();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
state = &SubstitutionGoal::tryToRun;
|
state = &SubstitutionGoal::tryToRun;
|
||||||
worker.waitForBuildSlot(shared_from_this());
|
worker.waitForBuildSlot(shared_from_this());
|
||||||
}
|
}
|
||||||
|
@ -1452,6 +1564,40 @@ void SubstitutionGoal::trace(const format & f)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
//////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
|
||||||
|
/* A fake goal used to receive notification of success or failure of
|
||||||
|
other goals. */
|
||||||
|
class PseudoGoal : public Goal
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
bool success;
|
||||||
|
|
||||||
|
public:
|
||||||
|
PseudoGoal(Worker & _worker) : Goal(_worker)
|
||||||
|
{
|
||||||
|
success = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void work()
|
||||||
|
{
|
||||||
|
abort();
|
||||||
|
}
|
||||||
|
|
||||||
|
void waiteeDone(bool success)
|
||||||
|
{
|
||||||
|
if (!success) this->success = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool isOkay()
|
||||||
|
{
|
||||||
|
return success;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//////////////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
|
||||||
|
@ -1687,13 +1833,15 @@ Path normaliseStoreExpr(const Path & nePath)
|
||||||
startNest(nest, lvlDebug, format("normalising `%1%'") % nePath);
|
startNest(nest, lvlDebug, format("normalising `%1%'") % nePath);
|
||||||
|
|
||||||
Worker worker;
|
Worker worker;
|
||||||
worker.addNormalisationGoal(nePath, GoalPtr());
|
shared_ptr<PseudoGoal> pseudo(new PseudoGoal(worker));
|
||||||
|
worker.addNormalisationGoal(nePath, pseudo);
|
||||||
worker.run();
|
worker.run();
|
||||||
|
|
||||||
Path nfPath;
|
if (!pseudo->isOkay())
|
||||||
if (!querySuccessor(nePath, nfPath))
|
throw Error(format("normalisation of store expression `%1%' failed") % nePath);
|
||||||
throw Error("there should be a successor");
|
|
||||||
|
|
||||||
|
Path nfPath;
|
||||||
|
if (!querySuccessor(nePath, nfPath)) abort();
|
||||||
return nfPath;
|
return nfPath;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1703,8 +1851,12 @@ void realiseClosure(const Path & nePath)
|
||||||
startNest(nest, lvlDebug, format("realising closure `%1%'") % nePath);
|
startNest(nest, lvlDebug, format("realising closure `%1%'") % nePath);
|
||||||
|
|
||||||
Worker worker;
|
Worker worker;
|
||||||
worker.addRealisationGoal(nePath, GoalPtr());
|
shared_ptr<PseudoGoal> pseudo(new PseudoGoal(worker));
|
||||||
|
worker.addRealisationGoal(nePath, pseudo);
|
||||||
worker.run();
|
worker.run();
|
||||||
|
|
||||||
|
if (!pseudo->isOkay())
|
||||||
|
throw Error(format("realisation of closure `%1%' failed") % nePath);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -1714,6 +1866,10 @@ void ensurePath(const Path & path)
|
||||||
if (isValidPath(path)) return;
|
if (isValidPath(path)) return;
|
||||||
|
|
||||||
Worker worker;
|
Worker worker;
|
||||||
worker.addSubstitutionGoal(path, GoalPtr());
|
shared_ptr<PseudoGoal> pseudo(new PseudoGoal(worker));
|
||||||
|
worker.addSubstitutionGoal(path, pseudo);
|
||||||
worker.run();
|
worker.run();
|
||||||
|
|
||||||
|
if (!pseudo->isOkay())
|
||||||
|
throw Error(format("path `%1%' does not exist and cannot be created") % path);
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue