forked from lix-project/lix
* 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:
parent
3a2c3f0cf2
commit
bfaf83a0fd
|
@ -42,6 +42,9 @@ typedef map<Path, WeakGoalPtr> WeakGoalMap;
|
|||
|
||||
class Goal : public enable_shared_from_this<Goal>
|
||||
{
|
||||
public:
|
||||
typedef enum {ecBusy, ecSuccess, ecFailed} ExitCode;
|
||||
|
||||
protected:
|
||||
|
||||
/* Backlink to the worker. */
|
||||
|
@ -57,16 +60,16 @@ protected:
|
|||
/* Number of goals we are/were waiting for that have failed. */
|
||||
unsigned int nrFailed;
|
||||
|
||||
/* Whether amDone() has been called. */
|
||||
bool done;
|
||||
|
||||
/* Name of this goal for debugging purposes. */
|
||||
string name;
|
||||
|
||||
/* Whether the goal is finished. */
|
||||
ExitCode exitCode;
|
||||
|
||||
Goal(Worker & worker) : worker(worker)
|
||||
{
|
||||
done = false;
|
||||
nrFailed = 0;
|
||||
exitCode = ecBusy;
|
||||
}
|
||||
|
||||
virtual ~Goal()
|
||||
|
@ -93,6 +96,11 @@ public:
|
|||
return name;
|
||||
}
|
||||
|
||||
ExitCode getExitCode()
|
||||
{
|
||||
return exitCode;
|
||||
}
|
||||
|
||||
protected:
|
||||
void amDone(bool success = true);
|
||||
};
|
||||
|
@ -165,9 +173,8 @@ public:
|
|||
/* Add a goal to the set of goals waiting for a build slot. */
|
||||
void waitForBuildSlot(GoalPtr goal, bool reallyWait = false);
|
||||
|
||||
/* Loop until the specified top-level goal has finished. Returns
|
||||
true if it has finished succesfully. */
|
||||
bool run(const Goals & topGoals);
|
||||
/* Loop until the specified top-level goals have finished. */
|
||||
void run(const Goals & topGoals);
|
||||
|
||||
/* Wait for input to become available. */
|
||||
void waitForInput();
|
||||
|
@ -232,8 +239,8 @@ void Goal::waiteeDone(GoalPtr waitee, bool success)
|
|||
void Goal::amDone(bool success)
|
||||
{
|
||||
trace("done");
|
||||
assert(!done);
|
||||
done = true;
|
||||
assert(exitCode == ecBusy);
|
||||
exitCode = success ? ecSuccess : ecFailed;
|
||||
for (WeakGoals::iterator i = waiters.begin(); i != waiters.end(); ++i) {
|
||||
GoalPtr goal = i->lock();
|
||||
if (goal) goal->waiteeDone(shared_from_this(), success);
|
||||
|
@ -342,6 +349,11 @@ public:
|
|||
|
||||
void work();
|
||||
|
||||
Path getDrvPath()
|
||||
{
|
||||
return drvPath;
|
||||
}
|
||||
|
||||
private:
|
||||
/* The states. */
|
||||
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)
|
||||
{
|
||||
topGoals.erase(goal);
|
||||
::removeGoal(goal, derivationGoals);
|
||||
::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();
|
||||
i != _topGoals.end(); ++i)
|
||||
{
|
||||
assert(*i);
|
||||
pseudo->addWaitee(*i);
|
||||
topGoals.insert(*i);
|
||||
}
|
||||
|
||||
startNest(nest, lvlDebug, format("entered goal loop"));
|
||||
|
||||
|
@ -1745,13 +1721,14 @@ bool Worker::run(const Goals & _topGoals)
|
|||
checkInterrupt();
|
||||
|
||||
/* Call every wake goal. */
|
||||
while (!awake.empty()) {
|
||||
while (!awake.empty() && !topGoals.empty()) {
|
||||
WeakGoals awake2(awake);
|
||||
awake.clear();
|
||||
for (WeakGoals::iterator i = awake2.begin(); i != awake2.end(); ++i) {
|
||||
checkInterrupt();
|
||||
GoalPtr goal = i->lock();
|
||||
if (goal) goal->work();
|
||||
if (topGoals.empty()) break;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1770,8 +1747,6 @@ bool Worker::run(const Goals & _topGoals)
|
|||
assert(!keepGoing || awake.empty());
|
||||
assert(!keepGoing || wantingToBuild.empty());
|
||||
assert(!keepGoing || children.empty());
|
||||
|
||||
return pseudo->isOkay();
|
||||
}
|
||||
|
||||
|
||||
|
@ -1847,8 +1822,18 @@ void buildDerivations(const PathSet & drvPaths)
|
|||
i != drvPaths.end(); ++i)
|
||||
goals.insert(worker.makeDerivationGoal(*i));
|
||||
|
||||
if (!worker.run(goals))
|
||||
throw Error(format("build failed"));
|
||||
worker.run(goals);
|
||||
|
||||
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;
|
||||
|
||||
Worker worker;
|
||||
Goals goals;
|
||||
goals.insert(worker.makeSubstitutionGoal(path));
|
||||
if (!worker.run(goals))
|
||||
GoalPtr goal = worker.makeSubstitutionGoal(path);
|
||||
Goals goals = singleton<Goals>(goal);
|
||||
|
||||
worker.run(goals);
|
||||
|
||||
if (goal->getExitCode() != Goal::ecSuccess)
|
||||
throw Error(format("path `%1%' does not exist and cannot be created") % path);
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue