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
1 changed files with 50 additions and 62 deletions
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue