forked from lix-project/lix
Merge pull request #6221 from NixOS/build-paths-with-results
Add Store::buildPathsWithResults()
This commit is contained in:
commit
1c1a7074da
|
@ -12,6 +12,7 @@
|
||||||
#include "eval-cache.hh"
|
#include "eval-cache.hh"
|
||||||
#include "url.hh"
|
#include "url.hh"
|
||||||
#include "registry.hh"
|
#include "registry.hh"
|
||||||
|
#include "build-result.hh"
|
||||||
|
|
||||||
#include <regex>
|
#include <regex>
|
||||||
#include <queue>
|
#include <queue>
|
||||||
|
@ -769,8 +770,7 @@ BuiltPaths getBuiltPaths(ref<Store> evalStore, ref<Store> store, const DerivedPa
|
||||||
throw Error(
|
throw Error(
|
||||||
"the derivation '%s' doesn't have an output named '%s'",
|
"the derivation '%s' doesn't have an output named '%s'",
|
||||||
store->printStorePath(bfd.drvPath), output);
|
store->printStorePath(bfd.drvPath), output);
|
||||||
if (settings.isExperimentalFeatureEnabled(
|
if (settings.isExperimentalFeatureEnabled(Xp::CaDerivations)) {
|
||||||
Xp::CaDerivations)) {
|
|
||||||
auto outputId =
|
auto outputId =
|
||||||
DrvOutput{outputHashes.at(output), output};
|
DrvOutput{outputHashes.at(output), output};
|
||||||
auto realisation =
|
auto realisation =
|
||||||
|
@ -816,12 +816,33 @@ BuiltPaths Installable::build(
|
||||||
pathsToBuild.insert(pathsToBuild.end(), b.begin(), b.end());
|
pathsToBuild.insert(pathsToBuild.end(), b.begin(), b.end());
|
||||||
}
|
}
|
||||||
|
|
||||||
if (mode == Realise::Nothing || mode == Realise::Derivation)
|
switch (mode) {
|
||||||
|
case Realise::Nothing:
|
||||||
|
case Realise::Derivation:
|
||||||
printMissing(store, pathsToBuild, lvlError);
|
printMissing(store, pathsToBuild, lvlError);
|
||||||
else if (mode == Realise::Outputs)
|
|
||||||
store->buildPaths(pathsToBuild, bMode, evalStore);
|
|
||||||
|
|
||||||
return getBuiltPaths(evalStore, store, pathsToBuild);
|
return getBuiltPaths(evalStore, store, pathsToBuild);
|
||||||
|
case Realise::Outputs: {
|
||||||
|
BuiltPaths res;
|
||||||
|
for (auto & buildResult : store->buildPathsWithResults(pathsToBuild, bMode, evalStore)) {
|
||||||
|
if (!buildResult.success())
|
||||||
|
buildResult.rethrow();
|
||||||
|
std::visit(overloaded {
|
||||||
|
[&](const DerivedPath::Built & bfd) {
|
||||||
|
std::map<std::string, StorePath> outputs;
|
||||||
|
for (auto & path : buildResult.builtOutputs)
|
||||||
|
outputs.emplace(path.first.outputName, path.second.outPath);
|
||||||
|
res.push_back(BuiltPath::Built { bfd.drvPath, outputs });
|
||||||
|
},
|
||||||
|
[&](const DerivedPath::Opaque & bo) {
|
||||||
|
res.push_back(BuiltPath::Opaque { bo.path });
|
||||||
|
},
|
||||||
|
}, buildResult.path.raw());
|
||||||
|
}
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
assert(false);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
BuiltPaths Installable::toBuiltPaths(
|
BuiltPaths Installable::toBuiltPaths(
|
||||||
|
|
|
@ -28,6 +28,7 @@ struct BuildResult
|
||||||
LogLimitExceeded,
|
LogLimitExceeded,
|
||||||
NotDeterministic,
|
NotDeterministic,
|
||||||
ResolvesToAlreadyValid,
|
ResolvesToAlreadyValid,
|
||||||
|
NoSubstituters,
|
||||||
} status = MiscFailure;
|
} status = MiscFailure;
|
||||||
std::string errorMsg;
|
std::string errorMsg;
|
||||||
|
|
||||||
|
@ -63,15 +64,26 @@ struct BuildResult
|
||||||
non-determinism.) */
|
non-determinism.) */
|
||||||
bool isNonDeterministic = false;
|
bool isNonDeterministic = false;
|
||||||
|
|
||||||
|
/* The derivation we built or the store path we substituted. */
|
||||||
|
DerivedPath path;
|
||||||
|
|
||||||
|
/* For derivations, the derivation path and the wanted outputs. */
|
||||||
|
std::optional<StorePath> drvPath;
|
||||||
DrvOutputs builtOutputs;
|
DrvOutputs builtOutputs;
|
||||||
|
|
||||||
/* The start/stop times of the build (or one of the rounds, if it
|
/* The start/stop times of the build (or one of the rounds, if it
|
||||||
was repeated). */
|
was repeated). */
|
||||||
time_t startTime = 0, stopTime = 0;
|
time_t startTime = 0, stopTime = 0;
|
||||||
|
|
||||||
bool success() {
|
bool success()
|
||||||
|
{
|
||||||
return status == Built || status == Substituted || status == AlreadyValid || status == ResolvesToAlreadyValid;
|
return status == Built || status == Substituted || status == AlreadyValid || status == ResolvesToAlreadyValid;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void rethrow()
|
||||||
|
{
|
||||||
|
throw Error("%s", errorMsg);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -66,7 +66,7 @@ namespace nix {
|
||||||
|
|
||||||
DerivationGoal::DerivationGoal(const StorePath & drvPath,
|
DerivationGoal::DerivationGoal(const StorePath & drvPath,
|
||||||
const StringSet & wantedOutputs, Worker & worker, BuildMode buildMode)
|
const StringSet & wantedOutputs, Worker & worker, BuildMode buildMode)
|
||||||
: Goal(worker)
|
: Goal(worker, DerivedPath::Built { .drvPath = drvPath, .outputs = wantedOutputs })
|
||||||
, useDerivation(true)
|
, useDerivation(true)
|
||||||
, drvPath(drvPath)
|
, drvPath(drvPath)
|
||||||
, wantedOutputs(wantedOutputs)
|
, wantedOutputs(wantedOutputs)
|
||||||
|
@ -85,7 +85,7 @@ DerivationGoal::DerivationGoal(const StorePath & drvPath,
|
||||||
|
|
||||||
DerivationGoal::DerivationGoal(const StorePath & drvPath, const BasicDerivation & drv,
|
DerivationGoal::DerivationGoal(const StorePath & drvPath, const BasicDerivation & drv,
|
||||||
const StringSet & wantedOutputs, Worker & worker, BuildMode buildMode)
|
const StringSet & wantedOutputs, Worker & worker, BuildMode buildMode)
|
||||||
: Goal(worker)
|
: Goal(worker, DerivedPath::Built { .drvPath = drvPath, .outputs = wantedOutputs })
|
||||||
, useDerivation(false)
|
, useDerivation(false)
|
||||||
, drvPath(drvPath)
|
, drvPath(drvPath)
|
||||||
, wantedOutputs(wantedOutputs)
|
, wantedOutputs(wantedOutputs)
|
||||||
|
@ -135,7 +135,7 @@ void DerivationGoal::killChild()
|
||||||
void DerivationGoal::timedOut(Error && ex)
|
void DerivationGoal::timedOut(Error && ex)
|
||||||
{
|
{
|
||||||
killChild();
|
killChild();
|
||||||
done(BuildResult::TimedOut, ex);
|
done(BuildResult::TimedOut, {}, ex);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -182,7 +182,7 @@ void DerivationGoal::loadDerivation()
|
||||||
trace("loading derivation");
|
trace("loading derivation");
|
||||||
|
|
||||||
if (nrFailed != 0) {
|
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;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -224,19 +224,11 @@ void DerivationGoal::haveDerivation()
|
||||||
});
|
});
|
||||||
|
|
||||||
/* Check what outputs paths are not already valid. */
|
/* Check what outputs paths are not already valid. */
|
||||||
checkPathValidity();
|
auto [allValid, validOutputs] = checkPathValidity();
|
||||||
bool allValid = true;
|
|
||||||
for (auto & [_, status] : initialOutputs) {
|
|
||||||
if (!status.wanted) continue;
|
|
||||||
if (!status.known || !status.known->isValid()) {
|
|
||||||
allValid = false;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* If they are all valid, then we're done. */
|
/* If they are all valid, then we're done. */
|
||||||
if (allValid && buildMode == bmNormal) {
|
if (allValid && buildMode == bmNormal) {
|
||||||
done(BuildResult::AlreadyValid);
|
done(BuildResult::AlreadyValid, std::move(validOutputs));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -277,7 +269,7 @@ void DerivationGoal::outputsSubstitutionTried()
|
||||||
trace("all outputs substituted (maybe)");
|
trace("all outputs substituted (maybe)");
|
||||||
|
|
||||||
if (nrFailed > 0 && nrFailed > nrNoSubstituters + nrIncompleteClosure && !settings.tryFallback) {
|
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 ",
|
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)));
|
worker.store.printStorePath(drvPath)));
|
||||||
return;
|
return;
|
||||||
|
@ -301,23 +293,17 @@ void DerivationGoal::outputsSubstitutionTried()
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
checkPathValidity();
|
auto [allValid, validOutputs] = checkPathValidity();
|
||||||
size_t nrInvalid = 0;
|
|
||||||
for (auto & [_, status] : initialOutputs) {
|
|
||||||
if (!status.wanted) continue;
|
|
||||||
if (!status.known || !status.known->isValid())
|
|
||||||
nrInvalid++;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (buildMode == bmNormal && nrInvalid == 0) {
|
if (buildMode == bmNormal && allValid) {
|
||||||
done(BuildResult::Substituted);
|
done(BuildResult::Substituted, std::move(validOutputs));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (buildMode == bmRepair && nrInvalid == 0) {
|
if (buildMode == bmRepair && allValid) {
|
||||||
repairClosure();
|
repairClosure();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (buildMode == bmCheck && nrInvalid > 0)
|
if (buildMode == bmCheck && !allValid)
|
||||||
throw Error("some outputs of '%s' are not valid, so checking is not possible",
|
throw Error("some outputs of '%s' are not valid, so checking is not possible",
|
||||||
worker.store.printStorePath(drvPath));
|
worker.store.printStorePath(drvPath));
|
||||||
|
|
||||||
|
@ -409,7 +395,7 @@ void DerivationGoal::repairClosure()
|
||||||
}
|
}
|
||||||
|
|
||||||
if (waitees.empty()) {
|
if (waitees.empty()) {
|
||||||
done(BuildResult::AlreadyValid);
|
done(BuildResult::AlreadyValid, assertPathValidity());
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -423,7 +409,7 @@ void DerivationGoal::closureRepaired()
|
||||||
if (nrFailed > 0)
|
if (nrFailed > 0)
|
||||||
throw Error("some paths in the output closure of derivation '%s' could not be repaired",
|
throw Error("some paths in the output closure of derivation '%s' could not be repaired",
|
||||||
worker.store.printStorePath(drvPath));
|
worker.store.printStorePath(drvPath));
|
||||||
done(BuildResult::AlreadyValid);
|
done(BuildResult::AlreadyValid, assertPathValidity());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -434,7 +420,7 @@ void DerivationGoal::inputsRealised()
|
||||||
if (nrFailed != 0) {
|
if (nrFailed != 0) {
|
||||||
if (!useDerivation)
|
if (!useDerivation)
|
||||||
throw Error("some dependencies of '%s' are missing", worker.store.printStorePath(drvPath));
|
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",
|
"%s dependencies of derivation '%s' failed to build",
|
||||||
nrFailed, worker.store.printStorePath(drvPath)));
|
nrFailed, worker.store.printStorePath(drvPath)));
|
||||||
return;
|
return;
|
||||||
|
@ -523,10 +509,11 @@ void DerivationGoal::inputsRealised()
|
||||||
state = &DerivationGoal::tryToBuild;
|
state = &DerivationGoal::tryToBuild;
|
||||||
worker.wakeUp(shared_from_this());
|
worker.wakeUp(shared_from_this());
|
||||||
|
|
||||||
result = BuildResult();
|
buildResult = BuildResult { .path = buildResult.path };
|
||||||
}
|
}
|
||||||
|
|
||||||
void DerivationGoal::started() {
|
void DerivationGoal::started()
|
||||||
|
{
|
||||||
auto msg = fmt(
|
auto msg = fmt(
|
||||||
buildMode == bmRepair ? "repairing outputs of '%s'" :
|
buildMode == bmRepair ? "repairing outputs of '%s'" :
|
||||||
buildMode == bmCheck ? "checking outputs of '%s'" :
|
buildMode == bmCheck ? "checking outputs of '%s'" :
|
||||||
|
@ -588,19 +575,12 @@ void DerivationGoal::tryToBuild()
|
||||||
omitted, but that would be less efficient.) Note that since we
|
omitted, but that would be less efficient.) Note that since we
|
||||||
now hold the locks on the output paths, no other process can
|
now hold the locks on the output paths, no other process can
|
||||||
build this derivation, so no further checks are necessary. */
|
build this derivation, so no further checks are necessary. */
|
||||||
checkPathValidity();
|
auto [allValid, validOutputs] = checkPathValidity();
|
||||||
bool allValid = true;
|
|
||||||
for (auto & [_, status] : initialOutputs) {
|
|
||||||
if (!status.wanted) continue;
|
|
||||||
if (!status.known || !status.known->isValid()) {
|
|
||||||
allValid = false;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (buildMode != bmCheck && allValid) {
|
if (buildMode != bmCheck && allValid) {
|
||||||
debug("skipping build of derivation '%s', someone beat us to it", worker.store.printStorePath(drvPath));
|
debug("skipping build of derivation '%s', someone beat us to it", worker.store.printStorePath(drvPath));
|
||||||
outputLocks.setDeletion(true);
|
outputLocks.setDeletion(true);
|
||||||
done(BuildResult::AlreadyValid);
|
done(BuildResult::AlreadyValid, std::move(validOutputs));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -626,7 +606,7 @@ void DerivationGoal::tryToBuild()
|
||||||
/* Yes, it has started doing so. Wait until we get
|
/* Yes, it has started doing so. Wait until we get
|
||||||
EOF from the hook. */
|
EOF from the hook. */
|
||||||
actLock.reset();
|
actLock.reset();
|
||||||
result.startTime = time(0); // inexact
|
buildResult.startTime = time(0); // inexact
|
||||||
state = &DerivationGoal::buildDone;
|
state = &DerivationGoal::buildDone;
|
||||||
started();
|
started();
|
||||||
return;
|
return;
|
||||||
|
@ -830,8 +810,8 @@ void DerivationGoal::buildDone()
|
||||||
|
|
||||||
debug("builder process for '%s' finished", worker.store.printStorePath(drvPath));
|
debug("builder process for '%s' finished", worker.store.printStorePath(drvPath));
|
||||||
|
|
||||||
result.timesBuilt++;
|
buildResult.timesBuilt++;
|
||||||
result.stopTime = time(0);
|
buildResult.stopTime = time(0);
|
||||||
|
|
||||||
/* So the child is gone now. */
|
/* So the child is gone now. */
|
||||||
worker.childTerminated(this);
|
worker.childTerminated(this);
|
||||||
|
@ -876,11 +856,11 @@ void DerivationGoal::buildDone()
|
||||||
|
|
||||||
/* Compute the FS closure of the outputs and register them as
|
/* Compute the FS closure of the outputs and register them as
|
||||||
being valid. */
|
being valid. */
|
||||||
registerOutputs();
|
auto builtOutputs = registerOutputs();
|
||||||
|
|
||||||
StorePathSet outputPaths;
|
StorePathSet outputPaths;
|
||||||
for (auto & [_, path] : finalOutputs)
|
for (auto & [_, output] : buildResult.builtOutputs)
|
||||||
outputPaths.insert(path);
|
outputPaths.insert(output.outPath);
|
||||||
runPostBuildHook(
|
runPostBuildHook(
|
||||||
worker.store,
|
worker.store,
|
||||||
*logger,
|
*logger,
|
||||||
|
@ -890,7 +870,7 @@ void DerivationGoal::buildDone()
|
||||||
|
|
||||||
if (buildMode == bmCheck) {
|
if (buildMode == bmCheck) {
|
||||||
cleanupPostOutputsRegisteredModeCheck();
|
cleanupPostOutputsRegisteredModeCheck();
|
||||||
done(BuildResult::Built);
|
done(BuildResult::Built, std::move(builtOutputs));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -911,6 +891,8 @@ void DerivationGoal::buildDone()
|
||||||
outputLocks.setDeletion(true);
|
outputLocks.setDeletion(true);
|
||||||
outputLocks.unlock();
|
outputLocks.unlock();
|
||||||
|
|
||||||
|
done(BuildResult::Built, std::move(builtOutputs));
|
||||||
|
|
||||||
} catch (BuildError & e) {
|
} catch (BuildError & e) {
|
||||||
outputLocks.unlock();
|
outputLocks.unlock();
|
||||||
|
|
||||||
|
@ -930,14 +912,13 @@ void DerivationGoal::buildDone()
|
||||||
BuildResult::PermanentFailure;
|
BuildResult::PermanentFailure;
|
||||||
}
|
}
|
||||||
|
|
||||||
done(st, e);
|
done(st, {}, e);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
done(BuildResult::Built);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void DerivationGoal::resolvedFinished() {
|
void DerivationGoal::resolvedFinished()
|
||||||
|
{
|
||||||
assert(resolvedDrvGoal);
|
assert(resolvedDrvGoal);
|
||||||
auto resolvedDrv = *resolvedDrvGoal->drv;
|
auto resolvedDrv = *resolvedDrvGoal->drv;
|
||||||
|
|
||||||
|
@ -950,6 +931,8 @@ void DerivationGoal::resolvedFinished() {
|
||||||
if (realWantedOutputs.empty())
|
if (realWantedOutputs.empty())
|
||||||
realWantedOutputs = resolvedDrv.outputNames();
|
realWantedOutputs = resolvedDrv.outputNames();
|
||||||
|
|
||||||
|
DrvOutputs builtOutputs;
|
||||||
|
|
||||||
for (auto & wantedOutput : realWantedOutputs) {
|
for (auto & wantedOutput : realWantedOutputs) {
|
||||||
assert(initialOutputs.count(wantedOutput) != 0);
|
assert(initialOutputs.count(wantedOutput) != 0);
|
||||||
assert(resolvedHashes.count(wantedOutput) != 0);
|
assert(resolvedHashes.count(wantedOutput) != 0);
|
||||||
|
@ -966,10 +949,11 @@ void DerivationGoal::resolvedFinished() {
|
||||||
signRealisation(newRealisation);
|
signRealisation(newRealisation);
|
||||||
worker.store.registerDrvOutput(newRealisation);
|
worker.store.registerDrvOutput(newRealisation);
|
||||||
outputPaths.insert(realisation->outPath);
|
outputPaths.insert(realisation->outPath);
|
||||||
|
builtOutputs.emplace(realisation->id, *realisation);
|
||||||
} else {
|
} else {
|
||||||
// If we don't have a realisation, then it must mean that something
|
// If we don't have a realisation, then it must mean that something
|
||||||
// failed when building the resolved drv
|
// failed when building the resolved drv
|
||||||
assert(!result.success());
|
assert(!buildResult.success());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -981,7 +965,7 @@ void DerivationGoal::resolvedFinished() {
|
||||||
);
|
);
|
||||||
|
|
||||||
auto status = [&]() {
|
auto status = [&]() {
|
||||||
auto resolvedResult = resolvedDrvGoal->getResult();
|
auto & resolvedResult = resolvedDrvGoal->buildResult;
|
||||||
switch (resolvedResult.status) {
|
switch (resolvedResult.status) {
|
||||||
case BuildResult::AlreadyValid:
|
case BuildResult::AlreadyValid:
|
||||||
return BuildResult::ResolvesToAlreadyValid;
|
return BuildResult::ResolvesToAlreadyValid;
|
||||||
|
@ -990,7 +974,7 @@ void DerivationGoal::resolvedFinished() {
|
||||||
}
|
}
|
||||||
}();
|
}();
|
||||||
|
|
||||||
done(status);
|
done(status, std::move(builtOutputs));
|
||||||
}
|
}
|
||||||
|
|
||||||
HookReply DerivationGoal::tryBuildHook()
|
HookReply DerivationGoal::tryBuildHook()
|
||||||
|
@ -1100,7 +1084,7 @@ HookReply DerivationGoal::tryBuildHook()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void DerivationGoal::registerOutputs()
|
DrvOutputs DerivationGoal::registerOutputs()
|
||||||
{
|
{
|
||||||
/* When using a build hook, the build hook can register the output
|
/* 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
|
as valid (by doing `nix-store --import'). If so we don't have
|
||||||
|
@ -1109,21 +1093,7 @@ void DerivationGoal::registerOutputs()
|
||||||
We can only early return when the outputs are known a priori. For
|
We can only early return when the outputs are known a priori. For
|
||||||
floating content-addressed derivations this isn't the case.
|
floating content-addressed derivations this isn't the case.
|
||||||
*/
|
*/
|
||||||
for (auto & [outputName, optOutputPath] : worker.store.queryPartialDerivationOutputMap(drvPath)) {
|
return assertPathValidity();
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Path DerivationGoal::openLogFile()
|
Path DerivationGoal::openLogFile()
|
||||||
|
@ -1185,7 +1155,7 @@ void DerivationGoal::handleChildOutput(int fd, std::string_view data)
|
||||||
if (settings.maxLogSize && logSize > settings.maxLogSize) {
|
if (settings.maxLogSize && logSize > settings.maxLogSize) {
|
||||||
killChild();
|
killChild();
|
||||||
done(
|
done(
|
||||||
BuildResult::LogLimitExceeded,
|
BuildResult::LogLimitExceeded, {},
|
||||||
Error("%s killed after writing more than %d bytes of log output",
|
Error("%s killed after writing more than %d bytes of log output",
|
||||||
getName(), settings.maxLogSize));
|
getName(), settings.maxLogSize));
|
||||||
return;
|
return;
|
||||||
|
@ -1274,10 +1244,12 @@ OutputPathMap DerivationGoal::queryDerivationOutputMap()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void DerivationGoal::checkPathValidity()
|
std::pair<bool, DrvOutputs> DerivationGoal::checkPathValidity()
|
||||||
{
|
{
|
||||||
bool checkHash = buildMode == bmRepair;
|
bool checkHash = buildMode == bmRepair;
|
||||||
auto wantedOutputsLeft = wantedOutputs;
|
auto wantedOutputsLeft = wantedOutputs;
|
||||||
|
DrvOutputs validOutputs;
|
||||||
|
|
||||||
for (auto & i : queryPartialDerivationOutputMap()) {
|
for (auto & i : queryPartialDerivationOutputMap()) {
|
||||||
InitialOutput & info = initialOutputs.at(i.first);
|
InitialOutput & info = initialOutputs.at(i.first);
|
||||||
info.wanted = wantOutput(i.first, wantedOutputs);
|
info.wanted = wantOutput(i.first, wantedOutputs);
|
||||||
|
@ -1294,18 +1266,18 @@ void DerivationGoal::checkPathValidity()
|
||||||
: PathStatus::Corrupt,
|
: PathStatus::Corrupt,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
if (settings.isExperimentalFeatureEnabled(Xp::CaDerivations)) {
|
|
||||||
auto drvOutput = DrvOutput{initialOutputs.at(i.first).outputHash, i.first};
|
auto drvOutput = DrvOutput{initialOutputs.at(i.first).outputHash, i.first};
|
||||||
|
if (settings.isExperimentalFeatureEnabled(Xp::CaDerivations)) {
|
||||||
if (auto real = worker.store.queryRealisation(drvOutput)) {
|
if (auto real = worker.store.queryRealisation(drvOutput)) {
|
||||||
info.known = {
|
info.known = {
|
||||||
.path = real->outPath,
|
.path = real->outPath,
|
||||||
.status = PathStatus::Valid,
|
.status = PathStatus::Valid,
|
||||||
};
|
};
|
||||||
} else if (info.known && info.known->status == PathStatus::Valid) {
|
} else if (info.known && info.known->isValid()) {
|
||||||
// We know the output because it' a static output of the
|
// We know the output because it's a static output of the
|
||||||
// derivation, and the output path is valid, but we don't have
|
// derivation, and the output path is valid, but we don't have
|
||||||
// its realisation stored (probably because it has been built
|
// its realisation stored (probably because it has been built
|
||||||
// without the `ca-derivations` experimental flag)
|
// without the `ca-derivations` experimental flag).
|
||||||
worker.store.registerDrvOutput(
|
worker.store.registerDrvOutput(
|
||||||
Realisation {
|
Realisation {
|
||||||
drvOutput,
|
drvOutput,
|
||||||
|
@ -1314,6 +1286,8 @@ void DerivationGoal::checkPathValidity()
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
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 all the outputs via the empty set, we are always fine.
|
||||||
// If we requested specific elements, the loop above removes all the valid
|
// If we requested specific elements, the loop above removes all the valid
|
||||||
|
@ -1322,24 +1296,51 @@ void DerivationGoal::checkPathValidity()
|
||||||
throw Error("derivation '%s' does not have wanted outputs %s",
|
throw Error("derivation '%s' does not have wanted outputs %s",
|
||||||
worker.store.printStorePath(drvPath),
|
worker.store.printStorePath(drvPath),
|
||||||
concatStringsSep(", ", quoteStrings(wantedOutputsLeft)));
|
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 };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void DerivationGoal::done(BuildResult::Status status, std::optional<Error> ex)
|
DrvOutputs DerivationGoal::assertPathValidity()
|
||||||
{
|
{
|
||||||
result.status = status;
|
auto [allValid, validOutputs] = checkPathValidity();
|
||||||
|
if (!allValid)
|
||||||
|
throw Error("some outputs are unexpectedly invalid");
|
||||||
|
return validOutputs;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void DerivationGoal::done(
|
||||||
|
BuildResult::Status status,
|
||||||
|
DrvOutputs builtOutputs,
|
||||||
|
std::optional<Error> ex)
|
||||||
|
{
|
||||||
|
buildResult.drvPath = drvPath;
|
||||||
|
buildResult.status = status;
|
||||||
if (ex)
|
if (ex)
|
||||||
result.errorMsg = ex->what();
|
// FIXME: strip: "error: "
|
||||||
amDone(result.success() ? ecSuccess : ecFailed, ex);
|
buildResult.errorMsg = ex->what();
|
||||||
if (result.status == BuildResult::TimedOut)
|
amDone(buildResult.success() ? ecSuccess : ecFailed, ex);
|
||||||
|
if (buildResult.status == BuildResult::TimedOut)
|
||||||
worker.timedOut = true;
|
worker.timedOut = true;
|
||||||
if (result.status == BuildResult::PermanentFailure)
|
if (buildResult.status == BuildResult::PermanentFailure)
|
||||||
worker.permanentFailure = true;
|
worker.permanentFailure = true;
|
||||||
|
|
||||||
mcExpectedBuilds.reset();
|
mcExpectedBuilds.reset();
|
||||||
mcRunningBuilds.reset();
|
mcRunningBuilds.reset();
|
||||||
|
|
||||||
if (result.success()) {
|
if (buildResult.success()) {
|
||||||
|
assert(!builtOutputs.empty());
|
||||||
|
buildResult.builtOutputs = std::move(builtOutputs);
|
||||||
if (status == BuildResult::Built)
|
if (status == BuildResult::Built)
|
||||||
worker.doneBuilds++;
|
worker.doneBuilds++;
|
||||||
} else {
|
} else {
|
||||||
|
@ -1353,7 +1354,7 @@ void DerivationGoal::done(BuildResult::Status status, std::optional<Error> ex)
|
||||||
if (traceBuiltOutputsFile != "") {
|
if (traceBuiltOutputsFile != "") {
|
||||||
std::fstream fs;
|
std::fstream fs;
|
||||||
fs.open(traceBuiltOutputsFile, std::fstream::out);
|
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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -2,7 +2,6 @@
|
||||||
|
|
||||||
#include "parsed-derivations.hh"
|
#include "parsed-derivations.hh"
|
||||||
#include "lock.hh"
|
#include "lock.hh"
|
||||||
#include "build-result.hh"
|
|
||||||
#include "store-api.hh"
|
#include "store-api.hh"
|
||||||
#include "pathlocks.hh"
|
#include "pathlocks.hh"
|
||||||
#include "goal.hh"
|
#include "goal.hh"
|
||||||
|
@ -105,20 +104,8 @@ struct DerivationGoal : public Goal
|
||||||
typedef void (DerivationGoal::*GoalState)();
|
typedef void (DerivationGoal::*GoalState)();
|
||||||
GoalState state;
|
GoalState state;
|
||||||
|
|
||||||
/* The final output paths of the build.
|
|
||||||
|
|
||||||
- For input-addressed derivations, always the precomputed paths
|
|
||||||
|
|
||||||
- For content-addressed derivations, calcuated from whatever the hash
|
|
||||||
ends up being. (Note that fixed outputs derivations that produce the
|
|
||||||
"wrong" output still install that data under its true content-address.)
|
|
||||||
*/
|
|
||||||
OutputPathMap finalOutputs;
|
|
||||||
|
|
||||||
BuildMode buildMode;
|
BuildMode buildMode;
|
||||||
|
|
||||||
BuildResult result;
|
|
||||||
|
|
||||||
/* The current round, if we're building multiple times. */
|
/* The current round, if we're building multiple times. */
|
||||||
size_t curRound = 1;
|
size_t curRound = 1;
|
||||||
|
|
||||||
|
@ -153,8 +140,6 @@ struct DerivationGoal : public Goal
|
||||||
/* Add wanted outputs to an already existing derivation goal. */
|
/* Add wanted outputs to an already existing derivation goal. */
|
||||||
void addWantedOutputs(const StringSet & outputs);
|
void addWantedOutputs(const StringSet & outputs);
|
||||||
|
|
||||||
BuildResult getResult() { return result; }
|
|
||||||
|
|
||||||
/* The states. */
|
/* The states. */
|
||||||
void getDerivation();
|
void getDerivation();
|
||||||
void loadDerivation();
|
void loadDerivation();
|
||||||
|
@ -176,7 +161,7 @@ struct DerivationGoal : public Goal
|
||||||
|
|
||||||
/* Check that the derivation outputs all exist and register them
|
/* Check that the derivation outputs all exist and register them
|
||||||
as valid. */
|
as valid. */
|
||||||
virtual void registerOutputs();
|
virtual DrvOutputs registerOutputs();
|
||||||
|
|
||||||
/* Open a log file and a pipe to it. */
|
/* Open a log file and a pipe to it. */
|
||||||
Path openLogFile();
|
Path openLogFile();
|
||||||
|
@ -211,8 +196,17 @@ struct DerivationGoal : public Goal
|
||||||
std::map<std::string, std::optional<StorePath>> queryPartialDerivationOutputMap();
|
std::map<std::string, std::optional<StorePath>> queryPartialDerivationOutputMap();
|
||||||
OutputPathMap queryDerivationOutputMap();
|
OutputPathMap queryDerivationOutputMap();
|
||||||
|
|
||||||
/* Return the set of (in)valid paths. */
|
/* Update 'initialOutputs' to determine the current status of the
|
||||||
void checkPathValidity();
|
outputs of the derivation. Also returns a Boolean denoting
|
||||||
|
whether all outputs are valid and non-corrupt, and a
|
||||||
|
'DrvOutputs' structure containing the valid and wanted
|
||||||
|
outputs. */
|
||||||
|
std::pair<bool, DrvOutputs> checkPathValidity();
|
||||||
|
|
||||||
|
/* Aborts if any output is not valid or corrupt, and otherwise
|
||||||
|
returns a 'DrvOutputs' structure containing the wanted
|
||||||
|
outputs. */
|
||||||
|
DrvOutputs assertPathValidity();
|
||||||
|
|
||||||
/* Forcibly kill the child process, if any. */
|
/* Forcibly kill the child process, if any. */
|
||||||
virtual void killChild();
|
virtual void killChild();
|
||||||
|
@ -223,6 +217,7 @@ struct DerivationGoal : public Goal
|
||||||
|
|
||||||
void done(
|
void done(
|
||||||
BuildResult::Status status,
|
BuildResult::Status status,
|
||||||
|
DrvOutputs builtOutputs = {},
|
||||||
std::optional<Error> ex = {});
|
std::optional<Error> ex = {});
|
||||||
|
|
||||||
StorePathSet exportReferences(const StorePathSet & storePaths);
|
StorePathSet exportReferences(const StorePathSet & storePaths);
|
||||||
|
|
|
@ -6,8 +6,12 @@
|
||||||
|
|
||||||
namespace nix {
|
namespace nix {
|
||||||
|
|
||||||
DrvOutputSubstitutionGoal::DrvOutputSubstitutionGoal(const DrvOutput& id, Worker & worker, RepairFlag repair, std::optional<ContentAddress> ca)
|
DrvOutputSubstitutionGoal::DrvOutputSubstitutionGoal(
|
||||||
: Goal(worker)
|
const DrvOutput & id,
|
||||||
|
Worker & worker,
|
||||||
|
RepairFlag repair,
|
||||||
|
std::optional<ContentAddress> ca)
|
||||||
|
: Goal(worker, DerivedPath::Opaque { StorePath::dummy })
|
||||||
, id(id)
|
, id(id)
|
||||||
{
|
{
|
||||||
state = &DrvOutputSubstitutionGoal::init;
|
state = &DrvOutputSubstitutionGoal::init;
|
||||||
|
@ -32,7 +36,7 @@ void DrvOutputSubstitutionGoal::init()
|
||||||
|
|
||||||
void DrvOutputSubstitutionGoal::tryNext()
|
void DrvOutputSubstitutionGoal::tryNext()
|
||||||
{
|
{
|
||||||
trace("Trying next substituter");
|
trace("trying next substituter");
|
||||||
|
|
||||||
if (subs.size() == 0) {
|
if (subs.size() == 0) {
|
||||||
/* None left. Terminate this goal and let someone else deal
|
/* None left. Terminate this goal and let someone else deal
|
||||||
|
@ -119,7 +123,7 @@ void DrvOutputSubstitutionGoal::realisationFetched()
|
||||||
void DrvOutputSubstitutionGoal::outPathValid()
|
void DrvOutputSubstitutionGoal::outPathValid()
|
||||||
{
|
{
|
||||||
assert(outputInfo);
|
assert(outputInfo);
|
||||||
trace("Output path substituted");
|
trace("output path substituted");
|
||||||
|
|
||||||
if (nrFailed > 0) {
|
if (nrFailed > 0) {
|
||||||
debug("The output path of the derivation output '%s' could not be substituted", id.to_string());
|
debug("The output path of the derivation output '%s' could not be substituted", id.to_string());
|
||||||
|
|
|
@ -47,43 +47,51 @@ void Store::buildPaths(const std::vector<DerivedPath> & reqs, BuildMode buildMod
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::vector<BuildResult> Store::buildPathsWithResults(
|
||||||
|
const std::vector<DerivedPath> & reqs,
|
||||||
|
BuildMode buildMode,
|
||||||
|
std::shared_ptr<Store> evalStore)
|
||||||
|
{
|
||||||
|
Worker worker(*this, evalStore ? *evalStore : *this);
|
||||||
|
|
||||||
|
Goals goals;
|
||||||
|
for (const auto & br : reqs) {
|
||||||
|
std::visit(overloaded {
|
||||||
|
[&](const DerivedPath::Built & bfd) {
|
||||||
|
goals.insert(worker.makeDerivationGoal(bfd.drvPath, bfd.outputs, buildMode));
|
||||||
|
},
|
||||||
|
[&](const DerivedPath::Opaque & bo) {
|
||||||
|
goals.insert(worker.makePathSubstitutionGoal(bo.path, buildMode == bmRepair ? Repair : NoRepair));
|
||||||
|
},
|
||||||
|
}, br.raw());
|
||||||
|
}
|
||||||
|
|
||||||
|
worker.run(goals);
|
||||||
|
|
||||||
|
std::vector<BuildResult> results;
|
||||||
|
|
||||||
|
for (auto & i : goals)
|
||||||
|
results.push_back(i->buildResult);
|
||||||
|
|
||||||
|
return results;
|
||||||
|
}
|
||||||
|
|
||||||
BuildResult Store::buildDerivation(const StorePath & drvPath, const BasicDerivation & drv,
|
BuildResult Store::buildDerivation(const StorePath & drvPath, const BasicDerivation & drv,
|
||||||
BuildMode buildMode)
|
BuildMode buildMode)
|
||||||
{
|
{
|
||||||
Worker worker(*this, *this);
|
Worker worker(*this, *this);
|
||||||
auto goal = worker.makeBasicDerivationGoal(drvPath, drv, {}, buildMode);
|
auto goal = worker.makeBasicDerivationGoal(drvPath, drv, {}, buildMode);
|
||||||
|
|
||||||
BuildResult result;
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
worker.run(Goals{goal});
|
worker.run(Goals{goal});
|
||||||
result = goal->getResult();
|
return goal->buildResult;
|
||||||
} catch (Error & e) {
|
} catch (Error & e) {
|
||||||
result.status = BuildResult::MiscFailure;
|
return BuildResult {
|
||||||
result.errorMsg = e.msg();
|
.status = BuildResult::MiscFailure,
|
||||||
}
|
.errorMsg = e.msg(),
|
||||||
// XXX: Should use `goal->queryPartialDerivationOutputMap()` once it's
|
.path = DerivedPath::Built { .drvPath = drvPath },
|
||||||
// extended to return the full realisation for each output
|
};
|
||||||
auto staticDrvOutputs = drv.outputsAndOptPaths(*this);
|
};
|
||||||
auto outputHashes = staticOutputHashes(*this, drv);
|
|
||||||
for (auto & [outputName, staticOutput] : staticDrvOutputs) {
|
|
||||||
auto outputId = DrvOutput{outputHashes.at(outputName), outputName};
|
|
||||||
if (staticOutput.second)
|
|
||||||
result.builtOutputs.insert_or_assign(
|
|
||||||
outputId,
|
|
||||||
Realisation{ outputId, *staticOutput.second}
|
|
||||||
);
|
|
||||||
if (settings.isExperimentalFeatureEnabled(Xp::CaDerivations) && !derivationHasKnownOutputPaths(drv.type())) {
|
|
||||||
auto realisation = this->queryRealisation(outputId);
|
|
||||||
if (realisation)
|
|
||||||
result.builtOutputs.insert_or_assign(
|
|
||||||
outputId,
|
|
||||||
*realisation
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
|
|
||||||
#include "types.hh"
|
#include "types.hh"
|
||||||
#include "store-api.hh"
|
#include "store-api.hh"
|
||||||
|
#include "build-result.hh"
|
||||||
|
|
||||||
namespace nix {
|
namespace nix {
|
||||||
|
|
||||||
|
@ -55,10 +56,15 @@ struct Goal : public std::enable_shared_from_this<Goal>
|
||||||
/* Whether the goal is finished. */
|
/* Whether the goal is finished. */
|
||||||
ExitCode exitCode;
|
ExitCode exitCode;
|
||||||
|
|
||||||
|
/* Build result. */
|
||||||
|
BuildResult buildResult;
|
||||||
|
|
||||||
/* Exception containing an error message, if any. */
|
/* Exception containing an error message, if any. */
|
||||||
std::optional<Error> ex;
|
std::optional<Error> ex;
|
||||||
|
|
||||||
Goal(Worker & worker) : worker(worker)
|
Goal(Worker & worker, DerivedPath path)
|
||||||
|
: worker(worker)
|
||||||
|
, buildResult { .path = std::move(path) }
|
||||||
{
|
{
|
||||||
nrFailed = nrNoSubstituters = nrIncompleteClosure = 0;
|
nrFailed = nrNoSubstituters = nrIncompleteClosure = 0;
|
||||||
exitCode = ecBusy;
|
exitCode = ecBusy;
|
||||||
|
|
|
@ -194,7 +194,7 @@ void LocalDerivationGoal::tryLocalBuild() {
|
||||||
outputLocks.unlock();
|
outputLocks.unlock();
|
||||||
buildUser.reset();
|
buildUser.reset();
|
||||||
worker.permanentFailure = true;
|
worker.permanentFailure = true;
|
||||||
done(BuildResult::InputRejected, e);
|
done(BuildResult::InputRejected, {}, e);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -756,7 +756,7 @@ void LocalDerivationGoal::startBuilder()
|
||||||
if (tcsetattr(builderOut.writeSide.get(), TCSANOW, &term))
|
if (tcsetattr(builderOut.writeSide.get(), TCSANOW, &term))
|
||||||
throw SysError("putting pseudoterminal into raw mode");
|
throw SysError("putting pseudoterminal into raw mode");
|
||||||
|
|
||||||
result.startTime = time(0);
|
buildResult.startTime = time(0);
|
||||||
|
|
||||||
/* Fork a child to build the package. */
|
/* Fork a child to build the package. */
|
||||||
|
|
||||||
|
@ -1260,6 +1260,16 @@ struct RestrictedStore : public virtual RestrictedStoreConfig, public virtual Lo
|
||||||
}
|
}
|
||||||
|
|
||||||
void buildPaths(const std::vector<DerivedPath> & paths, BuildMode buildMode, std::shared_ptr<Store> evalStore) override
|
void buildPaths(const std::vector<DerivedPath> & paths, BuildMode buildMode, std::shared_ptr<Store> evalStore) override
|
||||||
|
{
|
||||||
|
for (auto & result : buildPathsWithResults(paths, buildMode, evalStore))
|
||||||
|
if (!result.success())
|
||||||
|
result.rethrow();
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<BuildResult> buildPathsWithResults(
|
||||||
|
const std::vector<DerivedPath> & paths,
|
||||||
|
BuildMode buildMode = bmNormal,
|
||||||
|
std::shared_ptr<Store> evalStore = nullptr) override
|
||||||
{
|
{
|
||||||
assert(!evalStore);
|
assert(!evalStore);
|
||||||
|
|
||||||
|
@ -1273,25 +1283,12 @@ struct RestrictedStore : public virtual RestrictedStoreConfig, public virtual Lo
|
||||||
throw InvalidPath("cannot build '%s' in recursive Nix because path is unknown", req.to_string(*next));
|
throw InvalidPath("cannot build '%s' in recursive Nix because path is unknown", req.to_string(*next));
|
||||||
}
|
}
|
||||||
|
|
||||||
next->buildPaths(paths, buildMode);
|
auto results = next->buildPathsWithResults(paths, buildMode);
|
||||||
|
|
||||||
for (auto & path : paths) {
|
for (auto & result : results) {
|
||||||
auto p = std::get_if<DerivedPath::Built>(&path);
|
for (auto & [outputName, output] : result.builtOutputs) {
|
||||||
if (!p) continue;
|
newPaths.insert(output.outPath);
|
||||||
auto & bfd = *p;
|
newRealisations.insert(output);
|
||||||
auto drv = readDerivation(bfd.drvPath);
|
|
||||||
auto drvHashes = staticOutputHashes(*this, drv);
|
|
||||||
auto outputs = next->queryDerivationOutputMap(bfd.drvPath);
|
|
||||||
for (auto & [outputName, outputPath] : outputs)
|
|
||||||
if (wantOutput(outputName, bfd.outputs)) {
|
|
||||||
newPaths.insert(outputPath);
|
|
||||||
if (settings.isExperimentalFeatureEnabled(Xp::CaDerivations)) {
|
|
||||||
auto thisRealisation = next->queryRealisation(
|
|
||||||
DrvOutput{drvHashes.at(outputName), outputName}
|
|
||||||
);
|
|
||||||
assert(thisRealisation);
|
|
||||||
newRealisations.insert(*thisRealisation);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1301,6 +1298,8 @@ struct RestrictedStore : public virtual RestrictedStoreConfig, public virtual Lo
|
||||||
goal.addDependency(path);
|
goal.addDependency(path);
|
||||||
for (auto & real : Realisation::closure(*next, newRealisations))
|
for (auto & real : Realisation::closure(*next, newRealisations))
|
||||||
goal.addedDrvOutputs.insert(real.id);
|
goal.addedDrvOutputs.insert(real.id);
|
||||||
|
|
||||||
|
return results;
|
||||||
}
|
}
|
||||||
|
|
||||||
BuildResult buildDerivation(const StorePath & drvPath, const BasicDerivation & drv,
|
BuildResult buildDerivation(const StorePath & drvPath, const BasicDerivation & drv,
|
||||||
|
@ -2069,7 +2068,7 @@ void LocalDerivationGoal::runChild()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void LocalDerivationGoal::registerOutputs()
|
DrvOutputs LocalDerivationGoal::registerOutputs()
|
||||||
{
|
{
|
||||||
/* When using a build hook, the build hook can register the output
|
/* 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
|
as valid (by doing `nix-store --import'). If so we don't have
|
||||||
|
@ -2078,10 +2077,8 @@ void LocalDerivationGoal::registerOutputs()
|
||||||
We can only early return when the outputs are known a priori. For
|
We can only early return when the outputs are known a priori. For
|
||||||
floating content-addressed derivations this isn't the case.
|
floating content-addressed derivations this isn't the case.
|
||||||
*/
|
*/
|
||||||
if (hook) {
|
if (hook)
|
||||||
DerivationGoal::registerOutputs();
|
return DerivationGoal::registerOutputs();
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::map<std::string, ValidPathInfo> infos;
|
std::map<std::string, ValidPathInfo> infos;
|
||||||
|
|
||||||
|
@ -2204,6 +2201,8 @@ void LocalDerivationGoal::registerOutputs()
|
||||||
|
|
||||||
std::reverse(sortedOutputNames.begin(), sortedOutputNames.end());
|
std::reverse(sortedOutputNames.begin(), sortedOutputNames.end());
|
||||||
|
|
||||||
|
OutputPathMap finalOutputs;
|
||||||
|
|
||||||
for (auto & outputName : sortedOutputNames) {
|
for (auto & outputName : sortedOutputNames) {
|
||||||
auto output = drv->outputs.at(outputName);
|
auto output = drv->outputs.at(outputName);
|
||||||
auto & scratchPath = scratchOutputs.at(outputName);
|
auto & scratchPath = scratchOutputs.at(outputName);
|
||||||
|
@ -2340,6 +2339,7 @@ void LocalDerivationGoal::registerOutputs()
|
||||||
};
|
};
|
||||||
|
|
||||||
ValidPathInfo newInfo = std::visit(overloaded {
|
ValidPathInfo newInfo = std::visit(overloaded {
|
||||||
|
|
||||||
[&](const DerivationOutputInputAddressed & output) {
|
[&](const DerivationOutputInputAddressed & output) {
|
||||||
/* input-addressed case */
|
/* input-addressed case */
|
||||||
auto requiredFinalPath = output.path;
|
auto requiredFinalPath = output.path;
|
||||||
|
@ -2359,6 +2359,7 @@ void LocalDerivationGoal::registerOutputs()
|
||||||
newInfo0.references.insert(newInfo0.path);
|
newInfo0.references.insert(newInfo0.path);
|
||||||
return newInfo0;
|
return newInfo0;
|
||||||
},
|
},
|
||||||
|
|
||||||
[&](const DerivationOutputCAFixed & dof) {
|
[&](const DerivationOutputCAFixed & dof) {
|
||||||
auto newInfo0 = newInfoFromCA(DerivationOutputCAFloating {
|
auto newInfo0 = newInfoFromCA(DerivationOutputCAFloating {
|
||||||
.method = dof.hash.method,
|
.method = dof.hash.method,
|
||||||
|
@ -2381,14 +2382,17 @@ void LocalDerivationGoal::registerOutputs()
|
||||||
}
|
}
|
||||||
return newInfo0;
|
return newInfo0;
|
||||||
},
|
},
|
||||||
[&](DerivationOutputCAFloating dof) {
|
|
||||||
|
[&](DerivationOutputCAFloating & dof) {
|
||||||
return newInfoFromCA(dof);
|
return newInfoFromCA(dof);
|
||||||
},
|
},
|
||||||
|
|
||||||
[&](DerivationOutputDeferred) -> ValidPathInfo {
|
[&](DerivationOutputDeferred) -> ValidPathInfo {
|
||||||
// No derivation should reach that point without having been
|
// No derivation should reach that point without having been
|
||||||
// rewritten first
|
// rewritten first
|
||||||
assert(false);
|
assert(false);
|
||||||
},
|
},
|
||||||
|
|
||||||
}, output.output);
|
}, output.output);
|
||||||
|
|
||||||
/* FIXME: set proper permissions in restorePath() so
|
/* FIXME: set proper permissions in restorePath() so
|
||||||
|
@ -2499,11 +2503,12 @@ void LocalDerivationGoal::registerOutputs()
|
||||||
}
|
}
|
||||||
|
|
||||||
if (buildMode == bmCheck) {
|
if (buildMode == bmCheck) {
|
||||||
// In case of FOD mismatches on `--check` an error must be thrown as this is also
|
/* In case of fixed-output derivations, if there are
|
||||||
// a source for non-determinism.
|
mismatches on `--check` an error must be thrown as this is
|
||||||
|
also a source for non-determinism. */
|
||||||
if (delayedException)
|
if (delayedException)
|
||||||
std::rethrow_exception(delayedException);
|
std::rethrow_exception(delayedException);
|
||||||
return;
|
return assertPathValidity();
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Apply output checks. */
|
/* Apply output checks. */
|
||||||
|
@ -2515,7 +2520,7 @@ void LocalDerivationGoal::registerOutputs()
|
||||||
assert(prevInfos.size() == infos.size());
|
assert(prevInfos.size() == infos.size());
|
||||||
for (auto i = prevInfos.begin(), j = infos.begin(); i != prevInfos.end(); ++i, ++j)
|
for (auto i = prevInfos.begin(), j = infos.begin(); i != prevInfos.end(); ++i, ++j)
|
||||||
if (!(*i == *j)) {
|
if (!(*i == *j)) {
|
||||||
result.isNonDeterministic = true;
|
buildResult.isNonDeterministic = true;
|
||||||
Path prev = worker.store.printStorePath(i->second.path) + checkSuffix;
|
Path prev = worker.store.printStorePath(i->second.path) + checkSuffix;
|
||||||
bool prevExists = keepPreviousRound && pathExists(prev);
|
bool prevExists = keepPreviousRound && pathExists(prev);
|
||||||
hintformat hint = prevExists
|
hintformat hint = prevExists
|
||||||
|
@ -2553,7 +2558,7 @@ void LocalDerivationGoal::registerOutputs()
|
||||||
|
|
||||||
if (curRound < nrRounds) {
|
if (curRound < nrRounds) {
|
||||||
prevInfos = std::move(infos);
|
prevInfos = std::move(infos);
|
||||||
return;
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Remove the .check directories if we're done. FIXME: keep them
|
/* Remove the .check directories if we're done. FIXME: keep them
|
||||||
|
@ -2588,17 +2593,24 @@ void LocalDerivationGoal::registerOutputs()
|
||||||
means it's safe to link the derivation to the output hash. We must do
|
means it's safe to link the derivation to the output hash. We must do
|
||||||
that for floating CA derivations, which otherwise couldn't be cached,
|
that for floating CA derivations, which otherwise couldn't be cached,
|
||||||
but it's fine to do in all cases. */
|
but it's fine to do in all cases. */
|
||||||
|
DrvOutputs builtOutputs;
|
||||||
|
|
||||||
if (settings.isExperimentalFeatureEnabled(Xp::CaDerivations)) {
|
|
||||||
for (auto & [outputName, newInfo] : infos) {
|
for (auto & [outputName, newInfo] : infos) {
|
||||||
auto thisRealisation = Realisation {
|
auto thisRealisation = Realisation {
|
||||||
.id = DrvOutput{initialOutputs.at(outputName).outputHash,
|
.id = DrvOutput {
|
||||||
outputName},
|
initialOutputs.at(outputName).outputHash,
|
||||||
.outPath = newInfo.path};
|
outputName
|
||||||
|
},
|
||||||
|
.outPath = newInfo.path
|
||||||
|
};
|
||||||
|
if (settings.isExperimentalFeatureEnabled(Xp::CaDerivations)) {
|
||||||
signRealisation(thisRealisation);
|
signRealisation(thisRealisation);
|
||||||
worker.store.registerDrvOutput(thisRealisation);
|
worker.store.registerDrvOutput(thisRealisation);
|
||||||
}
|
}
|
||||||
|
builtOutputs.emplace(thisRealisation.id, thisRealisation);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return builtOutputs;
|
||||||
}
|
}
|
||||||
|
|
||||||
void LocalDerivationGoal::signRealisation(Realisation & realisation)
|
void LocalDerivationGoal::signRealisation(Realisation & realisation)
|
||||||
|
@ -2607,7 +2619,7 @@ void LocalDerivationGoal::signRealisation(Realisation & realisation)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void LocalDerivationGoal::checkOutputs(const std::map<Path, ValidPathInfo> & outputs)
|
void LocalDerivationGoal::checkOutputs(const std::map<std::string, ValidPathInfo> & outputs)
|
||||||
{
|
{
|
||||||
std::map<Path, const ValidPathInfo &> outputsByPath;
|
std::map<Path, const ValidPathInfo &> outputsByPath;
|
||||||
for (auto & output : outputs)
|
for (auto & output : outputs)
|
||||||
|
@ -2679,8 +2691,8 @@ void LocalDerivationGoal::checkOutputs(const std::map<Path, ValidPathInfo> & out
|
||||||
for (auto & i : *value) {
|
for (auto & i : *value) {
|
||||||
if (worker.store.isStorePath(i))
|
if (worker.store.isStorePath(i))
|
||||||
spec.insert(worker.store.parseStorePath(i));
|
spec.insert(worker.store.parseStorePath(i));
|
||||||
else if (finalOutputs.count(i))
|
else if (outputs.count(i))
|
||||||
spec.insert(finalOutputs.at(i));
|
spec.insert(outputs.at(i).path);
|
||||||
else throw BuildError("derivation contains an illegal reference specifier '%s'", i);
|
else throw BuildError("derivation contains an illegal reference specifier '%s'", i);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -169,7 +169,7 @@ struct LocalDerivationGoal : public DerivationGoal
|
||||||
|
|
||||||
/* Check that the derivation outputs all exist and register them
|
/* Check that the derivation outputs all exist and register them
|
||||||
as valid. */
|
as valid. */
|
||||||
void registerOutputs() override;
|
DrvOutputs registerOutputs() override;
|
||||||
|
|
||||||
void signRealisation(Realisation &) override;
|
void signRealisation(Realisation &) override;
|
||||||
|
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
namespace nix {
|
namespace nix {
|
||||||
|
|
||||||
PathSubstitutionGoal::PathSubstitutionGoal(const StorePath & storePath, Worker & worker, RepairFlag repair, std::optional<ContentAddress> ca)
|
PathSubstitutionGoal::PathSubstitutionGoal(const StorePath & storePath, Worker & worker, RepairFlag repair, std::optional<ContentAddress> ca)
|
||||||
: Goal(worker)
|
: Goal(worker, DerivedPath::Opaque { storePath })
|
||||||
, storePath(storePath)
|
, storePath(storePath)
|
||||||
, repair(repair)
|
, repair(repair)
|
||||||
, ca(ca)
|
, ca(ca)
|
||||||
|
@ -24,6 +24,13 @@ PathSubstitutionGoal::~PathSubstitutionGoal()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void PathSubstitutionGoal::done(ExitCode result, BuildResult::Status status)
|
||||||
|
{
|
||||||
|
buildResult.status = status;
|
||||||
|
amDone(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
void PathSubstitutionGoal::work()
|
void PathSubstitutionGoal::work()
|
||||||
{
|
{
|
||||||
(this->*state)();
|
(this->*state)();
|
||||||
|
@ -38,7 +45,7 @@ void PathSubstitutionGoal::init()
|
||||||
|
|
||||||
/* If the path already exists we're done. */
|
/* If the path already exists we're done. */
|
||||||
if (!repair && worker.store.isValidPath(storePath)) {
|
if (!repair && worker.store.isValidPath(storePath)) {
|
||||||
amDone(ecSuccess);
|
done(ecSuccess, BuildResult::AlreadyValid);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -65,7 +72,7 @@ void PathSubstitutionGoal::tryNext()
|
||||||
/* Hack: don't indicate failure if there were no substituters.
|
/* Hack: don't indicate failure if there were no substituters.
|
||||||
In that case the calling derivation should just do a
|
In that case the calling derivation should just do a
|
||||||
build. */
|
build. */
|
||||||
amDone(substituterFailed ? ecFailed : ecNoSubstituters);
|
done(substituterFailed ? ecFailed : ecNoSubstituters, BuildResult::NoSubstituters);
|
||||||
|
|
||||||
if (substituterFailed) {
|
if (substituterFailed) {
|
||||||
worker.failedSubstitutions++;
|
worker.failedSubstitutions++;
|
||||||
|
@ -163,7 +170,9 @@ void PathSubstitutionGoal::referencesValid()
|
||||||
|
|
||||||
if (nrFailed > 0) {
|
if (nrFailed > 0) {
|
||||||
debug("some references of path '%s' could not be realised", worker.store.printStorePath(storePath));
|
debug("some references of path '%s' could not be realised", worker.store.printStorePath(storePath));
|
||||||
amDone(nrNoSubstituters > 0 || nrIncompleteClosure > 0 ? ecIncompleteClosure : ecFailed);
|
done(
|
||||||
|
nrNoSubstituters > 0 || nrIncompleteClosure > 0 ? ecIncompleteClosure : ecFailed,
|
||||||
|
BuildResult::DependencyFailed);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -268,7 +277,7 @@ void PathSubstitutionGoal::finished()
|
||||||
|
|
||||||
worker.updateProgress();
|
worker.updateProgress();
|
||||||
|
|
||||||
amDone(ecSuccess);
|
done(ecSuccess, BuildResult::Substituted);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -53,6 +53,8 @@ struct PathSubstitutionGoal : public Goal
|
||||||
/* Content address for recomputing store path */
|
/* Content address for recomputing store path */
|
||||||
std::optional<ContentAddress> ca;
|
std::optional<ContentAddress> ca;
|
||||||
|
|
||||||
|
void done(ExitCode result, BuildResult::Status status);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
PathSubstitutionGoal(const StorePath & storePath, Worker & worker, RepairFlag repair = NoRepair, std::optional<ContentAddress> ca = std::nullopt);
|
PathSubstitutionGoal(const StorePath & storePath, Worker & worker, RepairFlag repair = NoRepair, std::optional<ContentAddress> ca = std::nullopt);
|
||||||
~PathSubstitutionGoal();
|
~PathSubstitutionGoal();
|
||||||
|
|
|
@ -532,6 +532,25 @@ static void performOp(TunnelLogger * logger, ref<Store> store,
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
case wopBuildPathsWithResults: {
|
||||||
|
auto drvs = readDerivedPaths(*store, clientVersion, from);
|
||||||
|
BuildMode mode = bmNormal;
|
||||||
|
mode = (BuildMode) readInt(from);
|
||||||
|
|
||||||
|
/* Repairing is not atomic, so disallowed for "untrusted"
|
||||||
|
clients. */
|
||||||
|
if (mode == bmRepair && !trusted)
|
||||||
|
throw Error("repairing is not allowed because you are not in 'trusted-users'");
|
||||||
|
|
||||||
|
logger->startWork();
|
||||||
|
auto results = store->buildPathsWithResults(drvs, mode);
|
||||||
|
logger->stopWork();
|
||||||
|
|
||||||
|
worker_proto::write(*store, to, results);
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
case wopBuildDerivation: {
|
case wopBuildDerivation: {
|
||||||
auto drvPath = store->parseStorePath(readString(from));
|
auto drvPath = store->parseStorePath(readString(from));
|
||||||
BasicDerivation drv;
|
BasicDerivation drv;
|
||||||
|
|
|
@ -279,7 +279,7 @@ public:
|
||||||
|
|
||||||
conn->to.flush();
|
conn->to.flush();
|
||||||
|
|
||||||
BuildResult status;
|
BuildResult status { .path = DerivedPath::Built { .drvPath = drvPath } };
|
||||||
status.status = (BuildResult::Status) readInt(conn->from);
|
status.status = (BuildResult::Status) readInt(conn->from);
|
||||||
conn->from >> status.errorMsg;
|
conn->from >> status.errorMsg;
|
||||||
|
|
||||||
|
@ -317,7 +317,7 @@ public:
|
||||||
|
|
||||||
conn->to.flush();
|
conn->to.flush();
|
||||||
|
|
||||||
BuildResult result;
|
BuildResult result { .path = DerivedPath::Opaque { StorePath::dummy } };
|
||||||
result.status = (BuildResult::Status) readInt(conn->from);
|
result.status = (BuildResult::Status) readInt(conn->from);
|
||||||
|
|
||||||
if (!result.success()) {
|
if (!result.success()) {
|
||||||
|
|
|
@ -91,6 +91,35 @@ void write(const Store & store, Sink & out, const DrvOutput & drvOutput)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
BuildResult read(const Store & store, Source & from, Phantom<BuildResult> _)
|
||||||
|
{
|
||||||
|
auto path = worker_proto::read(store, from, Phantom<DerivedPath> {});
|
||||||
|
BuildResult res { .path = path };
|
||||||
|
res.status = (BuildResult::Status) readInt(from);
|
||||||
|
from
|
||||||
|
>> res.errorMsg
|
||||||
|
>> res.timesBuilt
|
||||||
|
>> res.isNonDeterministic
|
||||||
|
>> res.startTime
|
||||||
|
>> res.stopTime;
|
||||||
|
res.builtOutputs = worker_proto::read(store, from, Phantom<DrvOutputs> {});
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
void write(const Store & store, Sink & to, const BuildResult & res)
|
||||||
|
{
|
||||||
|
worker_proto::write(store, to, res.path);
|
||||||
|
to
|
||||||
|
<< res.status
|
||||||
|
<< res.errorMsg
|
||||||
|
<< res.timesBuilt
|
||||||
|
<< res.isNonDeterministic
|
||||||
|
<< res.startTime
|
||||||
|
<< res.stopTime;
|
||||||
|
worker_proto::write(store, to, res.builtOutputs);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
std::optional<StorePath> read(const Store & store, Source & from, Phantom<std::optional<StorePath>> _)
|
std::optional<StorePath> read(const Store & store, Source & from, Phantom<std::optional<StorePath>> _)
|
||||||
{
|
{
|
||||||
auto s = readString(from);
|
auto s = readString(from);
|
||||||
|
@ -747,17 +776,24 @@ static void writeDerivedPaths(RemoteStore & store, ConnectionHandle & conn, cons
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void RemoteStore::buildPaths(const std::vector<DerivedPath> & drvPaths, BuildMode buildMode, std::shared_ptr<Store> evalStore)
|
void RemoteStore::copyDrvsFromEvalStore(
|
||||||
|
const std::vector<DerivedPath> & paths,
|
||||||
|
std::shared_ptr<Store> evalStore)
|
||||||
{
|
{
|
||||||
if (evalStore && evalStore.get() != this) {
|
if (evalStore && evalStore.get() != this) {
|
||||||
/* The remote doesn't have a way to access evalStore, so copy
|
/* The remote doesn't have a way to access evalStore, so copy
|
||||||
the .drvs. */
|
the .drvs. */
|
||||||
RealisedPath::Set drvPaths2;
|
RealisedPath::Set drvPaths2;
|
||||||
for (auto & i : drvPaths)
|
for (auto & i : paths)
|
||||||
if (auto p = std::get_if<DerivedPath::Built>(&i))
|
if (auto p = std::get_if<DerivedPath::Built>(&i))
|
||||||
drvPaths2.insert(p->drvPath);
|
drvPaths2.insert(p->drvPath);
|
||||||
copyClosure(*evalStore, *this, drvPaths2);
|
copyClosure(*evalStore, *this, drvPaths2);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void RemoteStore::buildPaths(const std::vector<DerivedPath> & drvPaths, BuildMode buildMode, std::shared_ptr<Store> evalStore)
|
||||||
|
{
|
||||||
|
copyDrvsFromEvalStore(drvPaths, evalStore);
|
||||||
|
|
||||||
auto conn(getConnection());
|
auto conn(getConnection());
|
||||||
conn->to << wopBuildPaths;
|
conn->to << wopBuildPaths;
|
||||||
|
@ -774,6 +810,91 @@ void RemoteStore::buildPaths(const std::vector<DerivedPath> & drvPaths, BuildMod
|
||||||
readInt(conn->from);
|
readInt(conn->from);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::vector<BuildResult> RemoteStore::buildPathsWithResults(
|
||||||
|
const std::vector<DerivedPath> & paths,
|
||||||
|
BuildMode buildMode,
|
||||||
|
std::shared_ptr<Store> evalStore)
|
||||||
|
{
|
||||||
|
copyDrvsFromEvalStore(paths, evalStore);
|
||||||
|
|
||||||
|
std::optional<ConnectionHandle> conn_(getConnection());
|
||||||
|
auto & conn = *conn_;
|
||||||
|
|
||||||
|
if (GET_PROTOCOL_MINOR(conn->daemonVersion) >= 34) {
|
||||||
|
conn->to << wopBuildPathsWithResults;
|
||||||
|
writeDerivedPaths(*this, conn, paths);
|
||||||
|
conn->to << buildMode;
|
||||||
|
conn.processStderr();
|
||||||
|
return worker_proto::read(*this, conn->from, Phantom<std::vector<BuildResult>> {});
|
||||||
|
} else {
|
||||||
|
// Avoid deadlock.
|
||||||
|
conn_.reset();
|
||||||
|
|
||||||
|
// Note: this throws an exception if a build/substitution
|
||||||
|
// fails, but meh.
|
||||||
|
buildPaths(paths, buildMode, evalStore);
|
||||||
|
|
||||||
|
std::vector<BuildResult> results;
|
||||||
|
|
||||||
|
for (auto & path : paths) {
|
||||||
|
std::visit(
|
||||||
|
overloaded {
|
||||||
|
[&](const DerivedPath::Opaque & bo) {
|
||||||
|
results.push_back(BuildResult {
|
||||||
|
.status = BuildResult::Substituted,
|
||||||
|
.path = bo,
|
||||||
|
});
|
||||||
|
},
|
||||||
|
[&](const DerivedPath::Built & bfd) {
|
||||||
|
BuildResult res {
|
||||||
|
.status = BuildResult::Built,
|
||||||
|
.path = bfd,
|
||||||
|
};
|
||||||
|
|
||||||
|
OutputPathMap outputs;
|
||||||
|
auto drv = evalStore->readDerivation(bfd.drvPath);
|
||||||
|
auto outputHashes = staticOutputHashes(*evalStore, drv); // FIXME: expensive
|
||||||
|
auto drvOutputs = drv.outputsAndOptPaths(*this);
|
||||||
|
for (auto & output : bfd.outputs) {
|
||||||
|
if (!outputHashes.count(output))
|
||||||
|
throw Error(
|
||||||
|
"the derivation '%s' doesn't have an output named '%s'",
|
||||||
|
printStorePath(bfd.drvPath), output);
|
||||||
|
auto outputId =
|
||||||
|
DrvOutput{outputHashes.at(output), output};
|
||||||
|
if (settings.isExperimentalFeatureEnabled(Xp::CaDerivations)) {
|
||||||
|
auto realisation =
|
||||||
|
queryRealisation(outputId);
|
||||||
|
if (!realisation)
|
||||||
|
throw Error(
|
||||||
|
"cannot operate on an output of unbuilt "
|
||||||
|
"content-addressed derivation '%s'",
|
||||||
|
outputId.to_string());
|
||||||
|
res.builtOutputs.emplace(realisation->id, *realisation);
|
||||||
|
} else {
|
||||||
|
// If ca-derivations isn't enabled, assume that
|
||||||
|
// the output path is statically known.
|
||||||
|
assert(drvOutputs.count(output));
|
||||||
|
assert(drvOutputs.at(output).second);
|
||||||
|
res.builtOutputs.emplace(
|
||||||
|
outputId,
|
||||||
|
Realisation {
|
||||||
|
.id = outputId,
|
||||||
|
.outPath = *drvOutputs.at(output).second
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
results.push_back(res);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
path.raw());
|
||||||
|
}
|
||||||
|
|
||||||
|
return results;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
BuildResult RemoteStore::buildDerivation(const StorePath & drvPath, const BasicDerivation & drv,
|
BuildResult RemoteStore::buildDerivation(const StorePath & drvPath, const BasicDerivation & drv,
|
||||||
BuildMode buildMode)
|
BuildMode buildMode)
|
||||||
|
@ -783,7 +904,7 @@ BuildResult RemoteStore::buildDerivation(const StorePath & drvPath, const BasicD
|
||||||
writeDerivation(conn->to, *this, drv);
|
writeDerivation(conn->to, *this, drv);
|
||||||
conn->to << buildMode;
|
conn->to << buildMode;
|
||||||
conn.processStderr();
|
conn.processStderr();
|
||||||
BuildResult res;
|
BuildResult res { .path = DerivedPath::Built { .drvPath = drvPath } };
|
||||||
res.status = (BuildResult::Status) readInt(conn->from);
|
res.status = (BuildResult::Status) readInt(conn->from);
|
||||||
conn->from >> res.errorMsg;
|
conn->from >> res.errorMsg;
|
||||||
if (GET_PROTOCOL_MINOR(conn->daemonVersion) >= 29) {
|
if (GET_PROTOCOL_MINOR(conn->daemonVersion) >= 29) {
|
||||||
|
|
|
@ -97,6 +97,11 @@ public:
|
||||||
|
|
||||||
void buildPaths(const std::vector<DerivedPath> & paths, BuildMode buildMode, std::shared_ptr<Store> evalStore) override;
|
void buildPaths(const std::vector<DerivedPath> & paths, BuildMode buildMode, std::shared_ptr<Store> evalStore) override;
|
||||||
|
|
||||||
|
std::vector<BuildResult> buildPathsWithResults(
|
||||||
|
const std::vector<DerivedPath> & paths,
|
||||||
|
BuildMode buildMode,
|
||||||
|
std::shared_ptr<Store> evalStore) override;
|
||||||
|
|
||||||
BuildResult buildDerivation(const StorePath & drvPath, const BasicDerivation & drv,
|
BuildResult buildDerivation(const StorePath & drvPath, const BasicDerivation & drv,
|
||||||
BuildMode buildMode) override;
|
BuildMode buildMode) override;
|
||||||
|
|
||||||
|
@ -171,6 +176,9 @@ private:
|
||||||
|
|
||||||
std::atomic_bool failed{false};
|
std::atomic_bool failed{false};
|
||||||
|
|
||||||
|
void copyDrvsFromEvalStore(
|
||||||
|
const std::vector<DerivedPath> & paths,
|
||||||
|
std::shared_ptr<Store> evalStore);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -432,6 +432,16 @@ public:
|
||||||
BuildMode buildMode = bmNormal,
|
BuildMode buildMode = bmNormal,
|
||||||
std::shared_ptr<Store> evalStore = nullptr);
|
std::shared_ptr<Store> evalStore = nullptr);
|
||||||
|
|
||||||
|
/* Like `buildPaths()`, but return a vector of `BuildResult`s
|
||||||
|
corresponding to each element in `paths`. Note that in case of
|
||||||
|
a build/substitution error, this function won't throw an
|
||||||
|
exception, but return a `BuildResult` containing an error
|
||||||
|
message. */
|
||||||
|
virtual std::vector<BuildResult> buildPathsWithResults(
|
||||||
|
const std::vector<DerivedPath> & paths,
|
||||||
|
BuildMode buildMode = bmNormal,
|
||||||
|
std::shared_ptr<Store> evalStore = nullptr);
|
||||||
|
|
||||||
/* Build a single non-materialized derivation (i.e. not from an
|
/* Build a single non-materialized derivation (i.e. not from an
|
||||||
on-disk .drv file).
|
on-disk .drv file).
|
||||||
|
|
||||||
|
|
|
@ -9,7 +9,7 @@ namespace nix {
|
||||||
#define WORKER_MAGIC_1 0x6e697863
|
#define WORKER_MAGIC_1 0x6e697863
|
||||||
#define WORKER_MAGIC_2 0x6478696f
|
#define WORKER_MAGIC_2 0x6478696f
|
||||||
|
|
||||||
#define PROTOCOL_VERSION (1 << 8 | 33)
|
#define PROTOCOL_VERSION (1 << 8 | 34)
|
||||||
#define GET_PROTOCOL_MAJOR(x) ((x) & 0xff00)
|
#define GET_PROTOCOL_MAJOR(x) ((x) & 0xff00)
|
||||||
#define GET_PROTOCOL_MINOR(x) ((x) & 0x00ff)
|
#define GET_PROTOCOL_MINOR(x) ((x) & 0x00ff)
|
||||||
|
|
||||||
|
@ -57,6 +57,7 @@ typedef enum {
|
||||||
wopQueryRealisation = 43,
|
wopQueryRealisation = 43,
|
||||||
wopAddMultipleToStore = 44,
|
wopAddMultipleToStore = 44,
|
||||||
wopAddBuildLog = 45,
|
wopAddBuildLog = 45,
|
||||||
|
wopBuildPathsWithResults = 46,
|
||||||
} WorkerOp;
|
} WorkerOp;
|
||||||
|
|
||||||
|
|
||||||
|
@ -91,6 +92,7 @@ MAKE_WORKER_PROTO(, ContentAddress);
|
||||||
MAKE_WORKER_PROTO(, DerivedPath);
|
MAKE_WORKER_PROTO(, DerivedPath);
|
||||||
MAKE_WORKER_PROTO(, Realisation);
|
MAKE_WORKER_PROTO(, Realisation);
|
||||||
MAKE_WORKER_PROTO(, DrvOutput);
|
MAKE_WORKER_PROTO(, DrvOutput);
|
||||||
|
MAKE_WORKER_PROTO(, BuildResult);
|
||||||
|
|
||||||
MAKE_WORKER_PROTO(template<typename T>, std::vector<T>);
|
MAKE_WORKER_PROTO(template<typename T>, std::vector<T>);
|
||||||
MAKE_WORKER_PROTO(template<typename T>, std::set<T>);
|
MAKE_WORKER_PROTO(template<typename T>, std::set<T>);
|
||||||
|
|
Loading…
Reference in a new issue