HUUZFPPKGHTXFZMZCO2UGWYNGEED3E2CFHQRFQVVBJGPQVGVY4UAC HLSHCK3CP4RB6VO6N44FBHWMXBJYB6XPNWM5EKCKWU3KISU37N3AC NNOCZ4ROWC64ZKSAE2MPHZ3LGLI34C5TJJFW4MHXN6OELK6VMWOQC UPN2SAMLIQQETE63ZS7IC4ZIAMK4HFYBG3BNCUXXE2VQJAUNF6FQC TM6WKSP3QPVJOZULDTV4ZKBT446IFYU76HJWXOFZ2H2JSKZRVLDAC QJRDO2B4RGTXPSSK2SM6PQ6VEJNOLNMG2EFNMBGNRSEI74KKLVRAC ENXUSMSVOU3AZFMH2ZXR4ZVPV2LRRQYQJ6IFX33YN6IH2ORSNSAAC 24BMQDZAWDQ7VNIA7TIROXSOYLOJBNZ2E4264WHWNJAEN6ZB3UOAC 5AIYUMTBY6TFQTBRP3MJ2PYWUMRF57I77NIVWYE74UMEVQMBWZVQC OCZ4LSGGSCMSLGC3C32D5JUYYHS5CIPOKOAMADEFAFZOFXJ3YY3AC 22LDPAIPRVSZVZXEDCM54GUOH72VQ52EIY47PQV2VELZ3NORC5IQC 2IQRXLWETU2RLXPYKBSZIUULDHS346BAHE2NLOMOWXC6WDAX2BYQC JAUB2FT5SNQWXR24TWTOHVT6UUJ6OQPAFPBZIQAQBHVWHWWFHLYAC GKZN4UV75GV7GEHQGBM2O6UHN6CVSHA7BMASCUQSDIDYVUKKZL7AC NJJ7H64SZOX5EGACDCQAUQ7R6UEWD5IIC35A2MWFOOJV55DJYPHAC N5O7VEEOY2IE27VCOYRBG7YCY3K7JMQEDEMRT4OQ2MUE3NWULHHQC UQQ4IL55WHYMXNSPOXEFBTZAPMP7LQ726THOR7INRCJDSYVOP3ZAC PQFOMNTLFY4HINJFAQYIFTBTSDRST6W2WNCVKE5ITR2IDF4SWWXQC RYTQLATYOZ6ODIKYVJ63TC4OIQBXHSCV3NA2YD4NFP7443GQVSRQC YZAI5GQU3HNMK5MEGF2Y7WS445AN4YKD3HNJQVQP545ODN3F5DLAC KBZHIGLGHGLST5AZZDEJTYBJSQNE2XYNHEN2FN6XMAMY5BJYZR6QC LJILHOJ7QYXNTMKDQS6OU4PVCXPQ27C5KMQWHDBELCYP7FG3ZFYAC HHOMBU7GGRAEXODSDY3WUHQGOSQ35OTGRNBWKKAS2D4YEIZTTNUAC /* Prior to this, the build is not visible togetDependentBuilds(). Now it is, so the build can befailed if a dependency fails. (It can't succeed right awaybecause its top-level is not runnable yet). */
/* Check if the requested step already exists. */
/* Check if the requested step already exists. If not, create anew step. In any case, make the step reachable fromreferringBuild or referringStep. This is done atomically (with‘steps’ locked), to ensure that this step can never becomereachable from a new build after doBuildStep has removed itfrom ‘steps’. */Step::ptr step;bool isNew = false;
auto step = std::make_shared<Step>();step->drvPath = drvPath;
if (!isNew) {assert(step->created);return step;}/* Initialize the step. Note that the step may be visible in‘steps’ before this point, but that doesn't matter becauseit's not runnable yet, and other threads won't make itrunnable while step->created == false. */
}{auto steps_(steps.lock());assert(steps_->find(drvPath) == steps_->end());(*steps_)[drvPath] = step;}if (!hasDeps) newRunnable.insert(step);return step;}void State::destroyStep(Step::ptr step, bool proceed){if (step->destroyed) return;step->destroyed = true;printMsg(lvlDebug, format("destroying build step ‘%1%’") % step->drvPath);nrStepsDone++;{auto steps_(steps.lock());steps_->erase(step->drvPath);
rdeps = step_->rdeps;/* Sanity checks. */for (auto & build_ : step_->builds) {auto build = build_.lock();if (!build) continue;assert(build->drvPath == step->drvPath);assert(build->finishedInDB);}
assert(!step->created);step->created = true;if (step_->deps.empty())newRunnable.insert(step);
for (auto & rdep_ : rdeps) {auto rdep = rdep_.lock();if (!rdep) continue;bool runnable = false;{auto rdep_(rdep->state.lock());assert(has(rdep_->deps, step));rdep_->deps.erase(step);if (rdep_->deps.empty()) runnable = true;}if (proceed) {/* If this rdep has no other dependencies, then we can nowbuild it. */if (runnable)makeRunnable(rdep);} else/* If ‘step’ failed or was cancelled, then delete alldependent steps as well. */destroyStep(rdep, false);}
return step;
/* Remove this step. After this, incoming builds that depend ondrvPath will either see that the output paths exist, or willcreate a new build step for drvPath. The latter is fine - itwon't conflict with this one, because we're removing it. In anycase, the set of dependent builds for ‘step’ can't increaseanymore because ‘step’ is no longer visible to createStep(). */auto steps_(steps.lock());steps_->erase(step->drvPath);
if (result.status == RemoteResult::rrSuccess) {/* Register success in the database for all Build objects thathave this step as the top-level step. Since the queuemonitor thread may be creating new referring Buildsconcurrently, and updating the database may fail, we dothis in a loop, marking all known builds, repeating untilthere are no unmarked builds.*/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. Delete the step from‘steps’. Since we've been holding the ‘steps’ lock,no new referrers can have been added in themeantime or be added afterwards. */if (direct.empty()) {printMsg(lvlDebug, format("finishing build step ‘%1%’") % step->drvPath);nrStepsDone++;steps_->erase(step->drvPath);break;}}/* Update the database. */{pqxx::work txn(*conn);finishBuildStep(txn, result.startTime, result.stopTime, build->id, stepNr, machine->sshName, bssSuccess);for (auto & b : direct)markSucceededBuild(txn, b, res, build != b,result.startTime, result.stopTime);
std::set<Build::ptr> direct;{auto step_(step->state.lock());for (auto & build : step_->builds) {auto build_ = build.lock();if (build_) direct.insert(build_);
/* Remove the direct dependencies from ‘builds’. This willcause them to be destroyed. */for (auto & b : direct) {auto builds_(builds.lock());b->finishedInDB = true;builds_->erase(b->id);}
/* Update the database. */{pqxx::work txn(*conn);
/* Wake up any dependent steps that have no otherdependencies. */{auto step_(step->state.lock());for (auto & rdepWeak : step_->rdeps) {auto rdep = rdepWeak.lock();if (!rdep) continue;
/* Mark all builds of which this derivation is the toplevel as succeeded. */for (auto build2 : direct)markSucceededBuild(txn, build2, res, build != build2,result.startTime, result.stopTime);
} else {
BuildStatus buildStatus =result.status == RemoteResult::rrPermanentFailure ? bsFailed :result.status == RemoteResult::rrTimedOut ? bsTimedOut :bsAborted;BuildStepStatus buildStepStatus =result.status == RemoteResult::rrPermanentFailure ? bssFailed :result.status == RemoteResult::rrTimedOut ? bssTimedOut :bssAborted;
while (true) {
/* For regular failures, we don't care about the errormessage. */if (buildStatus != bsAborted) result.errorMsg = "";
/* 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);
/* Create failed build steps for every build that dependson this. For cached failures, only create a step forbuilds that don't have this step as top-level(otherwise the user won't be able to see what causedthe build to fail). */for (auto build2 : dependents) {if (build == build2) continue;if (cachedFailure && build2->drvPath == step->drvPath) continue;createBuildStep(txn, 0, build2, step, machine->sshName,buildStepStatus, result.errorMsg, build->id);
/* If there are no builds left, delete all referringsteps from ‘steps’. As for the success case, we canbe certain no new referrers can be added. */if (indirect.empty()) {for (auto & s : steps) {printMsg(lvlDebug, format("finishing build step ‘%1%’") % step->drvPath);nrStepsDone++;steps_->erase(s->drvPath);}break;}
if (!cachedFailure)finishBuildStep(txn, result.startTime, result.stopTime, build->id,stepNr, machine->sshName, buildStepStatus, result.errorMsg);
BuildStatus buildStatus =result.status == RemoteResult::rrPermanentFailure ? bsFailed :result.status == RemoteResult::rrTimedOut ? bsTimedOut :bsAborted;BuildStepStatus buildStepStatus =result.status == RemoteResult::rrPermanentFailure ? bssFailed :result.status == RemoteResult::rrTimedOut ? bssTimedOut :bssAborted;/* For regular failures, we don't care about the errormessage. */if (buildStatus != bsAborted) result.errorMsg = "";/* Create failed build steps for every build that dependson this. For cached failures, only create a step forbuilds that don't have this step as top-level(otherwise the user won't be able to see what causedthe build to fail). */for (auto & build2 : indirect) {if (build == build2) continue;if (cachedFailure && build2->drvPath == step->drvPath) continue;createBuildStep(txn, 0, build2, step, machine->sshName,buildStepStatus, result.errorMsg, build->id);}if (!cachedFailure)finishBuildStep(txn, result.startTime, result.stopTime, build->id,stepNr, machine->sshName, buildStepStatus, result.errorMsg);/* Mark all builds that depend on this derivation as failed. */for (auto & build2 : indirect) {printMsg(lvlError, format("marking build %1% as failed") % build2->id);assert(!build->finishedInDB);txn.parameterized("update Builds set finished = 1, busy = 0, buildStatus = $2, startTime = $3, stopTime = $4, isCachedBuild = $5 where id = $1")(build2->id)((int) (build2->drvPath != step->drvPath && buildStatus == bsFailed ? bsDepFailed : buildStatus))(result.startTime)(result.stopTime)(cachedFailure ? 1 : 0).exec();nrBuildsDone++;}/* Remember failed paths in the database so that theywon't be built again. */if (!cachedFailure && result.status == RemoteResult::rrPermanentFailure)for (auto & path : outputPaths(step->drv))txn.parameterized("insert into FailedPaths values ($1)")(path).exec();
/* Mark all builds that depend on this derivation as failed. */for (auto build2 : dependents) {printMsg(lvlError, format("marking build %1% as failed") % build2->id);txn.parameterized("update Builds set finished = 1, busy = 0, buildStatus = $2, startTime = $3, stopTime = $4, isCachedBuild = $5 where id = $1")(build2->id)((int) (build2->drvPath != step->drvPath && buildStatus == bsFailed ? bsDepFailed : buildStatus))(result.startTime)(result.stopTime)(cachedFailure ? 1 : 0).exec();build2->finishedInDB = true; // FIXME: txn might failnrBuildsDone++;
txn.commit();
/* Remember failed paths in the database so that theywon't be built again. */if (!cachedFailure && result.status == RemoteResult::rrPermanentFailure)for (auto & path : outputPaths(step->drv))txn.parameterized("insert into FailedPaths values ($1)")(path).exec();
/* Remove the indirect dependencies from ‘builds’. Thiswill cause them to be destroyed. */for (auto & b : indirect) {auto builds_(builds.lock());b->finishedInDB = true;builds_->erase(b->id);}
/* In case of success, destroy all Build objects of which ‘step’is the top-level derivation. In case of failure, destroy alldependent Build objects. Any Steps not referenced by otherBuilds will be destroyed as well. */for (auto build2 : dependents)if (build2->toplevel == step || result.status != RemoteResult::rrSuccess) {auto builds_(builds.lock());builds_->erase(build2->id);}
/* Remove the step from the graph. In case of success, makedependent build steps runnable if they have no otherdependencies. */destroyStep(step, result.status == RemoteResult::rrSuccess);