2015-07-21 15:14:17 +02:00
|
|
|
|
#include <cmath>
|
|
|
|
|
|
|
|
|
|
#include "state.hh"
|
|
|
|
|
#include "build-result.hh"
|
2016-04-13 16:18:35 +02:00
|
|
|
|
#include "finally.hh"
|
2017-03-15 16:43:54 +01:00
|
|
|
|
#include "binary-cache-store.hh"
|
2015-07-21 15:14:17 +02:00
|
|
|
|
|
|
|
|
|
using namespace nix;
|
|
|
|
|
|
|
|
|
|
|
2017-07-25 15:58:54 +02:00
|
|
|
|
void setThreadName(const std::string & name)
|
|
|
|
|
{
|
|
|
|
|
#ifdef __linux__
|
|
|
|
|
pthread_setname_np(pthread_self(), std::string(name, 0, 15).c_str());
|
|
|
|
|
#endif
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2015-08-17 13:50:41 +02:00
|
|
|
|
void State::builder(MachineReservation::ptr reservation)
|
2015-07-21 15:14:17 +02:00
|
|
|
|
{
|
2019-12-30 22:49:26 +01:00
|
|
|
|
setThreadName("bld~" + std::string(reservation->step->drvPath.to_string()));
|
2017-07-25 15:58:54 +02:00
|
|
|
|
|
2016-03-08 13:09:39 +01:00
|
|
|
|
StepResult res = sRetry;
|
2015-07-21 15:14:17 +02:00
|
|
|
|
|
2016-03-02 14:18:39 +01:00
|
|
|
|
nrStepsStarted++;
|
|
|
|
|
|
2016-11-08 11:42:31 +01:00
|
|
|
|
Step::wptr wstep = reservation->step;
|
|
|
|
|
|
|
|
|
|
{
|
|
|
|
|
auto activeStep = std::make_shared<ActiveStep>();
|
|
|
|
|
activeStep->step = reservation->step;
|
|
|
|
|
activeSteps_.lock()->insert(activeStep);
|
|
|
|
|
|
|
|
|
|
Finally removeActiveStep([&]() {
|
|
|
|
|
activeSteps_.lock()->erase(activeStep);
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
auto destStore = getDestStore();
|
|
|
|
|
res = doBuildStep(destStore, reservation, activeStep);
|
|
|
|
|
} catch (std::exception & e) {
|
2019-12-30 22:49:26 +01:00
|
|
|
|
printMsg(lvlError, "uncaught exception building ‘%s’ on ‘%s’: %s",
|
|
|
|
|
localStore->printStorePath(reservation->step->drvPath),
|
|
|
|
|
reservation->machine->sshName,
|
|
|
|
|
e.what());
|
2016-11-08 11:42:31 +01:00
|
|
|
|
}
|
2015-07-21 15:14:17 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Release the machine and wake up the dispatcher. */
|
|
|
|
|
assert(reservation.unique());
|
|
|
|
|
reservation = 0;
|
|
|
|
|
wakeDispatcher();
|
|
|
|
|
|
|
|
|
|
/* If there was a temporary failure, retry the step after an
|
|
|
|
|
exponentially increasing interval. */
|
2016-11-08 11:42:31 +01:00
|
|
|
|
Step::ptr step = wstep.lock();
|
|
|
|
|
if (res != sDone && step) {
|
|
|
|
|
|
2016-03-08 13:09:39 +01:00
|
|
|
|
if (res == sRetry) {
|
2015-07-21 15:14:17 +02:00
|
|
|
|
auto step_(step->state.lock());
|
|
|
|
|
step_->tries++;
|
|
|
|
|
nrRetries++;
|
|
|
|
|
if (step_->tries > maxNrRetries) maxNrRetries = step_->tries; // yeah yeah, not atomic
|
2016-09-30 17:05:07 +02:00
|
|
|
|
int delta = retryInterval * std::pow(retryBackoff, step_->tries - 1) + (rand() % 10);
|
2019-12-30 22:49:26 +01:00
|
|
|
|
printMsg(lvlInfo, "will retry ‘%s’ after %ss", localStore->printStorePath(step->drvPath), delta);
|
2015-07-21 15:14:17 +02:00
|
|
|
|
step_->after = std::chrono::system_clock::now() + std::chrono::seconds(delta);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
makeRunnable(step);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2016-11-07 19:34:35 +01:00
|
|
|
|
State::StepResult State::doBuildStep(nix::ref<Store> destStore,
|
|
|
|
|
MachineReservation::ptr reservation,
|
|
|
|
|
std::shared_ptr<ActiveStep> activeStep)
|
2015-07-21 15:14:17 +02:00
|
|
|
|
{
|
2016-11-07 19:34:35 +01:00
|
|
|
|
auto & step(reservation->step);
|
|
|
|
|
auto & machine(reservation->machine);
|
|
|
|
|
|
2015-07-21 15:14:17 +02:00
|
|
|
|
{
|
|
|
|
|
auto step_(step->state.lock());
|
|
|
|
|
assert(step_->created);
|
|
|
|
|
assert(!step->finished);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* There can be any number of builds in the database that depend
|
|
|
|
|
on this derivation. Arbitrarily pick one (though preferring a
|
|
|
|
|
build of which this is the top-level derivation) for the
|
|
|
|
|
purpose of creating build steps. We could create a build step
|
|
|
|
|
record for every build, but that could be very expensive
|
|
|
|
|
(e.g. a stdenv derivation can be a dependency of tens of
|
2016-10-31 14:58:29 +01:00
|
|
|
|
thousands of builds), so we don't.
|
|
|
|
|
|
|
|
|
|
We don't keep a Build::ptr here to allow
|
|
|
|
|
State::processQueueChange() to detect whether a step can be
|
|
|
|
|
cancelled (namely if there are no more Builds referring to
|
|
|
|
|
it). */
|
|
|
|
|
BuildID buildId;
|
2019-12-30 22:49:26 +01:00
|
|
|
|
std::optional<StorePath> buildDrvPath;
|
2016-10-31 14:58:29 +01:00
|
|
|
|
unsigned int maxSilentTime, buildTimeout;
|
2016-12-07 15:57:13 +01:00
|
|
|
|
unsigned int repeats = step->isDeterministic ? 1 : 0;
|
2015-07-21 15:14:17 +02:00
|
|
|
|
|
2019-08-09 19:11:38 +02:00
|
|
|
|
auto conn(dbPool.get());
|
|
|
|
|
|
2015-07-21 15:14:17 +02:00
|
|
|
|
{
|
|
|
|
|
std::set<Build::ptr> dependents;
|
|
|
|
|
std::set<Step::ptr> steps;
|
|
|
|
|
getDependents(step, dependents, steps);
|
|
|
|
|
|
|
|
|
|
if (dependents.empty()) {
|
|
|
|
|
/* Apparently all builds that depend on this derivation
|
|
|
|
|
are gone (e.g. cancelled). So don't bother. This is
|
|
|
|
|
very unlikely to happen, because normally Steps are
|
|
|
|
|
only kept alive by being reachable from a
|
|
|
|
|
Build. However, it's possible that a new Build just
|
|
|
|
|
created a reference to this step. So to handle that
|
|
|
|
|
possibility, we retry this step (putting it back in
|
|
|
|
|
the runnable queue). If there are really no strong
|
|
|
|
|
pointers to the step, it will be deleted. */
|
2019-12-30 22:49:26 +01:00
|
|
|
|
printMsg(lvlInfo, "maybe cancelling build step ‘%s’", localStore->printStorePath(step->drvPath));
|
2016-03-08 13:09:39 +01:00
|
|
|
|
return sMaybeCancelled;
|
2015-07-21 15:14:17 +02:00
|
|
|
|
}
|
|
|
|
|
|
2016-10-31 14:58:29 +01:00
|
|
|
|
Build::ptr build;
|
|
|
|
|
|
2016-03-11 21:48:31 -05:00
|
|
|
|
for (auto build2 : dependents) {
|
|
|
|
|
if (build2->drvPath == step->drvPath) {
|
2019-08-09 19:11:38 +02:00
|
|
|
|
build = build2;
|
|
|
|
|
pqxx::work txn(*conn);
|
|
|
|
|
notifyBuildStarted(txn, build->id);
|
|
|
|
|
txn.commit();
|
2016-03-11 21:48:31 -05:00
|
|
|
|
}
|
2016-12-07 15:57:13 +01:00
|
|
|
|
{
|
|
|
|
|
auto i = jobsetRepeats.find(std::make_pair(build2->projectName, build2->jobsetName));
|
|
|
|
|
if (i != jobsetRepeats.end())
|
|
|
|
|
repeats = std::max(repeats, i->second);
|
|
|
|
|
}
|
2016-03-11 21:48:31 -05:00
|
|
|
|
}
|
2015-07-21 15:14:17 +02:00
|
|
|
|
if (!build) build = *dependents.begin();
|
|
|
|
|
|
2016-10-31 14:58:29 +01:00
|
|
|
|
buildId = build->id;
|
2019-12-30 22:49:26 +01:00
|
|
|
|
buildDrvPath = build->drvPath.clone();
|
2016-10-31 14:58:29 +01:00
|
|
|
|
maxSilentTime = build->maxSilentTime;
|
|
|
|
|
buildTimeout = build->buildTimeout;
|
|
|
|
|
|
2016-12-07 15:57:13 +01:00
|
|
|
|
printInfo("performing step ‘%s’ %d times on ‘%s’ (needed by build %d and %d others)",
|
2019-12-30 22:49:26 +01:00
|
|
|
|
localStore->printStorePath(step->drvPath), repeats + 1, machine->sshName, buildId, (dependents.size() - 1));
|
2015-07-21 15:14:17 +02:00
|
|
|
|
}
|
|
|
|
|
|
2019-12-30 22:49:26 +01:00
|
|
|
|
bool quit = buildId == buildOne && step->drvPath == *buildDrvPath;
|
2015-07-21 15:14:17 +02:00
|
|
|
|
|
|
|
|
|
RemoteResult result;
|
|
|
|
|
BuildOutput res;
|
2016-05-27 14:32:48 +02:00
|
|
|
|
unsigned int stepNr = 0;
|
2016-04-13 16:18:35 +02:00
|
|
|
|
bool stepFinished = false;
|
|
|
|
|
|
|
|
|
|
Finally clearStep([&]() {
|
|
|
|
|
if (stepNr && !stepFinished) {
|
2016-10-31 14:58:29 +01:00
|
|
|
|
printError("marking step %d of build %d as orphaned", stepNr, buildId);
|
2016-04-13 16:18:35 +02:00
|
|
|
|
auto orphanedSteps_(orphanedSteps.lock());
|
2016-10-31 14:58:29 +01:00
|
|
|
|
orphanedSteps_->emplace(buildId, stepNr);
|
2016-04-13 16:18:35 +02:00
|
|
|
|
}
|
2017-03-13 17:18:22 +01:00
|
|
|
|
|
|
|
|
|
if (stepNr) {
|
2017-03-15 16:43:54 +01:00
|
|
|
|
/* Upload the log file to the binary cache. FIXME: should
|
|
|
|
|
be done on a worker thread. */
|
|
|
|
|
try {
|
|
|
|
|
auto store = destStore.dynamic_pointer_cast<BinaryCacheStore>();
|
|
|
|
|
if (uploadLogsToBinaryCache && store && pathExists(result.logFile)) {
|
2019-12-30 22:49:26 +01:00
|
|
|
|
store->upsertFile("log/" + std::string(step->drvPath.to_string()), readFile(result.logFile), "text/plain; charset=utf-8");
|
2017-03-15 16:43:54 +01:00
|
|
|
|
unlink(result.logFile.c_str());
|
|
|
|
|
}
|
|
|
|
|
} catch (...) {
|
|
|
|
|
ignoreException();
|
|
|
|
|
}
|
2017-03-13 17:18:22 +01:00
|
|
|
|
}
|
2016-04-13 16:18:35 +02:00
|
|
|
|
});
|
2015-07-21 15:14:17 +02:00
|
|
|
|
|
|
|
|
|
time_t stepStartTime = result.startTime = time(0);
|
|
|
|
|
|
|
|
|
|
/* If any of the outputs have previously failed, then don't bother
|
|
|
|
|
building again. */
|
2016-03-09 16:59:38 +01:00
|
|
|
|
if (checkCachedFailure(step, *conn))
|
|
|
|
|
result.stepStatus = bsCachedFailure;
|
2015-07-21 15:14:17 +02:00
|
|
|
|
else {
|
|
|
|
|
|
|
|
|
|
/* Create a build step record indicating that we started
|
2015-10-27 15:37:17 +01:00
|
|
|
|
building. */
|
2015-07-21 15:14:17 +02:00
|
|
|
|
{
|
2016-02-29 15:10:30 +01:00
|
|
|
|
auto mc = startDbUpdate();
|
2015-07-21 15:14:17 +02:00
|
|
|
|
pqxx::work txn(*conn);
|
2016-10-31 14:58:29 +01:00
|
|
|
|
stepNr = createBuildStep(txn, result.startTime, buildId, step, machine->sshName, bsBusy);
|
2015-07-21 15:14:17 +02:00
|
|
|
|
txn.commit();
|
|
|
|
|
}
|
|
|
|
|
|
2017-12-07 15:35:31 +01:00
|
|
|
|
auto updateStep = [&](StepState stepState) {
|
|
|
|
|
pqxx::work txn(*conn);
|
|
|
|
|
updateBuildStep(txn, buildId, stepNr, stepState);
|
|
|
|
|
txn.commit();
|
|
|
|
|
};
|
|
|
|
|
|
2015-07-21 15:14:17 +02:00
|
|
|
|
/* Do the build. */
|
|
|
|
|
try {
|
|
|
|
|
/* FIXME: referring builds may have conflicting timeouts. */
|
2017-12-07 15:35:31 +01:00
|
|
|
|
buildRemote(destStore, machine, step, maxSilentTime, buildTimeout, repeats, result, activeStep, updateStep);
|
2016-03-09 16:59:38 +01:00
|
|
|
|
} catch (NoTokens & e) {
|
|
|
|
|
result.stepStatus = bsNarSizeLimitExceeded;
|
2015-07-21 15:14:17 +02:00
|
|
|
|
} catch (Error & e) {
|
2016-11-07 19:34:35 +01:00
|
|
|
|
if (activeStep->state_.lock()->cancelled) {
|
2016-11-02 12:44:18 +01:00
|
|
|
|
printInfo("marking step %d of build %d as cancelled", stepNr, buildId);
|
2016-11-07 19:34:35 +01:00
|
|
|
|
result.stepStatus = bsCancelled;
|
|
|
|
|
result.canRetry = false;
|
|
|
|
|
} else {
|
|
|
|
|
result.stepStatus = bsAborted;
|
|
|
|
|
result.errorMsg = e.msg();
|
|
|
|
|
result.canRetry = true;
|
2016-10-31 14:58:29 +01:00
|
|
|
|
}
|
2015-07-21 15:14:17 +02:00
|
|
|
|
}
|
|
|
|
|
|
2017-12-07 15:35:31 +01:00
|
|
|
|
if (result.stepStatus == bsSuccess) {
|
|
|
|
|
updateStep(ssPostProcessing);
|
2019-12-30 22:49:26 +01:00
|
|
|
|
res = getBuildOutput(destStore, ref<FSAccessor>(result.accessor), *step->drv);
|
2017-12-07 15:35:31 +01:00
|
|
|
|
}
|
2016-11-16 17:46:00 +01:00
|
|
|
|
|
|
|
|
|
result.accessor = 0;
|
|
|
|
|
result.tokens = 0;
|
2015-07-21 15:14:17 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
time_t stepStopTime = time(0);
|
|
|
|
|
if (!result.stopTime) result.stopTime = stepStopTime;
|
|
|
|
|
|
2019-08-12 15:50:43 +02:00
|
|
|
|
/* For standard failures, we don't care about the error
|
|
|
|
|
message. */
|
|
|
|
|
if (result.stepStatus != bsAborted)
|
|
|
|
|
result.errorMsg = "";
|
|
|
|
|
|
2015-08-11 01:30:24 +02:00
|
|
|
|
/* Account the time we spent building this step by dividing it
|
|
|
|
|
among the jobsets that depend on it. */
|
|
|
|
|
{
|
|
|
|
|
auto step_(step->state.lock());
|
2015-10-30 18:01:48 +01:00
|
|
|
|
if (!step_->jobsets.empty()) {
|
|
|
|
|
// FIXME: loss of precision.
|
|
|
|
|
time_t charge = (result.stopTime - result.startTime) / step_->jobsets.size();
|
|
|
|
|
for (auto & jobset : step_->jobsets)
|
|
|
|
|
jobset->addStep(result.startTime, charge);
|
|
|
|
|
}
|
2015-08-11 01:30:24 +02:00
|
|
|
|
}
|
|
|
|
|
|
2019-08-12 15:50:43 +02:00
|
|
|
|
/* Finish the step in the database. */
|
|
|
|
|
if (stepNr) {
|
|
|
|
|
pqxx::work txn(*conn);
|
|
|
|
|
finishBuildStep(txn, result, buildId, stepNr, machine->sshName);
|
|
|
|
|
txn.commit();
|
|
|
|
|
}
|
|
|
|
|
|
2015-07-21 15:14:17 +02:00
|
|
|
|
/* The step had a hopefully temporary failure (e.g. network
|
|
|
|
|
issue). Retry a number of times. */
|
2016-03-09 16:59:38 +01:00
|
|
|
|
if (result.canRetry) {
|
2019-12-30 22:49:26 +01:00
|
|
|
|
printMsg(lvlError, "possibly transient failure building ‘%s’ on ‘%s’: %s",
|
|
|
|
|
localStore->printStorePath(step->drvPath), machine->sshName, result.errorMsg);
|
2016-10-26 14:42:28 +02:00
|
|
|
|
assert(stepNr);
|
2015-07-21 15:14:17 +02:00
|
|
|
|
bool retry;
|
|
|
|
|
{
|
|
|
|
|
auto step_(step->state.lock());
|
|
|
|
|
retry = step_->tries + 1 < maxTries;
|
|
|
|
|
}
|
|
|
|
|
if (retry) {
|
2016-02-29 15:10:30 +01:00
|
|
|
|
auto mc = startDbUpdate();
|
2016-04-13 16:18:35 +02:00
|
|
|
|
stepFinished = true;
|
2015-07-21 15:14:17 +02:00
|
|
|
|
if (quit) exit(1);
|
2016-03-08 13:09:39 +01:00
|
|
|
|
return sRetry;
|
2015-07-21 15:14:17 +02:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2016-03-09 16:59:38 +01:00
|
|
|
|
if (result.stepStatus == bsSuccess) {
|
2015-07-21 15:14:17 +02:00
|
|
|
|
|
2016-10-26 14:42:28 +02:00
|
|
|
|
assert(stepNr);
|
|
|
|
|
|
2019-12-30 22:49:26 +01:00
|
|
|
|
for (auto & path : step->drv->outputPaths())
|
2017-10-19 12:28:06 +02:00
|
|
|
|
addRoot(path);
|
2017-10-12 18:55:38 +02:00
|
|
|
|
|
2015-07-21 15:14:17 +02:00
|
|
|
|
/* Register success in the database for all Build objects that
|
|
|
|
|
have this step as the top-level step. Since the queue
|
|
|
|
|
monitor thread may be creating new referring Builds
|
|
|
|
|
concurrently, and updating the database may fail, we do
|
|
|
|
|
this in a loop, marking all known builds, repeating until
|
|
|
|
|
there are no unmarked builds.
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
std::vector<BuildID> buildIDs;
|
|
|
|
|
|
|
|
|
|
while (true) {
|
|
|
|
|
|
|
|
|
|
/* Get the builds that have this one as the top-level. */
|
|
|
|
|
std::vector<Build::ptr> direct;
|
|
|
|
|
{
|
|
|
|
|
auto steps_(steps.lock());
|
|
|
|
|
auto step_(step->state.lock());
|
|
|
|
|
|
|
|
|
|
for (auto & b_ : step_->builds) {
|
|
|
|
|
auto b = b_.lock();
|
|
|
|
|
if (b && !b->finishedInDB) direct.push_back(b);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* If there are no builds left to update in the DB,
|
|
|
|
|
then we're done (except for calling
|
|
|
|
|
finishBuildStep()). Delete the step from
|
|
|
|
|
‘steps’. Since we've been holding the ‘steps’ lock,
|
|
|
|
|
no new referrers can have been added in the
|
|
|
|
|
meantime or be added afterwards. */
|
|
|
|
|
if (direct.empty()) {
|
2019-12-30 22:49:26 +01:00
|
|
|
|
printMsg(lvlDebug, "finishing build step ‘%s’",
|
|
|
|
|
localStore->printStorePath(step->drvPath));
|
2015-07-21 15:14:17 +02:00
|
|
|
|
steps_->erase(step->drvPath);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Update the database. */
|
|
|
|
|
{
|
2016-02-29 15:10:30 +01:00
|
|
|
|
auto mc = startDbUpdate();
|
|
|
|
|
|
2015-07-21 15:14:17 +02:00
|
|
|
|
pqxx::work txn(*conn);
|
|
|
|
|
|
2016-04-13 13:42:05 +02:00
|
|
|
|
for (auto & b : direct) {
|
|
|
|
|
printMsg(lvlInfo, format("marking build %1% as succeeded") % b->id);
|
2016-10-31 14:58:29 +01:00
|
|
|
|
markSucceededBuild(txn, b, res, buildId != b->id || result.isCached,
|
2015-07-21 15:14:17 +02:00
|
|
|
|
result.startTime, result.stopTime);
|
2016-04-13 13:42:05 +02:00
|
|
|
|
}
|
2015-07-21 15:14:17 +02:00
|
|
|
|
|
|
|
|
|
txn.commit();
|
|
|
|
|
}
|
|
|
|
|
|
2016-04-13 16:18:35 +02:00
|
|
|
|
stepFinished = true;
|
|
|
|
|
|
2015-07-21 15:14:17 +02:00
|
|
|
|
if (direct.empty()) break;
|
|
|
|
|
|
|
|
|
|
/* Remove the direct dependencies from ‘builds’. This will
|
|
|
|
|
cause them to be destroyed. */
|
|
|
|
|
for (auto & b : direct) {
|
|
|
|
|
auto builds_(builds.lock());
|
|
|
|
|
b->finishedInDB = true;
|
|
|
|
|
builds_->erase(b->id);
|
|
|
|
|
buildIDs.push_back(b->id);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Send notification about the builds that have this step as
|
|
|
|
|
the top-level. */
|
2019-08-09 19:11:38 +02:00
|
|
|
|
{
|
|
|
|
|
pqxx::work txn(*conn);
|
|
|
|
|
for (auto id : buildIDs)
|
|
|
|
|
notifyBuildFinished(txn, id, {});
|
|
|
|
|
txn.commit();
|
|
|
|
|
}
|
2015-07-21 15:14:17 +02:00
|
|
|
|
|
|
|
|
|
/* Wake up any dependent steps that have no other
|
|
|
|
|
dependencies. */
|
|
|
|
|
{
|
|
|
|
|
auto step_(step->state.lock());
|
|
|
|
|
for (auto & rdepWeak : step_->rdeps) {
|
|
|
|
|
auto rdep = rdepWeak.lock();
|
|
|
|
|
if (!rdep) continue;
|
|
|
|
|
|
|
|
|
|
bool runnable = false;
|
|
|
|
|
{
|
|
|
|
|
auto rdep_(rdep->state.lock());
|
|
|
|
|
rdep_->deps.erase(step);
|
|
|
|
|
/* Note: if the step has not finished
|
|
|
|
|
initialisation yet, it will be made runnable in
|
|
|
|
|
createStep(), if appropriate. */
|
|
|
|
|
if (rdep_->deps.empty() && rdep_->created) runnable = true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (runnable) makeRunnable(rdep);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2020-03-26 15:00:04 +01:00
|
|
|
|
} else
|
|
|
|
|
failStep(*conn, step, buildId, result, machine, stepFinished, quit);
|
2015-07-21 15:14:17 +02:00
|
|
|
|
|
2020-03-26 15:00:04 +01:00
|
|
|
|
// FIXME: keep stats about aborted steps?
|
|
|
|
|
nrStepsDone++;
|
|
|
|
|
totalStepTime += stepStopTime - stepStartTime;
|
|
|
|
|
totalStepBuildTime += result.stopTime - result.startTime;
|
|
|
|
|
machine->state->nrStepsDone++;
|
|
|
|
|
machine->state->totalStepTime += stepStopTime - stepStartTime;
|
|
|
|
|
machine->state->totalStepBuildTime += result.stopTime - result.startTime;
|
2015-07-21 15:14:17 +02:00
|
|
|
|
|
2020-03-26 15:00:04 +01:00
|
|
|
|
if (quit) exit(0); // testing hack; FIXME: this won't run plugins
|
2015-07-21 15:14:17 +02:00
|
|
|
|
|
2020-03-26 15:00:04 +01:00
|
|
|
|
return sDone;
|
|
|
|
|
}
|
2015-07-21 15:14:17 +02:00
|
|
|
|
|
2016-10-26 14:42:28 +02:00
|
|
|
|
|
2020-03-26 15:00:04 +01:00
|
|
|
|
void State::failStep(
|
|
|
|
|
Connection & conn,
|
|
|
|
|
Step::ptr step,
|
|
|
|
|
BuildID buildId,
|
|
|
|
|
const RemoteResult & result,
|
|
|
|
|
Machine::ptr machine,
|
|
|
|
|
bool & stepFinished,
|
|
|
|
|
bool & quit)
|
|
|
|
|
{
|
|
|
|
|
/* Register failure in the database for all Build objects that
|
|
|
|
|
directly or indirectly depend on this step. */
|
2016-02-29 15:10:30 +01:00
|
|
|
|
|
2020-03-26 15:00:04 +01:00
|
|
|
|
std::vector<BuildID> dependentIDs;
|
2015-07-21 15:14:17 +02:00
|
|
|
|
|
2020-03-26 15:00:04 +01:00
|
|
|
|
while (true) {
|
|
|
|
|
/* Get the builds and steps that depend on this step. */
|
|
|
|
|
std::set<Build::ptr> indirect;
|
|
|
|
|
{
|
|
|
|
|
auto steps_(steps.lock());
|
|
|
|
|
std::set<Step::ptr> steps;
|
|
|
|
|
getDependents(step, indirect, steps);
|
|
|
|
|
|
|
|
|
|
/* If there are no builds left, delete all referring
|
|
|
|
|
steps from ‘steps’. As for the success case, we can
|
|
|
|
|
be certain no new referrers can be added. */
|
|
|
|
|
if (indirect.empty()) {
|
|
|
|
|
for (auto & s : steps) {
|
|
|
|
|
printMsg(lvlDebug, "finishing build step ‘%s’",
|
|
|
|
|
localStore->printStorePath(s->drvPath));
|
|
|
|
|
steps_->erase(s->drvPath);
|
2015-07-21 15:14:17 +02:00
|
|
|
|
}
|
2020-03-26 15:00:04 +01:00
|
|
|
|
}
|
|
|
|
|
}
|
2015-07-21 15:14:17 +02:00
|
|
|
|
|
2020-03-26 15:00:04 +01:00
|
|
|
|
if (indirect.empty() && stepFinished) break;
|
2015-07-21 15:14:17 +02:00
|
|
|
|
|
2020-03-26 15:00:04 +01:00
|
|
|
|
/* Update the database. */
|
|
|
|
|
{
|
|
|
|
|
auto mc = startDbUpdate();
|
2015-07-21 15:14:17 +02:00
|
|
|
|
|
2020-03-26 15:00:04 +01:00
|
|
|
|
pqxx::work txn(conn);
|
|
|
|
|
|
|
|
|
|
/* Create failed build steps for every build that
|
|
|
|
|
depends on this, except when this step is cached
|
|
|
|
|
and is the top-level of that build (since then it's
|
|
|
|
|
redundant with the build's isCachedBuild field). */
|
|
|
|
|
for (auto & build : indirect) {
|
|
|
|
|
if ((result.stepStatus == bsCachedFailure && build->drvPath == step->drvPath) ||
|
|
|
|
|
((result.stepStatus != bsCachedFailure && result.stepStatus != bsUnsupported) && buildId == build->id) ||
|
|
|
|
|
build->finishedInDB)
|
|
|
|
|
continue;
|
|
|
|
|
createBuildStep(txn,
|
|
|
|
|
0, build->id, step, machine ? machine->sshName : "",
|
|
|
|
|
result.stepStatus, result.errorMsg, buildId == build->id ? 0 : buildId);
|
2015-07-21 15:14:17 +02:00
|
|
|
|
}
|
|
|
|
|
|
2020-03-26 15:00:04 +01:00
|
|
|
|
/* Mark all builds that depend on this derivation as failed. */
|
|
|
|
|
for (auto & build : indirect) {
|
|
|
|
|
if (build->finishedInDB) continue;
|
|
|
|
|
printMsg(lvlError, format("marking build %1% as failed") % build->id);
|
2020-04-01 11:54:41 -07:00
|
|
|
|
txn.exec_params0
|
|
|
|
|
("update Builds set finished = 1, buildStatus = $2, startTime = $3, stopTime = $4, isCachedBuild = $5, notificationPendingSince = $4 where id = $1 and finished = 0",
|
|
|
|
|
build->id,
|
|
|
|
|
(int) (build->drvPath != step->drvPath && result.buildStatus() == bsFailed ? bsDepFailed : result.buildStatus()),
|
|
|
|
|
result.startTime,
|
|
|
|
|
result.stopTime,
|
|
|
|
|
result.stepStatus == bsCachedFailure ? 1 : 0);
|
2020-03-26 15:00:04 +01:00
|
|
|
|
nrBuildsDone++;
|
2015-07-21 15:14:17 +02:00
|
|
|
|
}
|
|
|
|
|
|
2020-03-26 15:00:04 +01:00
|
|
|
|
/* Remember failed paths in the database so that they
|
|
|
|
|
won't be built again. */
|
|
|
|
|
if (result.stepStatus != bsCachedFailure && result.canCache)
|
2020-04-01 11:54:41 -07:00
|
|
|
|
for (auto & path : step->drv.outputPaths())
|
|
|
|
|
txn.exec_params0("insert into FailedPaths values ($1)", localStore->printStorePath(path));
|
2020-03-26 15:00:04 +01:00
|
|
|
|
|
2019-08-09 19:11:38 +02:00
|
|
|
|
txn.commit();
|
2015-07-21 15:14:17 +02:00
|
|
|
|
}
|
|
|
|
|
|
2020-03-26 15:00:04 +01:00
|
|
|
|
stepFinished = true;
|
2015-07-21 15:14:17 +02:00
|
|
|
|
|
2020-03-26 15:00:04 +01:00
|
|
|
|
/* Remove the indirect dependencies from ‘builds’. This
|
|
|
|
|
will cause them to be destroyed. */
|
|
|
|
|
for (auto & b : indirect) {
|
|
|
|
|
auto builds_(builds.lock());
|
|
|
|
|
b->finishedInDB = true;
|
|
|
|
|
builds_->erase(b->id);
|
|
|
|
|
dependentIDs.push_back(b->id);
|
|
|
|
|
if (buildOne == b->id) quit = true;
|
|
|
|
|
}
|
|
|
|
|
}
|
2015-07-21 15:14:17 +02:00
|
|
|
|
|
2020-03-26 15:00:04 +01:00
|
|
|
|
/* Send notification about this build and its dependents. */
|
|
|
|
|
{
|
|
|
|
|
pqxx::work txn(conn);
|
|
|
|
|
notifyBuildFinished(txn, buildId, dependentIDs);
|
|
|
|
|
txn.commit();
|
|
|
|
|
}
|
2015-07-21 15:14:17 +02:00
|
|
|
|
}
|
2017-10-19 12:28:06 +02:00
|
|
|
|
|
|
|
|
|
|
2019-12-30 22:49:26 +01:00
|
|
|
|
void State::addRoot(const StorePath & storePath)
|
2017-10-19 12:28:06 +02:00
|
|
|
|
{
|
2019-12-30 22:49:26 +01:00
|
|
|
|
auto root = rootsDir + "/" + std::string(storePath.to_string());
|
2017-10-19 12:28:06 +02:00
|
|
|
|
if (!pathExists(root)) writeFile(root, "");
|
|
|
|
|
}
|