* When multiple derivations are specified in `nix-store -r', don't

continue building when one fails unless `--keep-going' is
  specified.
* When `--keep-going' is specified, print out the set of failing
  derivations at the end (otherwise it can be hard to find out which
  failed).
This commit is contained in:
Eelco Dolstra 2005-02-23 11:19:27 +00:00
parent 3a2c3f0cf2
commit bfaf83a0fd

View file

@ -42,6 +42,9 @@ typedef map<Path, WeakGoalPtr> WeakGoalMap;
class Goal : public enable_shared_from_this<Goal> class Goal : public enable_shared_from_this<Goal>
{ {
public:
typedef enum {ecBusy, ecSuccess, ecFailed} ExitCode;
protected: protected:
/* Backlink to the worker. */ /* Backlink to the worker. */
@ -57,16 +60,16 @@ protected:
/* Number of goals we are/were waiting for that have failed. */ /* Number of goals we are/were waiting for that have failed. */
unsigned int nrFailed; unsigned int nrFailed;
/* Whether amDone() has been called. */
bool done;
/* Name of this goal for debugging purposes. */ /* Name of this goal for debugging purposes. */
string name; string name;
/* Whether the goal is finished. */
ExitCode exitCode;
Goal(Worker & worker) : worker(worker) Goal(Worker & worker) : worker(worker)
{ {
done = false;
nrFailed = 0; nrFailed = 0;
exitCode = ecBusy;
} }
virtual ~Goal() virtual ~Goal()
@ -93,6 +96,11 @@ public:
return name; return name;
} }
ExitCode getExitCode()
{
return exitCode;
}
protected: protected:
void amDone(bool success = true); void amDone(bool success = true);
}; };
@ -165,9 +173,8 @@ public:
/* Add a goal to the set of goals waiting for a build slot. */ /* Add a goal to the set of goals waiting for a build slot. */
void waitForBuildSlot(GoalPtr goal, bool reallyWait = false); void waitForBuildSlot(GoalPtr goal, bool reallyWait = false);
/* Loop until the specified top-level goal has finished. Returns /* Loop until the specified top-level goals have finished. */
true if it has finished succesfully. */ void run(const Goals & topGoals);
bool run(const Goals & topGoals);
/* Wait for input to become available. */ /* Wait for input to become available. */
void waitForInput(); void waitForInput();
@ -232,8 +239,8 @@ void Goal::waiteeDone(GoalPtr waitee, bool success)
void Goal::amDone(bool success) void Goal::amDone(bool success)
{ {
trace("done"); trace("done");
assert(!done); assert(exitCode == ecBusy);
done = true; exitCode = success ? ecSuccess : ecFailed;
for (WeakGoals::iterator i = waiters.begin(); i != waiters.end(); ++i) { for (WeakGoals::iterator i = waiters.begin(); i != waiters.end(); ++i) {
GoalPtr goal = i->lock(); GoalPtr goal = i->lock();
if (goal) goal->waiteeDone(shared_from_this(), success); if (goal) goal->waiteeDone(shared_from_this(), success);
@ -342,6 +349,11 @@ public:
void work(); void work();
Path getDrvPath()
{
return drvPath;
}
private: private:
/* The states. */ /* The states. */
void init(); void init();
@ -1549,41 +1561,6 @@ void SubstitutionGoal::writeLog(int fd,
//////////////////////////////////////////////////////////////////////
/* 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;
name = "pseudo-goal";
}
void work()
{
abort();
}
void waiteeDone(GoalPtr waitee, bool success)
{
if (!success) this->success = false;
}
bool isOkay()
{
return success;
}
};
////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////
@ -1653,9 +1630,15 @@ static void removeGoal(GoalPtr goal, WeakGoalMap & goalMap)
void Worker::removeGoal(GoalPtr goal) void Worker::removeGoal(GoalPtr goal)
{ {
topGoals.erase(goal);
::removeGoal(goal, derivationGoals); ::removeGoal(goal, derivationGoals);
::removeGoal(goal, substitutionGoals); ::removeGoal(goal, substitutionGoals);
if (topGoals.find(goal) != topGoals.end()) {
topGoals.erase(goal);
/* If a top-level goal failed, then kill all other goals
(unless keepGoing was set). */
if (goal->getExitCode() == Goal::ecFailed && !keepGoing)
topGoals.clear();
}
} }
@ -1725,18 +1708,11 @@ void Worker::waitForBuildSlot(GoalPtr goal, bool reallyWait)
} }
bool Worker::run(const Goals & _topGoals) void Worker::run(const Goals & _topGoals)
{ {
/* Wrap the specified top-level goal in a pseudo-goal so that we
can check whether it succeeded. */
shared_ptr<PseudoGoal> pseudo(new PseudoGoal(*this));
for (Goals::iterator i = _topGoals.begin(); for (Goals::iterator i = _topGoals.begin();
i != _topGoals.end(); ++i) i != _topGoals.end(); ++i)
{
assert(*i);
pseudo->addWaitee(*i);
topGoals.insert(*i); topGoals.insert(*i);
}
startNest(nest, lvlDebug, format("entered goal loop")); startNest(nest, lvlDebug, format("entered goal loop"));
@ -1745,13 +1721,14 @@ bool Worker::run(const Goals & _topGoals)
checkInterrupt(); checkInterrupt();
/* Call every wake goal. */ /* Call every wake goal. */
while (!awake.empty()) { while (!awake.empty() && !topGoals.empty()) {
WeakGoals awake2(awake); WeakGoals awake2(awake);
awake.clear(); awake.clear();
for (WeakGoals::iterator i = awake2.begin(); i != awake2.end(); ++i) { for (WeakGoals::iterator i = awake2.begin(); i != awake2.end(); ++i) {
checkInterrupt(); checkInterrupt();
GoalPtr goal = i->lock(); GoalPtr goal = i->lock();
if (goal) goal->work(); if (goal) goal->work();
if (topGoals.empty()) break;
} }
} }
@ -1770,8 +1747,6 @@ bool Worker::run(const Goals & _topGoals)
assert(!keepGoing || awake.empty()); assert(!keepGoing || awake.empty());
assert(!keepGoing || wantingToBuild.empty()); assert(!keepGoing || wantingToBuild.empty());
assert(!keepGoing || children.empty()); assert(!keepGoing || children.empty());
return pseudo->isOkay();
} }
@ -1847,8 +1822,18 @@ void buildDerivations(const PathSet & drvPaths)
i != drvPaths.end(); ++i) i != drvPaths.end(); ++i)
goals.insert(worker.makeDerivationGoal(*i)); goals.insert(worker.makeDerivationGoal(*i));
if (!worker.run(goals)) worker.run(goals);
throw Error(format("build failed"));
PathSet failed;
for (Goals::iterator i = goals.begin(); i != goals.end(); ++i)
if ((*i)->getExitCode() == Goal::ecFailed) {
DerivationGoal * i2 = dynamic_cast<DerivationGoal *>(i->get());
assert(i2);
failed.insert(i2->getDrvPath());
}
if (!failed.empty())
throw Error(format("build of %1% failed") % showPaths(failed));
} }
@ -1858,8 +1843,11 @@ void ensurePath(const Path & path)
if (isValidPath(path)) return; if (isValidPath(path)) return;
Worker worker; Worker worker;
Goals goals; GoalPtr goal = worker.makeSubstitutionGoal(path);
goals.insert(worker.makeSubstitutionGoal(path)); Goals goals = singleton<Goals>(goal);
if (!worker.run(goals))
worker.run(goals);
if (goal->getExitCode() != Goal::ecSuccess)
throw Error(format("path `%1%' does not exist and cannot be created") % path); throw Error(format("path `%1%' does not exist and cannot be created") % path);
} }