Fix tests on systems with a non-master git defaultBranch #1

Open
zebreus wants to merge 140 commits from fix-tests-without-master into main
15 changed files with 225 additions and 135 deletions
Showing only changes of commit f2a49032a6 - Show all commits

View file

@ -229,6 +229,7 @@ configdata += {
} }
boost = dependency('boost', required : true, modules : ['container'], include_type : 'system') boost = dependency('boost', required : true, modules : ['container'], include_type : 'system')
kj = dependency('kj-async', required : true, include_type : 'system')
# cpuid only makes sense on x86_64 # cpuid only makes sense on x86_64
cpuid_required = is_x64 ? get_option('cpuid') : false cpuid_required = is_x64 ? get_option('cpuid') : false

View file

@ -15,6 +15,8 @@
brotli, brotli,
bzip2, bzip2,
callPackage, callPackage,
capnproto-lix ? __forDefaults.capnproto-lix,
capnproto,
cmake, cmake,
curl, curl,
doxygen, doxygen,
@ -83,6 +85,9 @@
}); });
build-release-notes = callPackage ./maintainers/build-release-notes.nix { }; build-release-notes = callPackage ./maintainers/build-release-notes.nix { };
# needs explicit c++20 to enable coroutine support
capnproto-lix = capnproto.overrideAttrs { CXXFLAGS = "-std=c++20"; };
}, },
}: }:
let let
@ -220,6 +225,7 @@ stdenv.mkDerivation (finalAttrs: {
ninja ninja
cmake cmake
rustc rustc
capnproto-lix
] ]
++ [ ++ [
(lib.getBin lowdown) (lib.getBin lowdown)
@ -260,6 +266,7 @@ stdenv.mkDerivation (finalAttrs: {
libsodium libsodium
toml11 toml11
pegtl pegtl
capnproto-lix
] ]
++ lib.optionals hostPlatform.isLinux [ ++ lib.optionals hostPlatform.isLinux [
libseccomp libseccomp

View file

@ -131,7 +131,7 @@ Goal::Finished DerivationGoal::timedOut(Error && ex)
} }
Goal::WorkResult DerivationGoal::work(bool inBuildSlot) kj::Promise<Result<Goal::WorkResult>> DerivationGoal::work(bool inBuildSlot) noexcept
{ {
return (this->*state)(inBuildSlot); return (this->*state)(inBuildSlot);
} }
@ -157,8 +157,8 @@ void DerivationGoal::addWantedOutputs(const OutputsSpec & outputs)
} }
Goal::WorkResult DerivationGoal::getDerivation(bool inBuildSlot) kj::Promise<Result<Goal::WorkResult>> DerivationGoal::getDerivation(bool inBuildSlot) noexcept
{ try {
trace("init"); trace("init");
/* The first thing to do is to make sure that the derivation /* The first thing to do is to make sure that the derivation
@ -170,16 +170,22 @@ Goal::WorkResult DerivationGoal::getDerivation(bool inBuildSlot)
state = &DerivationGoal::loadDerivation; state = &DerivationGoal::loadDerivation;
return WaitForGoals{{worker.goalFactory().makePathSubstitutionGoal(drvPath)}}; return {WaitForGoals{{worker.goalFactory().makePathSubstitutionGoal(drvPath)}}};
} catch (...) {
return {std::current_exception()};
} }
Goal::WorkResult DerivationGoal::loadDerivation(bool inBuildSlot) kj::Promise<Result<Goal::WorkResult>> DerivationGoal::loadDerivation(bool inBuildSlot) noexcept
{ try {
trace("loading derivation"); trace("loading derivation");
if (nrFailed != 0) { if (nrFailed != 0) {
return done(BuildResult::MiscFailure, {}, Error("cannot build missing derivation '%s'", worker.store.printStorePath(drvPath))); return {done(
BuildResult::MiscFailure,
{},
Error("cannot build missing derivation '%s'", worker.store.printStorePath(drvPath))
)};
} }
/* `drvPath' should already be a root, but let's be on the safe /* `drvPath' should already be a root, but let's be on the safe
@ -202,11 +208,13 @@ Goal::WorkResult DerivationGoal::loadDerivation(bool inBuildSlot)
assert(drv); assert(drv);
return haveDerivation(inBuildSlot); return haveDerivation(inBuildSlot);
} catch (...) {
return {std::current_exception()};
} }
Goal::WorkResult DerivationGoal::haveDerivation(bool inBuildSlot) kj::Promise<Result<Goal::WorkResult>> DerivationGoal::haveDerivation(bool inBuildSlot) noexcept
{ try {
trace("have derivation"); trace("have derivation");
parsedDrv = std::make_unique<ParsedDerivation>(drvPath, *drv); parsedDrv = std::make_unique<ParsedDerivation>(drvPath, *drv);
@ -255,7 +263,7 @@ Goal::WorkResult DerivationGoal::haveDerivation(bool inBuildSlot)
/* 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) {
return done(BuildResult::AlreadyValid, std::move(validOutputs)); return {done(BuildResult::AlreadyValid, std::move(validOutputs))};
} }
/* We are first going to try to create the invalid output paths /* We are first going to try to create the invalid output paths
@ -290,20 +298,29 @@ Goal::WorkResult DerivationGoal::haveDerivation(bool inBuildSlot)
return outputsSubstitutionTried(inBuildSlot); return outputsSubstitutionTried(inBuildSlot);
} else { } else {
state = &DerivationGoal::outputsSubstitutionTried; state = &DerivationGoal::outputsSubstitutionTried;
return result; return {std::move(result)};
} }
} catch (...) {
return {std::current_exception()};
} }
Goal::WorkResult DerivationGoal::outputsSubstitutionTried(bool inBuildSlot) kj::Promise<Result<Goal::WorkResult>> DerivationGoal::outputsSubstitutionTried(bool inBuildSlot) noexcept
{ try {
trace("all outputs substituted (maybe)"); trace("all outputs substituted (maybe)");
assert(drv->type().isPure()); assert(drv->type().isPure());
if (nrFailed > 0 && nrFailed > nrNoSubstituters + nrIncompleteClosure && !settings.tryFallback) { if (nrFailed > 0 && nrFailed > nrNoSubstituters + nrIncompleteClosure && !settings.tryFallback)
return 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 ", return {done(
worker.store.printStorePath(drvPath))); 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)
)
)};
} }
/* If the substitutes form an incomplete closure, then we should /* If the substitutes form an incomplete closure, then we should
@ -343,7 +360,7 @@ Goal::WorkResult DerivationGoal::outputsSubstitutionTried(bool inBuildSlot)
auto [allValid, validOutputs] = checkPathValidity(); auto [allValid, validOutputs] = checkPathValidity();
if (buildMode == bmNormal && allValid) { if (buildMode == bmNormal && allValid) {
return done(BuildResult::Substituted, std::move(validOutputs)); return {done(BuildResult::Substituted, std::move(validOutputs))};
} }
if (buildMode == bmRepair && allValid) { if (buildMode == bmRepair && allValid) {
return repairClosure(); return repairClosure();
@ -354,13 +371,15 @@ Goal::WorkResult DerivationGoal::outputsSubstitutionTried(bool inBuildSlot)
/* Nothing to wait for; tail call */ /* Nothing to wait for; tail call */
return gaveUpOnSubstitution(inBuildSlot); return gaveUpOnSubstitution(inBuildSlot);
} catch (...) {
return {std::current_exception()};
} }
/* At least one of the output paths could not be /* At least one of the output paths could not be
produced using a substitute. So we have to build instead. */ produced using a substitute. So we have to build instead. */
Goal::WorkResult DerivationGoal::gaveUpOnSubstitution(bool inBuildSlot) kj::Promise<Result<Goal::WorkResult>> DerivationGoal::gaveUpOnSubstitution(bool inBuildSlot) noexcept
{ try {
WaitForGoals result; WaitForGoals result;
/* At this point we are building all outputs, so if more are wanted there /* At this point we are building all outputs, so if more are wanted there
@ -426,13 +445,15 @@ Goal::WorkResult DerivationGoal::gaveUpOnSubstitution(bool inBuildSlot)
return inputsRealised(inBuildSlot); return inputsRealised(inBuildSlot);
} else { } else {
state = &DerivationGoal::inputsRealised; state = &DerivationGoal::inputsRealised;
return result; return {result};
} }
} catch (...) {
return {std::current_exception()};
} }
Goal::WorkResult DerivationGoal::repairClosure() kj::Promise<Result<Goal::WorkResult>> DerivationGoal::repairClosure() noexcept
{ try {
assert(drv->type().isPure()); assert(drv->type().isPure());
/* If we're repairing, we now know that our own outputs are valid. /* If we're repairing, we now know that our own outputs are valid.
@ -486,34 +507,44 @@ Goal::WorkResult DerivationGoal::repairClosure()
} }
if (result.goals.empty()) { if (result.goals.empty()) {
return done(BuildResult::AlreadyValid, assertPathValidity()); return {done(BuildResult::AlreadyValid, assertPathValidity())};
} }
state = &DerivationGoal::closureRepaired; state = &DerivationGoal::closureRepaired;
return result; return {result};
} catch (...) {
return {std::current_exception()};
} }
Goal::WorkResult DerivationGoal::closureRepaired(bool inBuildSlot) kj::Promise<Result<Goal::WorkResult>> DerivationGoal::closureRepaired(bool inBuildSlot) noexcept
{ try {
trace("closure repaired"); trace("closure repaired");
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));
return done(BuildResult::AlreadyValid, assertPathValidity()); return {done(BuildResult::AlreadyValid, assertPathValidity())};
} catch (...) {
return {std::current_exception()};
} }
Goal::WorkResult DerivationGoal::inputsRealised(bool inBuildSlot) kj::Promise<Result<Goal::WorkResult>> DerivationGoal::inputsRealised(bool inBuildSlot) noexcept
{ try {
trace("all inputs realised"); trace("all inputs realised");
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));
return done(BuildResult::DependencyFailed, {}, Error( return {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)
)
)};
} }
if (retrySubstitution == RetrySubstitution::YesNeed) { if (retrySubstitution == RetrySubstitution::YesNeed) {
@ -584,7 +615,7 @@ Goal::WorkResult DerivationGoal::inputsRealised(bool inBuildSlot)
pathResolved, wantedOutputs, buildMode); pathResolved, wantedOutputs, buildMode);
state = &DerivationGoal::resolvedFinished; state = &DerivationGoal::resolvedFinished;
return WaitForGoals{{resolvedDrvGoal}}; return {WaitForGoals{{resolvedDrvGoal}}};
} }
std::function<void(const StorePath &, const DerivedPathMap<StringSet>::ChildNode &)> accumInputPaths; std::function<void(const StorePath &, const DerivedPathMap<StringSet>::ChildNode &)> accumInputPaths;
@ -650,6 +681,8 @@ Goal::WorkResult DerivationGoal::inputsRealised(bool inBuildSlot)
build hook. */ build hook. */
state = &DerivationGoal::tryToBuild; state = &DerivationGoal::tryToBuild;
return tryToBuild(inBuildSlot); return tryToBuild(inBuildSlot);
} catch (...) {
return {std::current_exception()};
} }
void DerivationGoal::started() void DerivationGoal::started()
@ -665,8 +698,8 @@ void DerivationGoal::started()
mcRunningBuilds = worker.runningBuilds.addTemporarily(1); mcRunningBuilds = worker.runningBuilds.addTemporarily(1);
} }
Goal::WorkResult DerivationGoal::tryToBuild(bool inBuildSlot) kj::Promise<Result<Goal::WorkResult>> DerivationGoal::tryToBuild(bool inBuildSlot) noexcept
{ try {
trace("trying to build"); trace("trying to build");
/* Obtain locks on all output paths, if the paths are known a priori. /* Obtain locks on all output paths, if the paths are known a priori.
@ -700,7 +733,7 @@ Goal::WorkResult DerivationGoal::tryToBuild(bool inBuildSlot)
if (!actLock) if (!actLock)
actLock = std::make_unique<Activity>(*logger, lvlWarn, actBuildWaiting, actLock = std::make_unique<Activity>(*logger, lvlWarn, actBuildWaiting,
fmt("waiting for lock on %s", Magenta(showPaths(lockFiles)))); fmt("waiting for lock on %s", Magenta(showPaths(lockFiles))));
return WaitForAWhile{}; return {WaitForAWhile{}};
} }
actLock.reset(); actLock.reset();
@ -717,7 +750,7 @@ Goal::WorkResult DerivationGoal::tryToBuild(bool inBuildSlot)
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);
return done(BuildResult::AlreadyValid, std::move(validOutputs)); return {done(BuildResult::AlreadyValid, std::move(validOutputs))};
} }
/* If any of the outputs already exist but are not valid, delete /* If any of the outputs already exist but are not valid, delete
@ -765,7 +798,7 @@ Goal::WorkResult DerivationGoal::tryToBuild(bool inBuildSlot)
}, },
hookReply); hookReply);
if (result) { if (result) {
return std::move(*result); return {std::move(*result)};
} }
} }
@ -773,13 +806,18 @@ Goal::WorkResult DerivationGoal::tryToBuild(bool inBuildSlot)
state = &DerivationGoal::tryLocalBuild; state = &DerivationGoal::tryLocalBuild;
return tryLocalBuild(inBuildSlot); return tryLocalBuild(inBuildSlot);
} catch (...) {
return {std::current_exception()};
} }
Goal::WorkResult DerivationGoal::tryLocalBuild(bool inBuildSlot) { kj::Promise<Result<Goal::WorkResult>> DerivationGoal::tryLocalBuild(bool inBuildSlot) noexcept
try {
throw Error( throw Error(
"unable to build with a primary store that isn't a local store; " "unable to build with a primary store that isn't a local store; "
"either pass a different '--store' or enable remote builds." "either pass a different '--store' or enable remote builds."
"\nhttps://docs.lix.systems/manual/lix/stable/advanced-topics/distributed-builds.html"); "\nhttps://docs.lix.systems/manual/lix/stable/advanced-topics/distributed-builds.html");
} catch (...) {
return {std::current_exception()};
} }
@ -935,8 +973,8 @@ void runPostBuildHook(
proc.getStdout()->drainInto(sink); proc.getStdout()->drainInto(sink);
} }
Goal::WorkResult DerivationGoal::buildDone(bool inBuildSlot) kj::Promise<Result<Goal::WorkResult>> DerivationGoal::buildDone(bool inBuildSlot) noexcept
{ try {
trace("build done"); trace("build done");
Finally releaseBuildUser([&](){ this->cleanupHookFinally(); }); Finally releaseBuildUser([&](){ this->cleanupHookFinally(); });
@ -1030,7 +1068,7 @@ Goal::WorkResult DerivationGoal::buildDone(bool inBuildSlot)
outputLocks.setDeletion(true); outputLocks.setDeletion(true);
outputLocks.unlock(); outputLocks.unlock();
return done(BuildResult::Built, std::move(builtOutputs)); return {done(BuildResult::Built, std::move(builtOutputs))};
} catch (BuildError & e) { } catch (BuildError & e) {
outputLocks.unlock(); outputLocks.unlock();
@ -1051,12 +1089,14 @@ Goal::WorkResult DerivationGoal::buildDone(bool inBuildSlot)
BuildResult::PermanentFailure; BuildResult::PermanentFailure;
} }
return done(st, {}, std::move(e)); return {done(st, {}, std::move(e))};
} }
} catch (...) {
return {std::current_exception()};
} }
Goal::WorkResult DerivationGoal::resolvedFinished(bool inBuildSlot) kj::Promise<Result<Goal::WorkResult>> DerivationGoal::resolvedFinished(bool inBuildSlot) noexcept
{ try {
trace("resolved derivation finished"); trace("resolved derivation finished");
assert(resolvedDrvGoal); assert(resolvedDrvGoal);
@ -1123,7 +1163,9 @@ Goal::WorkResult DerivationGoal::resolvedFinished(bool inBuildSlot)
if (status == BuildResult::AlreadyValid) if (status == BuildResult::AlreadyValid)
status = BuildResult::ResolvesToAlreadyValid; status = BuildResult::ResolvesToAlreadyValid;
return done(status, std::move(builtOutputs)); return {done(status, std::move(builtOutputs))};
} catch (...) {
return {std::current_exception()};
} }
HookReply DerivationGoal::tryBuildHook(bool inBuildSlot) HookReply DerivationGoal::tryBuildHook(bool inBuildSlot)

View file

@ -213,7 +213,7 @@ struct DerivationGoal : public Goal
*/ */
std::optional<DerivationType> derivationType; std::optional<DerivationType> derivationType;
typedef WorkResult (DerivationGoal::*GoalState)(bool inBuildSlot); typedef kj::Promise<Result<WorkResult>> (DerivationGoal::*GoalState)(bool inBuildSlot) noexcept;
GoalState state; GoalState state;
BuildMode buildMode; BuildMode buildMode;
@ -246,7 +246,7 @@ struct DerivationGoal : public Goal
std::string key() override; std::string key() override;
WorkResult work(bool inBuildSlot) override; kj::Promise<Result<WorkResult>> work(bool inBuildSlot) noexcept override;
/** /**
* Add wanted outputs to an already existing derivation goal. * Add wanted outputs to an already existing derivation goal.
@ -256,18 +256,18 @@ struct DerivationGoal : public Goal
/** /**
* The states. * The states.
*/ */
WorkResult getDerivation(bool inBuildSlot); kj::Promise<Result<WorkResult>> getDerivation(bool inBuildSlot) noexcept;
WorkResult loadDerivation(bool inBuildSlot); kj::Promise<Result<WorkResult>> loadDerivation(bool inBuildSlot) noexcept;
WorkResult haveDerivation(bool inBuildSlot); kj::Promise<Result<WorkResult>> haveDerivation(bool inBuildSlot) noexcept;
WorkResult outputsSubstitutionTried(bool inBuildSlot); kj::Promise<Result<WorkResult>> outputsSubstitutionTried(bool inBuildSlot) noexcept;
WorkResult gaveUpOnSubstitution(bool inBuildSlot); kj::Promise<Result<WorkResult>> gaveUpOnSubstitution(bool inBuildSlot) noexcept;
WorkResult closureRepaired(bool inBuildSlot); kj::Promise<Result<WorkResult>> closureRepaired(bool inBuildSlot) noexcept;
WorkResult inputsRealised(bool inBuildSlot); kj::Promise<Result<WorkResult>> inputsRealised(bool inBuildSlot) noexcept;
WorkResult tryToBuild(bool inBuildSlot); kj::Promise<Result<WorkResult>> tryToBuild(bool inBuildSlot) noexcept;
virtual WorkResult tryLocalBuild(bool inBuildSlot); virtual kj::Promise<Result<WorkResult>> tryLocalBuild(bool inBuildSlot) noexcept;
WorkResult buildDone(bool inBuildSlot); kj::Promise<Result<WorkResult>> buildDone(bool inBuildSlot) noexcept;
WorkResult resolvedFinished(bool inBuildSlot); kj::Promise<Result<WorkResult>> resolvedFinished(bool inBuildSlot) noexcept;
/** /**
* Is the build hook willing to perform the build? * Is the build hook willing to perform the build?
@ -346,7 +346,7 @@ struct DerivationGoal : public Goal
*/ */
virtual void killChild(); virtual void killChild();
WorkResult repairClosure(); kj::Promise<Result<WorkResult>> repairClosure() noexcept;
void started(); void started();

View file

@ -22,25 +22,27 @@ DrvOutputSubstitutionGoal::DrvOutputSubstitutionGoal(
} }
Goal::WorkResult DrvOutputSubstitutionGoal::init(bool inBuildSlot) kj::Promise<Result<Goal::WorkResult>> DrvOutputSubstitutionGoal::init(bool inBuildSlot) noexcept
{ try {
trace("init"); trace("init");
/* If the derivation already exists, were done */ /* If the derivation already exists, were done */
if (worker.store.queryRealisation(id)) { if (worker.store.queryRealisation(id)) {
return Finished{ecSuccess, std::move(buildResult)}; return {Finished{ecSuccess, std::move(buildResult)}};
} }
subs = settings.useSubstitutes ? getDefaultSubstituters() : std::list<ref<Store>>(); subs = settings.useSubstitutes ? getDefaultSubstituters() : std::list<ref<Store>>();
return tryNext(inBuildSlot); return tryNext(inBuildSlot);
} catch (...) {
return {std::current_exception()};
} }
Goal::WorkResult DrvOutputSubstitutionGoal::tryNext(bool inBuildSlot) kj::Promise<Result<Goal::WorkResult>> DrvOutputSubstitutionGoal::tryNext(bool inBuildSlot) noexcept
{ try {
trace("trying next substituter"); trace("trying next substituter");
if (!inBuildSlot) { if (!inBuildSlot) {
return WaitForSlot{}; return {WaitForSlot{}};
} }
maintainRunningSubstitutions = worker.runningSubstitutions.addTemporarily(1); maintainRunningSubstitutions = worker.runningSubstitutions.addTemporarily(1);
@ -57,7 +59,7 @@ Goal::WorkResult DrvOutputSubstitutionGoal::tryNext(bool inBuildSlot)
/* 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. */
return Finished{substituterFailed ? ecFailed : ecNoSubstituters, std::move(buildResult)}; return {Finished{substituterFailed ? ecFailed : ecNoSubstituters, std::move(buildResult)}};
} }
sub = subs.front(); sub = subs.front();
@ -77,11 +79,13 @@ Goal::WorkResult DrvOutputSubstitutionGoal::tryNext(bool inBuildSlot)
}); });
state = &DrvOutputSubstitutionGoal::realisationFetched; state = &DrvOutputSubstitutionGoal::realisationFetched;
return WaitForWorld{{downloadState->outPipe.readSide.get()}, true}; return {WaitForWorld{{downloadState->outPipe.readSide.get()}, true}};
} catch (...) {
return {std::current_exception()};
} }
Goal::WorkResult DrvOutputSubstitutionGoal::realisationFetched(bool inBuildSlot) kj::Promise<Result<Goal::WorkResult>> DrvOutputSubstitutionGoal::realisationFetched(bool inBuildSlot) noexcept
{ try {
worker.childTerminated(this); worker.childTerminated(this);
maintainRunningSubstitutions.reset(); maintainRunningSubstitutions.reset();
@ -122,31 +126,37 @@ Goal::WorkResult DrvOutputSubstitutionGoal::realisationFetched(bool inBuildSlot)
return outPathValid(inBuildSlot); return outPathValid(inBuildSlot);
} else { } else {
state = &DrvOutputSubstitutionGoal::outPathValid; state = &DrvOutputSubstitutionGoal::outPathValid;
return result; return {std::move(result)};
} }
} catch (...) {
return {std::current_exception()};
} }
Goal::WorkResult DrvOutputSubstitutionGoal::outPathValid(bool inBuildSlot) kj::Promise<Result<Goal::WorkResult>> DrvOutputSubstitutionGoal::outPathValid(bool inBuildSlot) noexcept
{ try {
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());
return Finished{ return {Finished{
nrNoSubstituters > 0 || nrIncompleteClosure > 0 ? ecIncompleteClosure : ecFailed, nrNoSubstituters > 0 || nrIncompleteClosure > 0 ? ecIncompleteClosure : ecFailed,
std::move(buildResult), std::move(buildResult),
}; }};
} }
worker.store.registerDrvOutput(*outputInfo); worker.store.registerDrvOutput(*outputInfo);
return finished(); return finished();
} catch (...) {
return {std::current_exception()};
} }
Goal::WorkResult DrvOutputSubstitutionGoal::finished() kj::Promise<Result<Goal::WorkResult>> DrvOutputSubstitutionGoal::finished() noexcept
{ try {
trace("finished"); trace("finished");
return Finished{ecSuccess, std::move(buildResult)}; return {Finished{ecSuccess, std::move(buildResult)}};
} catch (...) {
return {std::current_exception()};
} }
std::string DrvOutputSubstitutionGoal::key() std::string DrvOutputSubstitutionGoal::key()
@ -156,7 +166,7 @@ std::string DrvOutputSubstitutionGoal::key()
return "a$" + std::string(id.to_string()); return "a$" + std::string(id.to_string());
} }
Goal::WorkResult DrvOutputSubstitutionGoal::work(bool inBuildSlot) kj::Promise<Result<Goal::WorkResult>> DrvOutputSubstitutionGoal::work(bool inBuildSlot) noexcept
{ {
return (this->*state)(inBuildSlot); return (this->*state)(inBuildSlot);
} }

View file

@ -65,20 +65,20 @@ public:
std::optional<ContentAddress> ca = std::nullopt std::optional<ContentAddress> ca = std::nullopt
); );
typedef WorkResult (DrvOutputSubstitutionGoal::*GoalState)(bool inBuildSlot); typedef kj::Promise<Result<WorkResult>> (DrvOutputSubstitutionGoal::*GoalState)(bool inBuildSlot) noexcept;
GoalState state; GoalState state;
WorkResult init(bool inBuildSlot); kj::Promise<Result<WorkResult>> init(bool inBuildSlot) noexcept;
WorkResult tryNext(bool inBuildSlot); kj::Promise<Result<WorkResult>> tryNext(bool inBuildSlot) noexcept;
WorkResult realisationFetched(bool inBuildSlot); kj::Promise<Result<WorkResult>> realisationFetched(bool inBuildSlot) noexcept;
WorkResult outPathValid(bool inBuildSlot); kj::Promise<Result<WorkResult>> outPathValid(bool inBuildSlot) noexcept;
WorkResult finished(); kj::Promise<Result<WorkResult>> finished() noexcept;
Finished timedOut(Error && ex) override { abort(); }; Finished timedOut(Error && ex) override { abort(); };
std::string key() override; std::string key() override;
WorkResult work(bool inBuildSlot) override; kj::Promise<Result<WorkResult>> work(bool inBuildSlot) noexcept override;
JobCategory jobCategory() const override { JobCategory jobCategory() const override {
return JobCategory::Substitution; return JobCategory::Substitution;

View file

@ -6,11 +6,17 @@
namespace nix { namespace nix {
static auto runWorker(Worker & worker, auto mkGoals)
{
return worker.run(mkGoals);
}
void Store::buildPaths(const std::vector<DerivedPath> & reqs, BuildMode buildMode, std::shared_ptr<Store> evalStore) void Store::buildPaths(const std::vector<DerivedPath> & reqs, BuildMode buildMode, std::shared_ptr<Store> evalStore)
{ {
Worker worker(*this, evalStore ? *evalStore : *this); auto aio = kj::setupAsyncIo();
Worker worker(*this, evalStore ? *evalStore : *this, aio);
auto goals = worker.run([&](GoalFactory & gf) { auto goals = runWorker(worker, [&](GoalFactory & gf) {
Goals goals; Goals goals;
for (auto & br : reqs) for (auto & br : reqs)
goals.insert(gf.makeGoal(br, buildMode)); goals.insert(gf.makeGoal(br, buildMode));
@ -48,10 +54,12 @@ std::vector<KeyedBuildResult> Store::buildPathsWithResults(
BuildMode buildMode, BuildMode buildMode,
std::shared_ptr<Store> evalStore) std::shared_ptr<Store> evalStore)
{ {
Worker worker(*this, evalStore ? *evalStore : *this); auto aio = kj::setupAsyncIo();
Worker worker(*this, evalStore ? *evalStore : *this, aio);
std::vector<std::pair<const DerivedPath &, GoalPtr>> state; std::vector<std::pair<const DerivedPath &, GoalPtr>> state;
auto goals = worker.run([&](GoalFactory & gf) { auto goals = runWorker(worker, [&](GoalFactory & gf) {
Goals goals; Goals goals;
for (const auto & req : reqs) { for (const auto & req : reqs) {
auto goal = gf.makeGoal(req, buildMode); auto goal = gf.makeGoal(req, buildMode);
@ -72,10 +80,11 @@ std::vector<KeyedBuildResult> Store::buildPathsWithResults(
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); auto aio = kj::setupAsyncIo();
Worker worker(*this, *this, aio);
try { try {
auto goals = worker.run([&](GoalFactory & gf) -> Goals { auto goals = runWorker(worker, [&](GoalFactory & gf) -> Goals {
return Goals{gf.makeBasicDerivationGoal(drvPath, drv, OutputsSpec::All{}, buildMode)}; return Goals{gf.makeBasicDerivationGoal(drvPath, drv, OutputsSpec::All{}, buildMode)};
}); });
auto goal = *goals.begin(); auto goal = *goals.begin();
@ -97,10 +106,12 @@ void Store::ensurePath(const StorePath & path)
/* If the path is already valid, we're done. */ /* If the path is already valid, we're done. */
if (isValidPath(path)) return; if (isValidPath(path)) return;
Worker worker(*this, *this); auto aio = kj::setupAsyncIo();
Worker worker(*this, *this, aio);
auto goals = auto goals = runWorker(worker, [&](GoalFactory & gf) {
worker.run([&](GoalFactory & gf) { return Goals{gf.makePathSubstitutionGoal(path)}; }); return Goals{gf.makePathSubstitutionGoal(path)};
});
auto goal = *goals.begin(); auto goal = *goals.begin();
if (goal->exitCode != Goal::ecSuccess) { if (goal->exitCode != Goal::ecSuccess) {
@ -115,9 +126,10 @@ void Store::ensurePath(const StorePath & path)
void Store::repairPath(const StorePath & path) void Store::repairPath(const StorePath & path)
{ {
Worker worker(*this, *this); auto aio = kj::setupAsyncIo();
Worker worker(*this, *this, aio);
auto goals = worker.run([&](GoalFactory & gf) { auto goals = runWorker(worker, [&](GoalFactory & gf) {
return Goals{gf.makePathSubstitutionGoal(path, Repair)}; return Goals{gf.makePathSubstitutionGoal(path, Repair)};
}); });
auto goal = *goals.begin(); auto goal = *goals.begin();

View file

@ -1,9 +1,11 @@
#pragma once #pragma once
///@file ///@file
#include "result.hh"
#include "types.hh" #include "types.hh"
#include "store-api.hh" #include "store-api.hh"
#include "build-result.hh" #include "build-result.hh"
#include <kj/async.h>
namespace nix { namespace nix {
@ -161,7 +163,7 @@ public:
trace("goal destroyed"); trace("goal destroyed");
} }
virtual WorkResult work(bool inBuildSlot) = 0; virtual kj::Promise<Result<WorkResult>> work(bool inBuildSlot) noexcept = 0;
virtual void waiteeDone(GoalPtr waitee) { } virtual void waiteeDone(GoalPtr waitee) { }

View file

@ -149,8 +149,8 @@ void LocalDerivationGoal::killSandbox(bool getStats)
} }
Goal::WorkResult LocalDerivationGoal::tryLocalBuild(bool inBuildSlot) kj::Promise<Result<Goal::WorkResult>> LocalDerivationGoal::tryLocalBuild(bool inBuildSlot) noexcept
{ try {
#if __APPLE__ #if __APPLE__
additionalSandboxProfile = parsedDrv->getStringAttr("__sandboxProfile").value_or(""); additionalSandboxProfile = parsedDrv->getStringAttr("__sandboxProfile").value_or("");
#endif #endif
@ -159,7 +159,7 @@ Goal::WorkResult LocalDerivationGoal::tryLocalBuild(bool inBuildSlot)
state = &DerivationGoal::tryToBuild; state = &DerivationGoal::tryToBuild;
outputLocks.unlock(); outputLocks.unlock();
if (0U != settings.maxBuildJobs) { if (0U != settings.maxBuildJobs) {
return WaitForSlot{}; return {WaitForSlot{}};
} }
if (getMachines().empty()) { if (getMachines().empty()) {
throw Error( throw Error(
@ -214,7 +214,7 @@ Goal::WorkResult LocalDerivationGoal::tryLocalBuild(bool inBuildSlot)
if (!actLock) if (!actLock)
actLock = std::make_unique<Activity>(*logger, lvlWarn, actBuildWaiting, actLock = std::make_unique<Activity>(*logger, lvlWarn, actBuildWaiting,
fmt("waiting for a free build user ID for '%s'", Magenta(worker.store.printStorePath(drvPath)))); fmt("waiting for a free build user ID for '%s'", Magenta(worker.store.printStorePath(drvPath))));
return WaitForAWhile{}; return {WaitForAWhile{}};
} }
} }
@ -250,15 +250,17 @@ Goal::WorkResult LocalDerivationGoal::tryLocalBuild(bool inBuildSlot)
state = &DerivationGoal::buildDone; state = &DerivationGoal::buildDone;
started(); started();
return WaitForWorld{std::move(fds), true}; return {WaitForWorld{std::move(fds), true}};
} catch (BuildError & e) { } catch (BuildError & e) {
outputLocks.unlock(); outputLocks.unlock();
buildUser.reset(); buildUser.reset();
auto report = done(BuildResult::InputRejected, {}, std::move(e)); auto report = done(BuildResult::InputRejected, {}, std::move(e));
report.permanentFailure = true; report.permanentFailure = true;
return report; return {std::move(report)};
} }
} catch (...) {
return {std::current_exception()};
} }

View file

@ -213,7 +213,7 @@ struct LocalDerivationGoal : public DerivationGoal
/** /**
* The additional states. * The additional states.
*/ */
WorkResult tryLocalBuild(bool inBuildSlot) override; kj::Promise<Result<WorkResult>> tryLocalBuild(bool inBuildSlot) noexcept override;
/** /**
* Start building a derivation. * Start building a derivation.

View file

@ -45,21 +45,21 @@ Goal::Finished PathSubstitutionGoal::done(
} }
Goal::WorkResult PathSubstitutionGoal::work(bool inBuildSlot) kj::Promise<Result<Goal::WorkResult>> PathSubstitutionGoal::work(bool inBuildSlot) noexcept
{ {
return (this->*state)(inBuildSlot); return (this->*state)(inBuildSlot);
} }
Goal::WorkResult PathSubstitutionGoal::init(bool inBuildSlot) kj::Promise<Result<Goal::WorkResult>> PathSubstitutionGoal::init(bool inBuildSlot) noexcept
{ try {
trace("init"); trace("init");
worker.store.addTempRoot(storePath); worker.store.addTempRoot(storePath);
/* 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)) {
return done(ecSuccess, BuildResult::AlreadyValid); return {done(ecSuccess, BuildResult::AlreadyValid)};
} }
if (settings.readOnlyMode) if (settings.readOnlyMode)
@ -68,11 +68,13 @@ Goal::WorkResult PathSubstitutionGoal::init(bool inBuildSlot)
subs = settings.useSubstitutes ? getDefaultSubstituters() : std::list<ref<Store>>(); subs = settings.useSubstitutes ? getDefaultSubstituters() : std::list<ref<Store>>();
return tryNext(inBuildSlot); return tryNext(inBuildSlot);
} catch (...) {
return {std::current_exception()};
} }
Goal::WorkResult PathSubstitutionGoal::tryNext(bool inBuildSlot) kj::Promise<Result<Goal::WorkResult>> PathSubstitutionGoal::tryNext(bool inBuildSlot) noexcept
{ try {
trace("trying next substituter"); trace("trying next substituter");
cleanup(); cleanup();
@ -87,10 +89,10 @@ Goal::WorkResult PathSubstitutionGoal::tryNext(bool inBuildSlot)
/* 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. */
return done( return {done(
substituterFailed ? ecFailed : ecNoSubstituters, substituterFailed ? ecFailed : ecNoSubstituters,
BuildResult::NoSubstituters, BuildResult::NoSubstituters,
fmt("path '%s' is required, but there is no substituter that can build it", worker.store.printStorePath(storePath))); fmt("path '%s' is required, but there is no substituter that can build it", worker.store.printStorePath(storePath)))};
} }
sub = subs.front(); sub = subs.front();
@ -167,20 +169,22 @@ Goal::WorkResult PathSubstitutionGoal::tryNext(bool inBuildSlot)
return referencesValid(inBuildSlot); return referencesValid(inBuildSlot);
} else { } else {
state = &PathSubstitutionGoal::referencesValid; state = &PathSubstitutionGoal::referencesValid;
return result; return {std::move(result)};
} }
} catch (...) {
return {std::current_exception()};
} }
Goal::WorkResult PathSubstitutionGoal::referencesValid(bool inBuildSlot) kj::Promise<Result<Goal::WorkResult>> PathSubstitutionGoal::referencesValid(bool inBuildSlot) noexcept
{ try {
trace("all references realised"); trace("all references realised");
if (nrFailed > 0) { if (nrFailed > 0) {
return done( return {done(
nrNoSubstituters > 0 || nrIncompleteClosure > 0 ? ecIncompleteClosure : ecFailed, nrNoSubstituters > 0 || nrIncompleteClosure > 0 ? ecIncompleteClosure : ecFailed,
BuildResult::DependencyFailed, BuildResult::DependencyFailed,
fmt("some references of path '%s' could not be realised", worker.store.printStorePath(storePath))); fmt("some references of path '%s' could not be realised", worker.store.printStorePath(storePath)))};
} }
for (auto & i : info->references) for (auto & i : info->references)
@ -189,15 +193,17 @@ Goal::WorkResult PathSubstitutionGoal::referencesValid(bool inBuildSlot)
state = &PathSubstitutionGoal::tryToRun; state = &PathSubstitutionGoal::tryToRun;
return tryToRun(inBuildSlot); return tryToRun(inBuildSlot);
} catch (...) {
return {std::current_exception()};
} }
Goal::WorkResult PathSubstitutionGoal::tryToRun(bool inBuildSlot) kj::Promise<Result<Goal::WorkResult>> PathSubstitutionGoal::tryToRun(bool inBuildSlot) noexcept
{ try {
trace("trying to run"); trace("trying to run");
if (!inBuildSlot) { if (!inBuildSlot) {
return WaitForSlot{}; return {WaitForSlot{}};
} }
maintainRunningSubstitutions = worker.runningSubstitutions.addTemporarily(1); maintainRunningSubstitutions = worker.runningSubstitutions.addTemporarily(1);
@ -228,12 +234,14 @@ Goal::WorkResult PathSubstitutionGoal::tryToRun(bool inBuildSlot)
}); });
state = &PathSubstitutionGoal::finished; state = &PathSubstitutionGoal::finished;
return WaitForWorld{{outPipe.readSide.get()}, true}; return {WaitForWorld{{outPipe.readSide.get()}, true}};
} catch (...) {
return {std::current_exception()};
} }
Goal::WorkResult PathSubstitutionGoal::finished(bool inBuildSlot) kj::Promise<Result<Goal::WorkResult>> PathSubstitutionGoal::finished(bool inBuildSlot) noexcept
{ try {
trace("substitute finished"); trace("substitute finished");
worker.childTerminated(this); worker.childTerminated(this);
@ -274,7 +282,9 @@ Goal::WorkResult PathSubstitutionGoal::finished(bool inBuildSlot)
worker.doneNarSize += maintainExpectedNar.delta(); worker.doneNarSize += maintainExpectedNar.delta();
maintainExpectedNar.reset(); maintainExpectedNar.reset();
return done(ecSuccess, BuildResult::Substituted); return {done(ecSuccess, BuildResult::Substituted)};
} catch (...) {
return {std::current_exception()};
} }

View file

@ -67,7 +67,7 @@ struct PathSubstitutionGoal : public Goal
NotifyingCounter<uint64_t>::Bump maintainExpectedSubstitutions, NotifyingCounter<uint64_t>::Bump maintainExpectedSubstitutions,
maintainRunningSubstitutions, maintainExpectedNar, maintainExpectedDownload; maintainRunningSubstitutions, maintainExpectedNar, maintainExpectedDownload;
typedef WorkResult (PathSubstitutionGoal::*GoalState)(bool inBuildSlot); typedef kj::Promise<Result<WorkResult>> (PathSubstitutionGoal::*GoalState)(bool inBuildSlot) noexcept;
GoalState state; GoalState state;
/** /**
@ -101,16 +101,16 @@ public:
return "a$" + std::string(storePath.name()) + "$" + worker.store.printStorePath(storePath); return "a$" + std::string(storePath.name()) + "$" + worker.store.printStorePath(storePath);
} }
WorkResult work(bool inBuildSlot) override; kj::Promise<Result<WorkResult>> work(bool inBuildSlot) noexcept override;
/** /**
* The states. * The states.
*/ */
WorkResult init(bool inBuildSlot); kj::Promise<Result<WorkResult>> init(bool inBuildSlot) noexcept;
WorkResult tryNext(bool inBuildSlot); kj::Promise<Result<WorkResult>> tryNext(bool inBuildSlot) noexcept;
WorkResult referencesValid(bool inBuildSlot); kj::Promise<Result<WorkResult>> referencesValid(bool inBuildSlot) noexcept;
WorkResult tryToRun(bool inBuildSlot); kj::Promise<Result<WorkResult>> tryToRun(bool inBuildSlot) noexcept;
WorkResult finished(bool inBuildSlot); kj::Promise<Result<WorkResult>> finished(bool inBuildSlot) noexcept;
/** /**
* Callback used by the worker to write to the log. * Callback used by the worker to write to the log.

View file

@ -11,12 +11,13 @@
namespace nix { namespace nix {
Worker::Worker(Store & store, Store & evalStore) Worker::Worker(Store & store, Store & evalStore, kj::AsyncIoContext & aio)
: act(*logger, actRealise) : act(*logger, actRealise)
, actDerivations(*logger, actBuilds) , actDerivations(*logger, actBuilds)
, actSubstitutions(*logger, actCopyPaths) , actSubstitutions(*logger, actCopyPaths)
, store(store) , store(store)
, evalStore(evalStore) , evalStore(evalStore)
, aio(aio)
{ {
/* Debugging: prevent recursive workers. */ /* Debugging: prevent recursive workers. */
nrLocalBuilds = 0; nrLocalBuilds = 0;
@ -379,7 +380,7 @@ Goals Worker::run(std::function<Goals (GoalFactory &)> req)
const bool inSlot = goal->jobCategory() == JobCategory::Substitution const bool inSlot = goal->jobCategory() == JobCategory::Substitution
? nrSubstitutions < std::max(1U, (unsigned int) settings.maxSubstitutionJobs) ? nrSubstitutions < std::max(1U, (unsigned int) settings.maxSubstitutionJobs)
: nrLocalBuilds < settings.maxBuildJobs; : nrLocalBuilds < settings.maxBuildJobs;
handleWorkResult(goal, goal->work(inSlot)); handleWorkResult(goal, goal->work(inSlot).wait(aio.waitScope).value());
updateStatistics(); updateStatistics();
if (topGoals.empty()) break; // stuff may have been cancelled if (topGoals.empty()) break; // stuff may have been cancelled

View file

@ -9,6 +9,7 @@
#include "realisation.hh" #include "realisation.hh"
#include <future> #include <future>
#include <kj/async-io.h>
#include <thread> #include <thread>
namespace nix { namespace nix {
@ -237,6 +238,7 @@ public:
Store & store; Store & store;
Store & evalStore; Store & evalStore;
kj::AsyncIoContext & aio;
struct HookState { struct HookState {
std::unique_ptr<HookInstance> instance; std::unique_ptr<HookInstance> instance;
@ -264,7 +266,7 @@ public:
NotifyingCounter<uint64_t> expectedNarSize{[this] { updateStatisticsLater(); }}; NotifyingCounter<uint64_t> expectedNarSize{[this] { updateStatisticsLater(); }};
NotifyingCounter<uint64_t> doneNarSize{[this] { updateStatisticsLater(); }}; NotifyingCounter<uint64_t> doneNarSize{[this] { updateStatisticsLater(); }};
Worker(Store & store, Store & evalStore); Worker(Store & store, Store & evalStore, kj::AsyncIoContext & aio);
~Worker(); ~Worker();
/** /**

View file

@ -221,6 +221,7 @@ dependencies = [
aws_s3, aws_s3,
aws_sdk_transfer, aws_sdk_transfer,
nlohmann_json, nlohmann_json,
kj,
] ]
if host_machine.system() == 'freebsd' if host_machine.system() == 'freebsd'