|
| 1 | +#include "nix/store/build/derivation-creation-and-realisation-goal.hh" |
| 2 | +#include "nix/store/build/worker.hh" |
| 3 | +#include "nix/store/derivations.hh" |
| 4 | + |
| 5 | +namespace nix { |
| 6 | + |
| 7 | +DerivationCreationAndRealisationGoal::DerivationCreationAndRealisationGoal( |
| 8 | + ref<const SingleDerivedPath> drvReq, const OutputsSpec & wantedOutputs, Worker & worker, BuildMode buildMode) |
| 9 | + : Goal(worker, init()) |
| 10 | + , drvReq(drvReq) |
| 11 | + , wantedOutputs(wantedOutputs) |
| 12 | + , buildMode(buildMode) |
| 13 | +{ |
| 14 | + commonInit(); |
| 15 | +} |
| 16 | + |
| 17 | +DerivationCreationAndRealisationGoal::DerivationCreationAndRealisationGoal( |
| 18 | + const StorePath & drvPath, |
| 19 | + const OutputsSpec & wantedOutputs, |
| 20 | + const Derivation & drv, |
| 21 | + Worker & worker, |
| 22 | + BuildMode buildMode) |
| 23 | + : Goal(worker, haveDerivation(drvPath, drv)) |
| 24 | + , drvReq(makeConstantStorePathRef(drvPath)) |
| 25 | + , wantedOutputs(wantedOutputs) |
| 26 | + , buildMode(buildMode) |
| 27 | +{ |
| 28 | + commonInit(); |
| 29 | +} |
| 30 | + |
| 31 | +void DerivationCreationAndRealisationGoal::commonInit() |
| 32 | +{ |
| 33 | + name = |
| 34 | + fmt("outer obtaining drv from '%s' and then building outputs %s", |
| 35 | + drvReq->to_string(worker.store), |
| 36 | + std::visit( |
| 37 | + overloaded{ |
| 38 | + [&](const OutputsSpec::All) -> std::string { return "* (all of them)"; }, |
| 39 | + [&](const OutputsSpec::Names os) { return concatStringsSep(", ", quoteStrings(os)); }, |
| 40 | + }, |
| 41 | + wantedOutputs.raw)); |
| 42 | + trace("created outer"); |
| 43 | + |
| 44 | + worker.updateProgress(); |
| 45 | +} |
| 46 | + |
| 47 | +DerivationCreationAndRealisationGoal::~DerivationCreationAndRealisationGoal() {} |
| 48 | + |
| 49 | +static StorePath pathPartOfReq(const SingleDerivedPath & req) |
| 50 | +{ |
| 51 | + return std::visit( |
| 52 | + overloaded{ |
| 53 | + [&](const SingleDerivedPath::Opaque & bo) { return bo.path; }, |
| 54 | + [&](const SingleDerivedPath::Built & bfd) { return pathPartOfReq(*bfd.drvPath); }, |
| 55 | + }, |
| 56 | + req.raw()); |
| 57 | +} |
| 58 | + |
| 59 | +std::string DerivationCreationAndRealisationGoal::key() |
| 60 | +{ |
| 61 | + /* Ensure that derivations get built in order of their name, |
| 62 | + i.e. a derivation named "aardvark" always comes before "baboon". And |
| 63 | + substitution goals and inner derivation goals always happen before |
| 64 | + derivation goals (due to "b$"). */ |
| 65 | + return "c$" + std::string(pathPartOfReq(*drvReq).name()) + "$" + DerivedPath::Built{ |
| 66 | + .drvPath = drvReq, |
| 67 | + .outputs = wantedOutputs, |
| 68 | + }.to_string(worker.store); |
| 69 | +} |
| 70 | + |
| 71 | +void DerivationCreationAndRealisationGoal::timedOut(Error && ex) {} |
| 72 | + |
| 73 | +Goal::Co DerivationCreationAndRealisationGoal::init() |
| 74 | +{ |
| 75 | + trace("outer init"); |
| 76 | + |
| 77 | + /* The first thing to do is to make sure that the derivation |
| 78 | + exists. If it doesn't, it may be created through a |
| 79 | + substitute. */ |
| 80 | + if (auto optDrvPath = [this]() -> std::optional<StorePath> { |
| 81 | + if (buildMode != bmNormal) |
| 82 | + return std::nullopt; |
| 83 | + |
| 84 | + auto drvPath = StorePath::dummy; |
| 85 | + try { |
| 86 | + drvPath = resolveDerivedPath(worker.store, *drvReq); |
| 87 | + } catch (MissingRealisation &) { |
| 88 | + return std::nullopt; |
| 89 | + } |
| 90 | + auto cond = worker.evalStore.isValidPath(drvPath) || worker.store.isValidPath(drvPath); |
| 91 | + return cond ? std::optional{drvPath} : std::nullopt; |
| 92 | + }()) { |
| 93 | + trace( |
| 94 | + fmt("already have drv '%s' for '%s', can go straight to building", |
| 95 | + worker.store.printStorePath(*optDrvPath), |
| 96 | + drvReq->to_string(worker.store))); |
| 97 | + } else { |
| 98 | + trace("need to obtain drv we want to build"); |
| 99 | + Goals waitees{worker.makeGoal(DerivedPath::fromSingle(*drvReq))}; |
| 100 | + co_await await(std::move(waitees)); |
| 101 | + } |
| 102 | + |
| 103 | + trace("outer load and build derivation"); |
| 104 | + |
| 105 | + if (nrFailed != 0) { |
| 106 | + co_return amDone(ecFailed, Error("cannot build missing derivation '%s'", drvReq->to_string(worker.store))); |
| 107 | + } |
| 108 | + |
| 109 | + StorePath drvPath = resolveDerivedPath(worker.store, *drvReq); |
| 110 | + |
| 111 | + /* `drvPath' should already be a root, but let's be on the safe |
| 112 | + side: if the user forgot to make it a root, we wouldn't want |
| 113 | + things being garbage collected while we're busy. */ |
| 114 | + worker.evalStore.addTempRoot(drvPath); |
| 115 | + |
| 116 | + /* Get the derivation. It is probably in the eval store, but it might be inthe main store: |
| 117 | +
|
| 118 | + - Resolved derivation are resolved against main store realisations, and so must be stored there. |
| 119 | +
|
| 120 | + - Dynamic derivations are built, and so are found in the main store. |
| 121 | + */ |
| 122 | + auto drv = [&] { |
| 123 | + for (auto * drvStore : {&worker.evalStore, &worker.store}) |
| 124 | + if (drvStore->isValidPath(drvPath)) |
| 125 | + return drvStore->readDerivation(drvPath); |
| 126 | + assert(false); |
| 127 | + }(); |
| 128 | + |
| 129 | + co_return haveDerivation(std::move(drvPath), std::move(drv)); |
| 130 | +} |
| 131 | + |
| 132 | +Goal::Co DerivationCreationAndRealisationGoal::haveDerivation(StorePath drvPath, Derivation drv) |
| 133 | +{ |
| 134 | + auto resolvedWantedOutputs = std::visit( |
| 135 | + overloaded{ |
| 136 | + [&](const OutputsSpec::Names & names) -> OutputsSpec::Names { return names; }, |
| 137 | + [&](const OutputsSpec::All &) -> OutputsSpec::Names { |
| 138 | + StringSet outputs; |
| 139 | + for (auto & [outputName, _] : drv.outputs) |
| 140 | + outputs.insert(outputName); |
| 141 | + return outputs; |
| 142 | + }, |
| 143 | + }, |
| 144 | + wantedOutputs.raw); |
| 145 | + |
| 146 | + Goals concreteDrvGoals; |
| 147 | + |
| 148 | + /* Build this step! */ |
| 149 | + |
| 150 | + for (auto & output : resolvedWantedOutputs) { |
| 151 | + auto g = upcast_goal(worker.makeDerivationGoal(drvPath, drv, output, buildMode)); |
| 152 | + g->preserveException = true; |
| 153 | + /* We will finish with it ourselves, as if we were the derivational goal. */ |
| 154 | + concreteDrvGoals.insert(std::move(g)); |
| 155 | + } |
| 156 | + |
| 157 | + optDrvPath = std::move(drvPath); |
| 158 | + |
| 159 | + // Copy on purpose |
| 160 | + co_await await(Goals(concreteDrvGoals)); |
| 161 | + |
| 162 | + trace("outer build done"); |
| 163 | + |
| 164 | + auto & g = *concreteDrvGoals.begin(); |
| 165 | + buildResult = g->buildResult; |
| 166 | + for (auto & g2 : concreteDrvGoals) { |
| 167 | + for (auto && [x, y] : g2->buildResult.builtOutputs) |
| 168 | + buildResult.builtOutputs.insert_or_assign(x, y); |
| 169 | + } |
| 170 | + |
| 171 | + co_return amDone(g->exitCode, g->ex); |
| 172 | +} |
| 173 | +} |
0 commit comments