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:
parent
bebef8f0c4
commit
4e586149df
11 changed files with 198 additions and 331 deletions
|
@ -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$''
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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);
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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();
|
||||||
|
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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. */
|
||||||
|
|
|
@ -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());
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -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);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -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);
|
|
||||||
|
|
||||||
}
|
|
|
@ -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',
|
||||||
)
|
)
|
||||||
|
|
|
@ -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',
|
||||||
)
|
)
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue