1
0
Fork 0
mirror of https://github.com/NixOS/nix synced 2025-06-25 06:31:14 +02:00

Get rid of LocalDerivationGoal

I split it out before to try to separate the building logic, but now we
have the much better `DerivationBuilder` abstraction for that. With that
change, I think `LocalDerivationGoal` has outlived its usefulness.

We just inline it back into `DerivationGoal`, and do so with minimal
`#ifdef` for Windows.

Note that the order of statements in `~DerivationGoal` is different than
it was after the `~LocalDerivationGoal` split, but it is *restored* to
the way it original was before --- evidently I did the split slightly
wrong, but nobody noticed, probably because the order doesn't actually
matter.

Co-authored-by: Robert Hensing <roberth@users.noreply.github.com>
This commit is contained in:
John Ericson 2025-04-20 16:16:34 -04:00
parent bebef8f0c4
commit 4e586149df
11 changed files with 198 additions and 331 deletions

View file

@ -286,8 +286,6 @@
''^src/libstore/unix/build/hook-instance\.cc$'' ''^src/libstore/unix/build/hook-instance\.cc$''
''^src/libstore/unix/build/derivation-builder\.cc$'' ''^src/libstore/unix/build/derivation-builder\.cc$''
''^src/libstore/unix/include/nix/store/build/derivation-builder\.hh$'' ''^src/libstore/unix/include/nix/store/build/derivation-builder\.hh$''
''^src/libstore/unix/build/local-derivation-goal\.cc$''
''^src/libstore/unix/include/nix/store/build/local-derivation-goal\.hh$''
''^src/libstore/build/substitution-goal\.cc$'' ''^src/libstore/build/substitution-goal\.cc$''
''^src/libstore/include/nix/store/build/substitution-goal\.hh$'' ''^src/libstore/include/nix/store/build/substitution-goal\.hh$''
''^src/libstore/build/worker\.cc$'' ''^src/libstore/build/worker\.cc$''

View file

@ -1,6 +1,7 @@
#include "nix/store/build/derivation-goal.hh" #include "nix/store/build/derivation-goal.hh"
#ifndef _WIN32 // TODO enable build hook on Windows #ifndef _WIN32 // TODO enable build hook on Windows
# include "nix/store/build/hook-instance.hh" # include "nix/store/build/hook-instance.hh"
# include "nix/store/build/derivation-builder.hh"
#endif #endif
#include "nix/util/processes.hh" #include "nix/util/processes.hh"
#include "nix/util/config-global.hh" #include "nix/util/config-global.hh"
@ -68,6 +69,13 @@ DerivationGoal::~DerivationGoal()
{ {
/* Careful: we should never ever throw an exception from a /* Careful: we should never ever throw an exception from a
destructor. */ destructor. */
try { killChild(); } catch (...) { ignoreExceptionInDestructor(); }
#ifndef _WIN32 // TODO enable `DerivationBuilder` on Windows
if (builder) {
try { builder->stopDaemon(); } catch (...) { ignoreExceptionInDestructor(); }
try { builder->deleteTmpDir(false); } catch (...) { ignoreExceptionInDestructor(); }
}
#endif
try { closeLogFile(); } catch (...) { ignoreExceptionInDestructor(); } try { closeLogFile(); } catch (...) { ignoreExceptionInDestructor(); }
} }
@ -87,6 +95,22 @@ void DerivationGoal::killChild()
#ifndef _WIN32 // TODO enable build hook on Windows #ifndef _WIN32 // TODO enable build hook on Windows
hook.reset(); hook.reset();
#endif #endif
#ifndef _WIN32 // TODO enable `DerivationBuilder` on Windows
if (builder && builder->pid != -1) {
worker.childTerminated(this);
/* If we're using a build user, then there is a tricky race
condition: if we kill the build user before the child has
done its setuid() to the build user uid, then it won't be
killed, and we'll potentially lock up in pid.wait(). So
also send a conventional kill to the child. */
::kill(-builder->pid, SIGKILL); /* ignore the result */
builder->killSandbox(true);
builder->pid.wait();
}
#endif
} }
@ -666,18 +690,152 @@ Goal::Co DerivationGoal::tryToBuild()
actLock.reset(); actLock.reset();
co_await yield(); co_await yield();
co_return tryLocalBuild();
}
Goal::Co DerivationGoal::tryLocalBuild() { if (!dynamic_cast<LocalStore *>(&worker.store)) {
throw Error( throw Error(
R"( R"(
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.
For more information check 'man nix.conf' and search for '/machines'. For more information check 'man nix.conf' and search for '/machines'.
)" )"
); );
}
#ifdef _WIN32 // TODO enable `DerivationBuilder` on Windows
throw UnimplementedError("building derivations is not yet implemented on Windows");
#else
// Will continue here while waiting for a build user below
while (true) {
assert(!hook);
unsigned int curBuilds = worker.getNrLocalBuilds();
if (curBuilds >= settings.maxBuildJobs) {
outputLocks.unlock();
co_await waitForBuildSlot();
co_return tryToBuild();
}
if (!builder) {
/**
* Local implementation of these virtual methods, consider
* this just a record of lambdas.
*/
struct DerivationGoalCallbacks : DerivationBuilderCallbacks
{
DerivationGoal & goal;
DerivationGoalCallbacks(DerivationGoal & goal, std::unique_ptr<DerivationBuilder> & builder)
: goal{goal}
{}
~DerivationGoalCallbacks() override = default;
void childStarted(Descriptor builderOut) override
{
goal.worker.childStarted(goal.shared_from_this(), {builderOut}, true, true);
}
void childTerminated() override
{
goal.worker.childTerminated(&goal);
}
void noteHashMismatch() override
{
goal.worker.hashMismatch = true;
}
void noteCheckMismatch() override
{
goal.worker.checkMismatch = true;
}
void markContentsGood(const StorePath & path) override
{
goal.worker.markContentsGood(path);
}
Path openLogFile() override {
return goal.openLogFile();
}
void closeLogFile() override {
goal.closeLogFile();
}
SingleDrvOutputs assertPathValidity() override {
return goal.assertPathValidity();
}
void appendLogTailErrorMsg(std::string & msg) override {
goal.appendLogTailErrorMsg(msg);
}
};
/* If we have to wait and retry (see below), then `builder` will
already be created, so we don't need to create it again. */
builder = makeDerivationBuilder(
worker.store,
std::make_unique<DerivationGoalCallbacks>(*this, builder),
DerivationBuilderParams {
drvPath,
buildMode,
buildResult,
*drv,
parsedDrv.get(),
*drvOptions,
inputPaths,
initialOutputs,
});
}
if (!builder->prepareBuild()) {
if (!actLock)
actLock = std::make_unique<Activity>(*logger, lvlWarn, actBuildWaiting,
fmt("waiting for a free build user ID for '%s'", Magenta(worker.store.printStorePath(drvPath))));
co_await waitForAWhile();
continue;
}
break;
}
actLock.reset();
try {
/* Okay, we have to build. */
builder->startBuilder();
} catch (BuildError & e) {
outputLocks.unlock();
builder->buildUser.reset();
worker.permanentFailure = true;
co_return done(BuildResult::InputRejected, {}, std::move(e));
}
started();
co_await Suspend{};
trace("build done");
auto res = builder->unprepareBuild();
// N.B. cannot use `std::visit` with co-routine return
if (auto * ste = std::get_if<0>(&res)) {
outputLocks.unlock();
co_return done(std::move(ste->first), {}, std::move(ste->second));
} else if (auto * builtOutputs = std::get_if<1>(&res)) {
/* It is now safe to delete the lock files, since all future
lockers will see that the output paths are valid; they will
not create new lock files with the same names as the old
(unlinked) lock files. */
outputLocks.setDeletion(true);
outputLocks.unlock();
co_return done(BuildResult::Built, std::move(*builtOutputs));
} else {
unreachable();
}
#endif
} }
@ -1207,7 +1365,10 @@ bool DerivationGoal::isReadDesc(Descriptor fd)
#ifdef _WIN32 // TODO enable build hook on Windows #ifdef _WIN32 // TODO enable build hook on Windows
return false; return false;
#else #else
return fd == hook->builderOut.readSide.get(); return
(hook && fd == hook->builderOut.readSide.get())
||
(builder && fd == builder->builderOut.get());
#endif #endif
} }

View file

@ -5,7 +5,6 @@
#include "nix/store/build/drv-output-substitution-goal.hh" #include "nix/store/build/drv-output-substitution-goal.hh"
#include "nix/store/build/derivation-goal.hh" #include "nix/store/build/derivation-goal.hh"
#ifndef _WIN32 // TODO Enable building on Windows #ifndef _WIN32 // TODO Enable building on Windows
# include "nix/store/build/local-derivation-goal.hh"
# include "nix/store/build/hook-instance.hh" # include "nix/store/build/hook-instance.hh"
#endif #endif
#include "nix/util/signals.hh" #include "nix/util/signals.hh"
@ -65,13 +64,7 @@ std::shared_ptr<DerivationGoal> Worker::makeDerivationGoal(const StorePath & drv
const OutputsSpec & wantedOutputs, BuildMode buildMode) const OutputsSpec & wantedOutputs, BuildMode buildMode)
{ {
return makeDerivationGoalCommon(drvPath, wantedOutputs, [&]() -> std::shared_ptr<DerivationGoal> { return makeDerivationGoalCommon(drvPath, wantedOutputs, [&]() -> std::shared_ptr<DerivationGoal> {
return return std::make_shared<DerivationGoal>(drvPath, wantedOutputs, *this, buildMode);
#ifndef _WIN32 // TODO Enable building on Windows
dynamic_cast<LocalStore *>(&store)
? makeLocalDerivationGoal(drvPath, wantedOutputs, *this, buildMode)
:
#endif
std::make_shared</* */DerivationGoal>(drvPath, wantedOutputs, *this, buildMode);
}); });
} }
@ -79,13 +72,7 @@ std::shared_ptr<DerivationGoal> Worker::makeBasicDerivationGoal(const StorePath
const BasicDerivation & drv, const OutputsSpec & wantedOutputs, BuildMode buildMode) const BasicDerivation & drv, const OutputsSpec & wantedOutputs, BuildMode buildMode)
{ {
return makeDerivationGoalCommon(drvPath, wantedOutputs, [&]() -> std::shared_ptr<DerivationGoal> { return makeDerivationGoalCommon(drvPath, wantedOutputs, [&]() -> std::shared_ptr<DerivationGoal> {
return return std::make_shared<DerivationGoal>(drvPath, drv, wantedOutputs, *this, buildMode);
#ifndef _WIN32 // TODO Enable building on Windows
dynamic_cast<LocalStore *>(&store)
? makeLocalDerivationGoal(drvPath, drv, wantedOutputs, *this, buildMode)
:
#endif
std::make_shared</* */DerivationGoal>(drvPath, drv, wantedOutputs, *this, buildMode);
}); });
} }

View file

@ -16,6 +16,7 @@ using std::map;
#ifndef _WIN32 // TODO enable build hook on Windows #ifndef _WIN32 // TODO enable build hook on Windows
struct HookInstance; struct HookInstance;
struct DerivationBuilder;
#endif #endif
typedef enum {rpAccept, rpDecline, rpPostpone} HookReply; typedef enum {rpAccept, rpDecline, rpPostpone} HookReply;
@ -154,6 +155,8 @@ struct DerivationGoal : public Goal
* The build hook. * The build hook.
*/ */
std::unique_ptr<HookInstance> hook; std::unique_ptr<HookInstance> hook;
std::unique_ptr<DerivationBuilder> builder;
#endif #endif
BuildMode buildMode; BuildMode buildMode;
@ -180,7 +183,7 @@ struct DerivationGoal : public Goal
DerivationGoal(const StorePath & drvPath, const BasicDerivation & drv, DerivationGoal(const StorePath & drvPath, const BasicDerivation & drv,
const OutputsSpec & wantedOutputs, Worker & worker, const OutputsSpec & wantedOutputs, Worker & worker,
BuildMode buildMode = bmNormal); BuildMode buildMode = bmNormal);
virtual ~DerivationGoal(); ~DerivationGoal();
void timedOut(Error && ex) override; void timedOut(Error && ex) override;
@ -198,7 +201,6 @@ struct DerivationGoal : public Goal
Co haveDerivation(); Co haveDerivation();
Co gaveUpOnSubstitution(); Co gaveUpOnSubstitution();
Co tryToBuild(); Co tryToBuild();
virtual Co tryLocalBuild();
Co hookDone(); Co hookDone();
Co resolvedFinished(); Co resolvedFinished();
@ -218,7 +220,7 @@ struct DerivationGoal : public Goal
*/ */
void closeLogFile(); void closeLogFile();
virtual bool isReadDesc(Descriptor fd); bool isReadDesc(Descriptor fd);
/** /**
* Callback used by the worker to write to the log. * Callback used by the worker to write to the log.
@ -252,7 +254,7 @@ struct DerivationGoal : public Goal
/** /**
* Forcibly kill the child process, if any. * Forcibly kill the child process, if any.
*/ */
virtual void killChild(); void killChild();
Co repairClosure(); Co repairClosure();

View file

@ -398,7 +398,6 @@ private:
void addBuildLog(const StorePath & drvPath, std::string_view log) override; void addBuildLog(const StorePath & drvPath, std::string_view log) override;
friend struct LocalDerivationGoal;
friend struct PathSubstitutionGoal; friend struct PathSubstitutionGoal;
friend struct SubstitutionGoal; friend struct SubstitutionGoal;
friend struct DerivationGoal; friend struct DerivationGoal;

View file

@ -96,17 +96,17 @@ class DerivationBuilderImpl : public DerivationBuilder, DerivationBuilderParams
{ {
Store & store; Store & store;
DerivationBuilderCallbacks & miscMethods; std::unique_ptr<DerivationBuilderCallbacks> miscMethods;
public: public:
DerivationBuilderImpl( DerivationBuilderImpl(
Store & store, Store & store,
DerivationBuilderCallbacks & miscMethods, std::unique_ptr<DerivationBuilderCallbacks> miscMethods,
DerivationBuilderParams params) DerivationBuilderParams params)
: DerivationBuilderParams{std::move(params)} : DerivationBuilderParams{std::move(params)}
, store{store} , store{store}
, miscMethods{miscMethods} , miscMethods{std::move(miscMethods)}
{ } { }
LocalStore & getLocalStore(); LocalStore & getLocalStore();
@ -382,12 +382,12 @@ private:
std::unique_ptr<DerivationBuilder> makeDerivationBuilder( std::unique_ptr<DerivationBuilder> makeDerivationBuilder(
Store & store, Store & store,
DerivationBuilderCallbacks & miscMethods, std::unique_ptr<DerivationBuilderCallbacks> miscMethods,
DerivationBuilderParams params) DerivationBuilderParams params)
{ {
return std::make_unique<DerivationBuilderImpl>( return std::make_unique<DerivationBuilderImpl>(
store, store,
miscMethods, std::move(miscMethods),
std::move(params)); std::move(params));
} }
@ -550,13 +550,13 @@ std::variant<std::pair<BuildResult::Status, Error>, SingleDrvOutputs> Derivation
buildResult.stopTime = time(0); buildResult.stopTime = time(0);
/* So the child is gone now. */ /* So the child is gone now. */
miscMethods.childTerminated(); miscMethods->childTerminated();
/* Close the read side of the logger pipe. */ /* Close the read side of the logger pipe. */
builderOut.close(); builderOut.close();
/* Close the log file. */ /* Close the log file. */
miscMethods.closeLogFile(); miscMethods->closeLogFile();
/* When running under a build user, make sure that all processes /* When running under a build user, make sure that all processes
running under that uid are gone. This is to prevent a running under that uid are gone. This is to prevent a
@ -589,7 +589,7 @@ std::variant<std::pair<BuildResult::Status, Error>, SingleDrvOutputs> Derivation
Magenta(store.printStorePath(drvPath)), Magenta(store.printStorePath(drvPath)),
statusToString(status)); statusToString(status));
miscMethods.appendLogTailErrorMsg(msg); miscMethods->appendLogTailErrorMsg(msg);
if (diskFull) if (diskFull)
msg += "\nnote: build failure may have been caused by lack of free disk space"; msg += "\nnote: build failure may have been caused by lack of free disk space";
@ -1175,7 +1175,7 @@ void DerivationBuilderImpl::startBuilder()
printMsg(lvlVomit, "setting builder env variable '%1%'='%2%'", i.first, i.second); printMsg(lvlVomit, "setting builder env variable '%1%'='%2%'", i.first, i.second);
/* Create the log file. */ /* Create the log file. */
[[maybe_unused]] Path logFile = miscMethods.openLogFile(); [[maybe_unused]] Path logFile = miscMethods->openLogFile();
/* Create a pseudoterminal to get the output of the builder. */ /* Create a pseudoterminal to get the output of the builder. */
builderOut = posix_openpt(O_RDWR | O_NOCTTY); builderOut = posix_openpt(O_RDWR | O_NOCTTY);
@ -1385,7 +1385,7 @@ void DerivationBuilderImpl::startBuilder()
/* parent */ /* parent */
pid.setSeparatePG(true); pid.setSeparatePG(true);
miscMethods.childStarted(); miscMethods->childStarted(builderOut.get());
processSandboxSetupMessages(); processSandboxSetupMessages();
} }
@ -2673,7 +2673,7 @@ SingleDrvOutputs DerivationBuilderImpl::registerOutputs()
if (wanted != got) { if (wanted != got) {
/* Throw an error after registering the path as /* Throw an error after registering the path as
valid. */ valid. */
miscMethods.noteHashMismatch(); miscMethods->noteHashMismatch();
delayedException = std::make_exception_ptr( delayedException = std::make_exception_ptr(
BuildError("hash mismatch in fixed-output derivation '%s':\n specified: %s\n got: %s", BuildError("hash mismatch in fixed-output derivation '%s':\n specified: %s\n got: %s",
store.printStorePath(drvPath), store.printStorePath(drvPath),
@ -2761,7 +2761,7 @@ SingleDrvOutputs DerivationBuilderImpl::registerOutputs()
if (!store.isValidPath(newInfo.path)) continue; if (!store.isValidPath(newInfo.path)) continue;
ValidPathInfo oldInfo(*store.queryPathInfo(newInfo.path)); ValidPathInfo oldInfo(*store.queryPathInfo(newInfo.path));
if (newInfo.narHash != oldInfo.narHash) { if (newInfo.narHash != oldInfo.narHash) {
miscMethods.noteCheckMismatch(); miscMethods->noteCheckMismatch();
if (settings.runDiffHook || settings.keepFailed) { if (settings.runDiffHook || settings.keepFailed) {
auto dst = store.toRealPath(finalDestPath + checkSuffix); auto dst = store.toRealPath(finalDestPath + checkSuffix);
deletePath(dst); deletePath(dst);
@ -2798,7 +2798,7 @@ SingleDrvOutputs DerivationBuilderImpl::registerOutputs()
} }
localStore.optimisePath(actualPath, NoRepair); // FIXME: combine with scanForReferences() localStore.optimisePath(actualPath, NoRepair); // FIXME: combine with scanForReferences()
miscMethods.markContentsGood(newInfo.path); miscMethods->markContentsGood(newInfo.path);
newInfo.deriver = drvPath; newInfo.deriver = drvPath;
newInfo.ultimate = true; newInfo.ultimate = true;
@ -2821,7 +2821,7 @@ SingleDrvOutputs DerivationBuilderImpl::registerOutputs()
also a source for non-determinism. */ also a source for non-determinism. */
if (delayedException) if (delayedException)
std::rethrow_exception(delayedException); std::rethrow_exception(delayedException);
return miscMethods.assertPathValidity(); return miscMethods->assertPathValidity();
} }
/* Apply output checks. */ /* Apply output checks. */

View file

@ -1,254 +0,0 @@
#include "nix/store/build/local-derivation-goal.hh"
#include "nix/store/local-store.hh"
#include "nix/util/processes.hh"
#include "nix/store/build/worker.hh"
#include "nix/util/util.hh"
#include "nix/store/restricted-store.hh"
#include "nix/store/build/derivation-builder.hh"
#include <sys/un.h>
#include <fcntl.h>
#include <termios.h>
#include <unistd.h>
#include <sys/mman.h>
#include <sys/resource.h>
#include <sys/socket.h>
#include "store-config-private.hh"
#if HAVE_STATVFS
#include <sys/statvfs.h>
#endif
#include <pwd.h>
#include <grp.h>
namespace nix {
/**
* This hooks up `DerivationBuilder` to the scheduler / goal machinary.
*
* @todo Eventually, this shouldn't exist, because `DerivationGoal` can
* just choose to use `DerivationBuilder` or its remote-building
* equalivalent directly, at the "value level" rather than "class
* inheritance hierarchy" level.
*/
struct LocalDerivationGoal : DerivationGoal, DerivationBuilderCallbacks
{
std::unique_ptr<DerivationBuilder> builder;
LocalDerivationGoal(const StorePath & drvPath,
const OutputsSpec & wantedOutputs, Worker & worker,
BuildMode buildMode)
: DerivationGoal{drvPath, wantedOutputs, worker, buildMode}
{}
LocalDerivationGoal(const StorePath & drvPath, const BasicDerivation & drv,
const OutputsSpec & wantedOutputs, Worker & worker,
BuildMode buildMode = bmNormal)
: DerivationGoal{drvPath, drv, wantedOutputs, worker, buildMode}
{}
virtual ~LocalDerivationGoal() override;
/**
* The additional states.
*/
Goal::Co tryLocalBuild() override;
bool isReadDesc(int fd) override;
/**
* Forcibly kill the child process, if any.
*
* Called by destructor, can't be overridden
*/
void killChild() override final;
void childStarted() override;
void childTerminated() override;
void noteHashMismatch(void) override;
void noteCheckMismatch(void) override;
void markContentsGood(const StorePath &) override;
// Fake overrides to instantiate identically-named virtual methods
Path openLogFile() override {
return DerivationGoal::openLogFile();
}
void closeLogFile() override {
DerivationGoal::closeLogFile();
}
SingleDrvOutputs assertPathValidity() override {
return DerivationGoal::assertPathValidity();
}
void appendLogTailErrorMsg(std::string & msg) override {
DerivationGoal::appendLogTailErrorMsg(msg);
}
};
std::shared_ptr<DerivationGoal> makeLocalDerivationGoal(
const StorePath & drvPath,
const OutputsSpec & wantedOutputs, Worker & worker,
BuildMode buildMode)
{
return std::make_shared<LocalDerivationGoal>(drvPath, wantedOutputs, worker, buildMode);
}
std::shared_ptr<DerivationGoal> makeLocalDerivationGoal(
const StorePath & drvPath, const BasicDerivation & drv,
const OutputsSpec & wantedOutputs, Worker & worker,
BuildMode buildMode)
{
return std::make_shared<LocalDerivationGoal>(drvPath, drv, wantedOutputs, worker, buildMode);
}
LocalDerivationGoal::~LocalDerivationGoal()
{
/* Careful: we should never ever throw an exception from a
destructor. */
if (builder) {
try { builder->deleteTmpDir(false); } catch (...) { ignoreExceptionInDestructor(); }
}
try { killChild(); } catch (...) { ignoreExceptionInDestructor(); }
if (builder) {
try { builder->stopDaemon(); } catch (...) { ignoreExceptionInDestructor(); }
}
}
void LocalDerivationGoal::killChild()
{
if (builder && builder->pid != -1) {
worker.childTerminated(this);
/* If we're using a build user, then there is a tricky race
condition: if we kill the build user before the child has
done its setuid() to the build user uid, then it won't be
killed, and we'll potentially lock up in pid.wait(). So
also send a conventional kill to the child. */
::kill(-builder->pid, SIGKILL); /* ignore the result */
builder->killSandbox(true);
builder->pid.wait();
}
DerivationGoal::killChild();
}
void LocalDerivationGoal::childStarted()
{
assert(builder);
worker.childStarted(shared_from_this(), {builder->builderOut.get()}, true, true);
}
void LocalDerivationGoal::childTerminated()
{
worker.childTerminated(this);
}
void LocalDerivationGoal::noteHashMismatch()
{
worker.hashMismatch = true;
}
void LocalDerivationGoal::noteCheckMismatch()
{
worker.checkMismatch = true;
}
void LocalDerivationGoal::markContentsGood(const StorePath & path)
{
worker.markContentsGood(path);
}
Goal::Co LocalDerivationGoal::tryLocalBuild()
{
assert(!hook);
unsigned int curBuilds = worker.getNrLocalBuilds();
if (curBuilds >= settings.maxBuildJobs) {
outputLocks.unlock();
co_await waitForBuildSlot();
co_return tryToBuild();
}
if (!builder) {
/* If we have to wait and retry (see below), then `builder` will
already be created, so we don't need to create it again. */
builder = makeDerivationBuilder(
worker.store,
static_cast<DerivationBuilderCallbacks &>(*this),
DerivationBuilderParams {
DerivationGoal::drvPath,
DerivationGoal::buildMode,
DerivationGoal::buildResult,
*DerivationGoal::drv,
DerivationGoal::parsedDrv.get(),
*DerivationGoal::drvOptions,
DerivationGoal::inputPaths,
DerivationGoal::initialOutputs,
});
}
if (!builder->prepareBuild()) {
if (!actLock)
actLock = std::make_unique<Activity>(*logger, lvlWarn, actBuildWaiting,
fmt("waiting for a free build user ID for '%s'", Magenta(worker.store.printStorePath(drvPath))));
co_await waitForAWhile();
co_return tryLocalBuild();
}
actLock.reset();
try {
/* Okay, we have to build. */
builder->startBuilder();
} catch (BuildError & e) {
outputLocks.unlock();
builder->buildUser.reset();
worker.permanentFailure = true;
co_return done(BuildResult::InputRejected, {}, std::move(e));
}
started();
co_await Suspend{};
trace("build done");
auto res = builder->unprepareBuild();
// N.B. cannot use `std::visit` with co-routine return
if (auto * ste = std::get_if<0>(&res)) {
outputLocks.unlock();
co_return done(std::move(ste->first), {}, std::move(ste->second));
} else if (auto * builtOutputs = std::get_if<1>(&res)) {
/* It is now safe to delete the lock files, since all future
lockers will see that the output paths are valid; they will
not create new lock files with the same names as the old
(unlinked) lock files. */
outputLocks.setDeletion(true);
outputLocks.unlock();
co_return done(BuildResult::Built, std::move(*builtOutputs));
} else {
unreachable();
}
}
bool LocalDerivationGoal::isReadDesc(int fd)
{
return (hook && DerivationGoal::isReadDesc(fd)) ||
(!hook && builder && fd == builder->builderOut.get());
}
}

View file

@ -85,6 +85,8 @@ struct DerivationBuilderParams
*/ */
struct DerivationBuilderCallbacks struct DerivationBuilderCallbacks
{ {
virtual ~DerivationBuilderCallbacks() = default;
/** /**
* Open a log file and a pipe to it. * Open a log file and a pipe to it.
*/ */
@ -110,7 +112,7 @@ struct DerivationBuilderCallbacks
* *
* @todo this should be reworked * @todo this should be reworked
*/ */
virtual void childStarted() = 0; virtual void childStarted(Descriptor builderOut) = 0;
/** /**
* @todo this should be reworked * @todo this should be reworked
@ -201,7 +203,7 @@ struct DerivationBuilder : RestrictionContext
std::unique_ptr<DerivationBuilder> makeDerivationBuilder( std::unique_ptr<DerivationBuilder> makeDerivationBuilder(
Store & store, Store & store,
DerivationBuilderCallbacks & miscMethods, std::unique_ptr<DerivationBuilderCallbacks> miscMethods,
DerivationBuilderParams params); DerivationBuilderParams params);
} }

View file

@ -1,26 +0,0 @@
#pragma once
///@file
#include "nix/store/build/derivation-goal.hh"
namespace nix {
/**
* Create a local derivation goal, see `DerivationGoal` for info on each
* constructor variant.
*/
std::shared_ptr<DerivationGoal> makeLocalDerivationGoal(
const StorePath & drvPath,
const OutputsSpec & wantedOutputs, Worker & worker,
BuildMode buildMode = bmNormal);
/**
* Create a local derivation goal, see `DerivationGoal` for info on each
* constructor variant.
*/
std::shared_ptr<DerivationGoal> makeLocalDerivationGoal(
const StorePath & drvPath, const BasicDerivation & drv,
const OutputsSpec & wantedOutputs, Worker & worker,
BuildMode buildMode = bmNormal);
}

View file

@ -4,6 +4,5 @@ headers += files(
'build/child.hh', 'build/child.hh',
'build/derivation-builder.hh', 'build/derivation-builder.hh',
'build/hook-instance.hh', 'build/hook-instance.hh',
'build/local-derivation-goal.hh',
'user-lock.hh', 'user-lock.hh',
) )

View file

@ -2,7 +2,6 @@ sources += files(
'build/child.cc', 'build/child.cc',
'build/derivation-builder.cc', 'build/derivation-builder.cc',
'build/hook-instance.cc', 'build/hook-instance.cc',
'build/local-derivation-goal.cc',
'pathlocks.cc', 'pathlocks.cc',
'user-lock.cc', 'user-lock.cc',
) )