aboutsummaryrefslogtreecommitdiff
path: root/src/libstore/build/derivation-goal.cc
diff options
context:
space:
mode:
Diffstat (limited to 'src/libstore/build/derivation-goal.cc')
-rw-r--r--src/libstore/build/derivation-goal.cc416
1 files changed, 242 insertions, 174 deletions
diff --git a/src/libstore/build/derivation-goal.cc b/src/libstore/build/derivation-goal.cc
index 151217b8b..6582497bd 100644
--- a/src/libstore/build/derivation-goal.cc
+++ b/src/libstore/build/derivation-goal.cc
@@ -66,7 +66,7 @@ namespace nix {
DerivationGoal::DerivationGoal(const StorePath & drvPath,
const StringSet & wantedOutputs, Worker & worker, BuildMode buildMode)
- : Goal(worker)
+ : Goal(worker, DerivedPath::Built { .drvPath = drvPath, .outputs = wantedOutputs })
, useDerivation(true)
, drvPath(drvPath)
, wantedOutputs(wantedOutputs)
@@ -85,7 +85,7 @@ DerivationGoal::DerivationGoal(const StorePath & drvPath,
DerivationGoal::DerivationGoal(const StorePath & drvPath, const BasicDerivation & drv,
const StringSet & wantedOutputs, Worker & worker, BuildMode buildMode)
- : Goal(worker)
+ : Goal(worker, DerivedPath::Built { .drvPath = drvPath, .outputs = wantedOutputs })
, useDerivation(false)
, drvPath(drvPath)
, wantedOutputs(wantedOutputs)
@@ -116,7 +116,7 @@ DerivationGoal::~DerivationGoal()
}
-string DerivationGoal::key()
+std::string DerivationGoal::key()
{
/* Ensure that derivations get built in order of their name,
i.e. a derivation named "aardvark" always comes before
@@ -135,7 +135,7 @@ void DerivationGoal::killChild()
void DerivationGoal::timedOut(Error && ex)
{
killChild();
- done(BuildResult::TimedOut, ex);
+ done(BuildResult::TimedOut, {}, ex);
}
@@ -182,7 +182,7 @@ void DerivationGoal::loadDerivation()
trace("loading derivation");
if (nrFailed != 0) {
- done(BuildResult::MiscFailure, Error("cannot build missing derivation '%s'", worker.store.printStorePath(drvPath)));
+ done(BuildResult::MiscFailure, {}, Error("cannot build missing derivation '%s'", worker.store.printStorePath(drvPath)));
return;
}
@@ -204,10 +204,33 @@ void DerivationGoal::haveDerivation()
{
trace("have derivation");
- if (drv->type() == DerivationType::CAFloating)
+ parsedDrv = std::make_unique<ParsedDerivation>(drvPath, *drv);
+
+ if (!drv->type().hasKnownOutputPaths())
settings.requireExperimentalFeature(Xp::CaDerivations);
- retrySubstitution = false;
+ if (!drv->type().isPure()) {
+ settings.requireExperimentalFeature(Xp::ImpureDerivations);
+
+ for (auto & [outputName, output] : drv->outputs) {
+ auto randomPath = StorePath::random(outputPathName(drv->name, outputName));
+ assert(!worker.store.isValidPath(randomPath));
+ initialOutputs.insert({
+ outputName,
+ InitialOutput {
+ .wanted = true,
+ .outputHash = impureOutputHash,
+ .known = InitialOutputStatus {
+ .path = randomPath,
+ .status = PathStatus::Absent
+ }
+ }
+ });
+ }
+
+ gaveUpOnSubstitution();
+ return;
+ }
for (auto & i : drv->outputsAndOptPaths(worker.store))
if (i.second.second)
@@ -215,34 +238,23 @@ void DerivationGoal::haveDerivation()
auto outputHashes = staticOutputHashes(worker.evalStore, *drv);
for (auto & [outputName, outputHash] : outputHashes)
- initialOutputs.insert({
+ initialOutputs.insert({
outputName,
- InitialOutput{
+ InitialOutput {
.wanted = true, // Will be refined later
.outputHash = outputHash
}
- });
+ });
/* Check what outputs paths are not already valid. */
- checkPathValidity();
- bool allValid = true;
- for (auto & [_, status] : initialOutputs) {
- if (!status.wanted) continue;
- if (!status.known || !status.known->isValid()) {
- allValid = false;
- break;
- }
- }
+ auto [allValid, validOutputs] = checkPathValidity();
/* If they are all valid, then we're done. */
if (allValid && buildMode == bmNormal) {
- done(BuildResult::AlreadyValid);
+ done(BuildResult::AlreadyValid, std::move(validOutputs));
return;
}
- parsedDrv = std::make_unique<ParsedDerivation>(drvPath, *drv);
-
-
/* We are first going to try to create the invalid output paths
through substitutes. If that doesn't work, we'll build
them. */
@@ -276,8 +288,10 @@ void DerivationGoal::outputsSubstitutionTried()
{
trace("all outputs substituted (maybe)");
+ assert(drv->type().isPure());
+
if (nrFailed > 0 && nrFailed > nrNoSubstituters + nrIncompleteClosure && !settings.tryFallback) {
- done(BuildResult::TransientFailure,
+ done(BuildResult::TransientFailure, {},
Error("some substitutes for the outputs of derivation '%s' failed (usually happens due to networking issues); try '--fallback' to build derivation from source ",
worker.store.printStorePath(drvPath)));
return;
@@ -301,23 +315,17 @@ void DerivationGoal::outputsSubstitutionTried()
return;
}
- checkPathValidity();
- size_t nrInvalid = 0;
- for (auto & [_, status] : initialOutputs) {
- if (!status.wanted) continue;
- if (!status.known || !status.known->isValid())
- nrInvalid++;
- }
+ auto [allValid, validOutputs] = checkPathValidity();
- if (buildMode == bmNormal && nrInvalid == 0) {
- done(BuildResult::Substituted);
+ if (buildMode == bmNormal && allValid) {
+ done(BuildResult::Substituted, std::move(validOutputs));
return;
}
- if (buildMode == bmRepair && nrInvalid == 0) {
+ if (buildMode == bmRepair && allValid) {
repairClosure();
return;
}
- if (buildMode == bmCheck && nrInvalid > 0)
+ if (buildMode == bmCheck && !allValid)
throw Error("some outputs of '%s' are not valid, so checking is not possible",
worker.store.printStorePath(drvPath));
@@ -325,18 +333,27 @@ void DerivationGoal::outputsSubstitutionTried()
gaveUpOnSubstitution();
}
+
/* At least one of the output paths could not be
produced using a substitute. So we have to build instead. */
void DerivationGoal::gaveUpOnSubstitution()
{
- /* Make sure checkPathValidity() from now on checks all
- outputs. */
- wantedOutputs.clear();
-
/* The inputs must be built before we can build this goal. */
+ inputDrvOutputs.clear();
if (useDerivation)
- for (auto & i : dynamic_cast<Derivation *>(drv.get())->inputDrvs)
+ for (auto & i : dynamic_cast<Derivation *>(drv.get())->inputDrvs) {
+ /* Ensure that pure, non-fixed-output derivations don't
+ depend on impure derivations. */
+ if (drv->type().isPure() && !drv->type().isFixed()) {
+ auto inputDrv = worker.evalStore.readDerivation(i.first);
+ if (!inputDrv.type().isPure())
+ throw Error("pure derivation '%s' depends on impure derivation '%s'",
+ worker.store.printStorePath(drvPath),
+ worker.store.printStorePath(i.first));
+ }
+
addWaitee(worker.makeDerivationGoal(i.first, i.second, buildMode == bmRepair ? bmRepair : bmNormal));
+ }
/* Copy the input sources from the eval store to the build
store. */
@@ -364,6 +381,8 @@ void DerivationGoal::gaveUpOnSubstitution()
void DerivationGoal::repairClosure()
{
+ assert(drv->type().isPure());
+
/* If we're repairing, we now know that our own outputs are valid.
Now check whether the other paths in the outputs closure are
good. If not, then start derivation goals for the derivations
@@ -409,7 +428,7 @@ void DerivationGoal::repairClosure()
}
if (waitees.empty()) {
- done(BuildResult::AlreadyValid);
+ done(BuildResult::AlreadyValid, assertPathValidity());
return;
}
@@ -423,7 +442,7 @@ void DerivationGoal::closureRepaired()
if (nrFailed > 0)
throw Error("some paths in the output closure of derivation '%s' could not be repaired",
worker.store.printStorePath(drvPath));
- done(BuildResult::AlreadyValid);
+ done(BuildResult::AlreadyValid, assertPathValidity());
}
@@ -434,13 +453,14 @@ void DerivationGoal::inputsRealised()
if (nrFailed != 0) {
if (!useDerivation)
throw Error("some dependencies of '%s' are missing", worker.store.printStorePath(drvPath));
- done(BuildResult::DependencyFailed, Error(
+ done(BuildResult::DependencyFailed, {}, Error(
"%s dependencies of derivation '%s' failed to build",
nrFailed, worker.store.printStorePath(drvPath)));
return;
}
- if (retrySubstitution) {
+ if (retrySubstitution && !retriedSubstitution) {
+ retriedSubstitution = true;
haveDerivation();
return;
}
@@ -454,19 +474,40 @@ void DerivationGoal::inputsRealised()
if (useDerivation) {
auto & fullDrv = *dynamic_cast<Derivation *>(drv.get());
- if (settings.isExperimentalFeatureEnabled(Xp::CaDerivations) &&
- ((!fullDrv.inputDrvs.empty() && derivationIsCA(fullDrv.type()))
- || fullDrv.type() == DerivationType::DeferredInputAddressed)) {
+ auto drvType = fullDrv.type();
+ bool resolveDrv = std::visit(overloaded {
+ [&](const DerivationType::InputAddressed & ia) {
+ /* must resolve if deferred. */
+ return ia.deferred;
+ },
+ [&](const DerivationType::ContentAddressed & ca) {
+ return !fullDrv.inputDrvs.empty() && (
+ ca.fixed
+ /* Can optionally resolve if fixed, which is good
+ for avoiding unnecessary rebuilds. */
+ ? settings.isExperimentalFeatureEnabled(Xp::CaDerivations)
+ /* Must resolve if floating and there are any inputs
+ drvs. */
+ : true);
+ },
+ [&](const DerivationType::Impure &) {
+ return true;
+ }
+ }, drvType.raw());
+
+ if (resolveDrv && !fullDrv.inputDrvs.empty()) {
+ settings.requireExperimentalFeature(Xp::CaDerivations);
+
/* We are be able to resolve this derivation based on the
- now-known results of dependencies. If so, we become a stub goal
- aliasing that resolved derivation goal */
- std::optional attempt = fullDrv.tryResolve(worker.store);
+ now-known results of dependencies. If so, we become a
+ stub goal aliasing that resolved derivation goal. */
+ std::optional attempt = fullDrv.tryResolve(worker.store, inputDrvOutputs);
assert(attempt);
Derivation drvResolved { *std::move(attempt) };
auto pathResolved = writeDerivation(worker.store, drvResolved);
- auto msg = fmt("Resolved derivation: '%s' -> '%s'",
+ auto msg = fmt("resolved derivation: '%s' -> '%s'",
worker.store.printStorePath(drvPath),
worker.store.printStorePath(pathResolved));
act = std::make_unique<Activity>(*logger, lvlInfo, actBuildWaiting, msg,
@@ -487,21 +528,13 @@ void DerivationGoal::inputsRealised()
/* Add the relevant output closures of the input derivation
`i' as input paths. Only add the closures of output paths
that are specified as inputs. */
- assert(worker.evalStore.isValidPath(drvPath));
- auto outputs = worker.evalStore.queryPartialDerivationOutputMap(depDrvPath);
- for (auto & j : wantedDepOutputs) {
- if (outputs.count(j) > 0) {
- auto optRealizedInput = outputs.at(j);
- if (!optRealizedInput)
- throw Error(
- "derivation '%s' requires output '%s' from input derivation '%s', which is supposedly realized already, yet we still don't know what path corresponds to that output",
- worker.store.printStorePath(drvPath), j, worker.store.printStorePath(depDrvPath));
- worker.store.computeFSClosure(*optRealizedInput, inputPaths);
- } else
+ for (auto & j : wantedDepOutputs)
+ if (auto outPath = get(inputDrvOutputs, { depDrvPath, j }))
+ worker.store.computeFSClosure(*outPath, inputPaths);
+ else
throw Error(
"derivation '%s' requires non-existent output '%s' from input derivation '%s'",
worker.store.printStorePath(drvPath), j, worker.store.printStorePath(depDrvPath));
- }
}
}
@@ -515,7 +548,7 @@ void DerivationGoal::inputsRealised()
/* Don't repeat fixed-output derivations since they're already
verified by their output hash.*/
- nrRounds = derivationIsFixed(derivationType) ? 1 : settings.buildRepeat + 1;
+ nrRounds = derivationType.isFixed() ? 1 : settings.buildRepeat + 1;
/* Okay, try to build. Note that here we don't wait for a build
slot to become available, since we don't need one if there is a
@@ -523,10 +556,11 @@ void DerivationGoal::inputsRealised()
state = &DerivationGoal::tryToBuild;
worker.wakeUp(shared_from_this());
- result = BuildResult();
+ buildResult = BuildResult { .path = buildResult.path };
}
-void DerivationGoal::started() {
+void DerivationGoal::started()
+{
auto msg = fmt(
buildMode == bmRepair ? "repairing outputs of '%s'" :
buildMode == bmCheck ? "checking outputs of '%s'" :
@@ -588,19 +622,12 @@ void DerivationGoal::tryToBuild()
omitted, but that would be less efficient.) Note that since we
now hold the locks on the output paths, no other process can
build this derivation, so no further checks are necessary. */
- checkPathValidity();
- bool allValid = true;
- for (auto & [_, status] : initialOutputs) {
- if (!status.wanted) continue;
- if (!status.known || !status.known->isValid()) {
- allValid = false;
- break;
- }
- }
+ auto [allValid, validOutputs] = checkPathValidity();
+
if (buildMode != bmCheck && allValid) {
debug("skipping build of derivation '%s', someone beat us to it", worker.store.printStorePath(drvPath));
outputLocks.setDeletion(true);
- done(BuildResult::AlreadyValid);
+ done(BuildResult::AlreadyValid, std::move(validOutputs));
return;
}
@@ -626,7 +653,7 @@ void DerivationGoal::tryToBuild()
/* Yes, it has started doing so. Wait until we get
EOF from the hook. */
actLock.reset();
- result.startTime = time(0); // inexact
+ buildResult.startTime = time(0); // inexact
state = &DerivationGoal::buildDone;
started();
return;
@@ -830,8 +857,8 @@ void DerivationGoal::buildDone()
debug("builder process for '%s' finished", worker.store.printStorePath(drvPath));
- result.timesBuilt++;
- result.stopTime = time(0);
+ buildResult.timesBuilt++;
+ buildResult.stopTime = time(0);
/* So the child is gone now. */
worker.childTerminated(this);
@@ -876,11 +903,11 @@ void DerivationGoal::buildDone()
/* Compute the FS closure of the outputs and register them as
being valid. */
- registerOutputs();
+ auto builtOutputs = registerOutputs();
StorePathSet outputPaths;
- for (auto & [_, path] : finalOutputs)
- outputPaths.insert(path);
+ for (auto & [_, output] : buildResult.builtOutputs)
+ outputPaths.insert(output.outPath);
runPostBuildHook(
worker.store,
*logger,
@@ -890,7 +917,7 @@ void DerivationGoal::buildDone()
if (buildMode == bmCheck) {
cleanupPostOutputsRegisteredModeCheck();
- done(BuildResult::Built);
+ done(BuildResult::Built, std::move(builtOutputs));
return;
}
@@ -911,6 +938,8 @@ void DerivationGoal::buildDone()
outputLocks.setDeletion(true);
outputLocks.unlock();
+ done(BuildResult::Built, std::move(builtOutputs));
+
} catch (BuildError & e) {
outputLocks.unlock();
@@ -926,71 +955,66 @@ void DerivationGoal::buildDone()
st =
dynamic_cast<NotDeterministic*>(&e) ? BuildResult::NotDeterministic :
statusOk(status) ? BuildResult::OutputRejected :
- derivationIsImpure(derivationType) || diskFull ? BuildResult::TransientFailure :
+ !derivationType.isSandboxed() || diskFull ? BuildResult::TransientFailure :
BuildResult::PermanentFailure;
}
- done(st, e);
+ done(st, {}, e);
return;
}
-
- done(BuildResult::Built);
}
-void DerivationGoal::resolvedFinished() {
+void DerivationGoal::resolvedFinished()
+{
+ trace("resolved derivation finished");
+
assert(resolvedDrvGoal);
auto resolvedDrv = *resolvedDrvGoal->drv;
+ auto & resolvedResult = resolvedDrvGoal->buildResult;
- auto resolvedHashes = staticOutputHashes(worker.store, resolvedDrv);
+ DrvOutputs builtOutputs;
- StorePathSet outputPaths;
+ if (resolvedResult.success()) {
+ auto resolvedHashes = staticOutputHashes(worker.store, resolvedDrv);
- // `wantedOutputs` might be empty, which means “all the outputs”
- auto realWantedOutputs = wantedOutputs;
- if (realWantedOutputs.empty())
- realWantedOutputs = resolvedDrv.outputNames();
+ StorePathSet outputPaths;
- for (auto & wantedOutput : realWantedOutputs) {
- assert(initialOutputs.count(wantedOutput) != 0);
- assert(resolvedHashes.count(wantedOutput) != 0);
- auto realisation = worker.store.queryRealisation(
- DrvOutput{resolvedHashes.at(wantedOutput), wantedOutput}
- );
- // We've just built it, but maybe the build failed, in which case the
- // realisation won't be there
- if (realisation) {
- auto newRealisation = *realisation;
- newRealisation.id = DrvOutput{initialOutputs.at(wantedOutput).outputHash, wantedOutput};
- newRealisation.signatures.clear();
- newRealisation.dependentRealisations = drvOutputReferences(worker.store, *drv, realisation->outPath);
- signRealisation(newRealisation);
- worker.store.registerDrvOutput(newRealisation);
- outputPaths.insert(realisation->outPath);
- } else {
- // If we don't have a realisation, then it must mean that something
- // failed when building the resolved drv
- assert(!result.success());
+ // `wantedOutputs` might be empty, which means “all the outputs”
+ auto realWantedOutputs = wantedOutputs;
+ if (realWantedOutputs.empty())
+ realWantedOutputs = resolvedDrv.outputNames();
+
+ for (auto & wantedOutput : realWantedOutputs) {
+ assert(initialOutputs.count(wantedOutput) != 0);
+ assert(resolvedHashes.count(wantedOutput) != 0);
+ auto realisation = resolvedResult.builtOutputs.at(
+ DrvOutput { resolvedHashes.at(wantedOutput), wantedOutput });
+ if (drv->type().isPure()) {
+ auto newRealisation = realisation;
+ newRealisation.id = DrvOutput { initialOutputs.at(wantedOutput).outputHash, wantedOutput };
+ newRealisation.signatures.clear();
+ if (!drv->type().isFixed())
+ newRealisation.dependentRealisations = drvOutputReferences(worker.store, *drv, realisation.outPath);
+ signRealisation(newRealisation);
+ worker.store.registerDrvOutput(newRealisation);
+ }
+ outputPaths.insert(realisation.outPath);
+ builtOutputs.emplace(realisation.id, realisation);
}
+
+ runPostBuildHook(
+ worker.store,
+ *logger,
+ drvPath,
+ outputPaths
+ );
}
- runPostBuildHook(
- worker.store,
- *logger,
- drvPath,
- outputPaths
- );
-
- auto status = [&]() {
- auto resolvedResult = resolvedDrvGoal->getResult();
- switch (resolvedResult.status) {
- case BuildResult::AlreadyValid:
- return BuildResult::ResolvesToAlreadyValid;
- default:
- return resolvedResult.status;
- }
- }();
+ auto status = resolvedResult.status;
+ if (status == BuildResult::AlreadyValid)
+ status = BuildResult::ResolvesToAlreadyValid;
- done(status);
+ done(status, std::move(builtOutputs));
}
HookReply DerivationGoal::tryBuildHook()
@@ -1013,7 +1037,7 @@ HookReply DerivationGoal::tryBuildHook()
/* Read the first line of input, which should be a word indicating
whether the hook wishes to perform the build. */
- string reply;
+ std::string reply;
while (true) {
auto s = [&]() {
try {
@@ -1025,8 +1049,8 @@ HookReply DerivationGoal::tryBuildHook()
}();
if (handleJSONLogMessage(s, worker.act, worker.hook->activities, true))
;
- else if (string(s, 0, 2) == "# ") {
- reply = string(s, 2);
+ else if (s.substr(0, 2) == "# ") {
+ reply = s.substr(2);
break;
}
else {
@@ -1091,7 +1115,7 @@ HookReply DerivationGoal::tryBuildHook()
/* Create the log file and pipe. */
Path logFile = openLogFile();
- set<int> fds;
+ std::set<int> fds;
fds.insert(hook->fromHook.readSide.get());
fds.insert(hook->builderOut.readSide.get());
worker.childStarted(shared_from_this(), fds, false, false);
@@ -1100,7 +1124,7 @@ HookReply DerivationGoal::tryBuildHook()
}
-void DerivationGoal::registerOutputs()
+DrvOutputs DerivationGoal::registerOutputs()
{
/* When using a build hook, the build hook can register the output
as valid (by doing `nix-store --import'). If so we don't have
@@ -1109,21 +1133,7 @@ void DerivationGoal::registerOutputs()
We can only early return when the outputs are known a priori. For
floating content-addressed derivations this isn't the case.
*/
- for (auto & [outputName, optOutputPath] : worker.store.queryPartialDerivationOutputMap(drvPath)) {
- if (!wantOutput(outputName, wantedOutputs))
- continue;
- if (!optOutputPath)
- throw BuildError(
- "output '%s' from derivation '%s' does not have a known output path",
- outputName, worker.store.printStorePath(drvPath));
- auto & outputPath = *optOutputPath;
- if (!worker.store.isValidPath(outputPath))
- throw BuildError(
- "output '%s' from derivation '%s' is supposed to be at '%s' but that path is not valid",
- outputName, worker.store.printStorePath(drvPath), worker.store.printStorePath(outputPath));
-
- finalOutputs.insert_or_assign(outputName, outputPath);
- }
+ return assertPathValidity();
}
Path DerivationGoal::openLogFile()
@@ -1140,10 +1150,10 @@ Path DerivationGoal::openLogFile()
logDir = localStore->logDir;
else
logDir = settings.nixLogDir;
- Path dir = fmt("%s/%s/%s/", logDir, LocalFSStore::drvsLogDir, string(baseName, 0, 2));
+ Path dir = fmt("%s/%s/%s/", logDir, LocalFSStore::drvsLogDir, baseName.substr(0, 2));
createDirs(dir);
- Path logFileName = fmt("%s/%s%s", dir, string(baseName, 2),
+ Path logFileName = fmt("%s/%s%s", dir, baseName.substr(2),
settings.compressLog ? ".bz2" : "");
fdLogFile = open(logFileName.c_str(), O_CREAT | O_WRONLY | O_TRUNC | O_CLOEXEC, 0666);
@@ -1175,16 +1185,17 @@ bool DerivationGoal::isReadDesc(int fd)
return fd == hook->builderOut.readSide.get();
}
-
-void DerivationGoal::handleChildOutput(int fd, const string & data)
+void DerivationGoal::handleChildOutput(int fd, std::string_view data)
{
- if (isReadDesc(fd))
+ // local & `ssh://`-builds are dealt with here.
+ auto isWrittenToLog = isReadDesc(fd);
+ if (isWrittenToLog)
{
logSize += data.size();
if (settings.maxLogSize && logSize > settings.maxLogSize) {
killChild();
done(
- BuildResult::LogLimitExceeded,
+ BuildResult::LogLimitExceeded, {},
Error("%s killed after writing more than %d bytes of log output",
getName(), settings.maxLogSize));
return;
@@ -1207,7 +1218,16 @@ void DerivationGoal::handleChildOutput(int fd, const string & data)
if (hook && fd == hook->fromHook.readSide.get()) {
for (auto c : data)
if (c == '\n') {
- handleJSONLogMessage(currentHookLine, worker.act, hook->activities, true);
+ auto json = parseJSONMessage(currentHookLine);
+ if (json) {
+ auto s = handleJSONLogMessage(*json, worker.act, hook->activities, true);
+ // ensure that logs from a builder using `ssh-ng://` as protocol
+ // are also available to `nix log`.
+ if (s && !isWrittenToLog && logSink && (*json)["type"] == resBuildLogLine) {
+ auto f = (*json)["fields"];
+ (*logSink)((f.size() > 0 ? f.at(0).get<std::string>() : "") + "\n");
+ }
+ }
currentHookLine.clear();
} else
currentHookLine += c;
@@ -1241,7 +1261,8 @@ void DerivationGoal::flushLine()
std::map<std::string, std::optional<StorePath>> DerivationGoal::queryPartialDerivationOutputMap()
{
- if (!useDerivation || drv->type() != DerivationType::CAFloating) {
+ assert(drv->type().isPure());
+ if (!useDerivation || drv->type().hasKnownOutputPaths()) {
std::map<std::string, std::optional<StorePath>> res;
for (auto & [name, output] : drv->outputs)
res.insert_or_assign(name, output.path(worker.store, drv->name, name));
@@ -1253,7 +1274,8 @@ std::map<std::string, std::optional<StorePath>> DerivationGoal::queryPartialDeri
OutputPathMap DerivationGoal::queryDerivationOutputMap()
{
- if (!useDerivation || drv->type() != DerivationType::CAFloating) {
+ assert(drv->type().isPure());
+ if (!useDerivation || drv->type().hasKnownOutputPaths()) {
OutputPathMap res;
for (auto & [name, output] : drv->outputsAndOptPaths(worker.store))
res.insert_or_assign(name, *output.second);
@@ -1264,10 +1286,14 @@ OutputPathMap DerivationGoal::queryDerivationOutputMap()
}
-void DerivationGoal::checkPathValidity()
+std::pair<bool, DrvOutputs> DerivationGoal::checkPathValidity()
{
+ if (!drv->type().isPure()) return { false, {} };
+
bool checkHash = buildMode == bmRepair;
auto wantedOutputsLeft = wantedOutputs;
+ DrvOutputs validOutputs;
+
for (auto & i : queryPartialDerivationOutputMap()) {
InitialOutput & info = initialOutputs.at(i.first);
info.wanted = wantOutput(i.first, wantedOutputs);
@@ -1284,27 +1310,30 @@ void DerivationGoal::checkPathValidity()
: PathStatus::Corrupt,
};
}
+ auto drvOutput = DrvOutput{initialOutputs.at(i.first).outputHash, i.first};
if (settings.isExperimentalFeatureEnabled(Xp::CaDerivations)) {
- auto drvOutput = DrvOutput{initialOutputs.at(i.first).outputHash, i.first};
if (auto real = worker.store.queryRealisation(drvOutput)) {
info.known = {
.path = real->outPath,
.status = PathStatus::Valid,
};
- } else if (info.known && info.known->status == PathStatus::Valid) {
- // We know the output because it' a static output of the
+ } else if (info.known && info.known->isValid()) {
+ // We know the output because it's a static output of the
// derivation, and the output path is valid, but we don't have
// its realisation stored (probably because it has been built
- // without the `ca-derivations` experimental flag)
+ // without the `ca-derivations` experimental flag).
worker.store.registerDrvOutput(
- Realisation{
+ Realisation {
drvOutput,
info.known->path,
}
);
}
}
+ if (info.wanted && info.known && info.known->isValid())
+ validOutputs.emplace(drvOutput, Realisation { drvOutput, info.known->path });
}
+
// If we requested all the outputs via the empty set, we are always fine.
// If we requested specific elements, the loop above removes all the valid
// ones, so any that are left must be invalid.
@@ -1312,24 +1341,49 @@ void DerivationGoal::checkPathValidity()
throw Error("derivation '%s' does not have wanted outputs %s",
worker.store.printStorePath(drvPath),
concatStringsSep(", ", quoteStrings(wantedOutputsLeft)));
+
+ bool allValid = true;
+ for (auto & [_, status] : initialOutputs) {
+ if (!status.wanted) continue;
+ if (!status.known || !status.known->isValid()) {
+ allValid = false;
+ break;
+ }
+ }
+
+ return { allValid, validOutputs };
+}
+
+
+DrvOutputs DerivationGoal::assertPathValidity()
+{
+ auto [allValid, validOutputs] = checkPathValidity();
+ if (!allValid)
+ throw Error("some outputs are unexpectedly invalid");
+ return validOutputs;
}
-void DerivationGoal::done(BuildResult::Status status, std::optional<Error> ex)
+void DerivationGoal::done(
+ BuildResult::Status status,
+ DrvOutputs builtOutputs,
+ std::optional<Error> ex)
{
- result.status = status;
+ buildResult.status = status;
if (ex)
- result.errorMsg = ex->what();
- amDone(result.success() ? ecSuccess : ecFailed, ex);
- if (result.status == BuildResult::TimedOut)
+ // FIXME: strip: "error: "
+ buildResult.errorMsg = ex->what();
+ if (buildResult.status == BuildResult::TimedOut)
worker.timedOut = true;
- if (result.status == BuildResult::PermanentFailure)
+ if (buildResult.status == BuildResult::PermanentFailure)
worker.permanentFailure = true;
mcExpectedBuilds.reset();
mcRunningBuilds.reset();
- if (result.success()) {
+ if (buildResult.success()) {
+ assert(!builtOutputs.empty());
+ buildResult.builtOutputs = std::move(builtOutputs);
if (status == BuildResult::Built)
worker.doneBuilds++;
} else {
@@ -1343,9 +1397,23 @@ void DerivationGoal::done(BuildResult::Status status, std::optional<Error> ex)
if (traceBuiltOutputsFile != "") {
std::fstream fs;
fs.open(traceBuiltOutputsFile, std::fstream::out);
- fs << worker.store.printStorePath(drvPath) << "\t" << result.toString() << std::endl;
+ fs << worker.store.printStorePath(drvPath) << "\t" << buildResult.toString() << std::endl;
}
+
+ amDone(buildResult.success() ? ecSuccess : ecFailed, ex);
}
+void DerivationGoal::waiteeDone(GoalPtr waitee, ExitCode result)
+{
+ Goal::waiteeDone(waitee, result);
+
+ if (waitee->buildResult.success())
+ if (auto bfd = std::get_if<DerivedPath::Built>(&waitee->buildResult.path))
+ for (auto & [output, realisation] : waitee->buildResult.builtOutputs)
+ inputDrvOutputs.insert_or_assign(
+ { bfd->drvPath, output.outputName },
+ realisation.outPath);
+}
+
}